>

Forward Semantic CSS Refactoring & Modernization

CSSForge is a blazing-fast, 100% cascade-safe, multi-platform CSS transformation engine and interactive terminal workbench written in Rust. Refactor flat legacy CSS into modern native nesting, :is() clusters, and Range media queries with zero declaration loss and mathematical specificity proof guarantees.

📦 Installation

Install CSSForge globally across all major platforms using Cargo or download pre-compiled standalone binary releases:

If you have Rust installed, install CSSForge directly from crates.io:

cargo install cssforge

This places cssforge directly into your ~/.cargo/bin directory.

🚀 Quick Start & Usage

CSSForge automatically discovers CSS files in the current working directory where you execute it:

1. Run in Any Project Directory (Automatic Discovery)

# 1. Navigate to your project folder:
cd /path/to/my-web-app

# 2. Launch interactive modernization:
cssforge

2. Target a Specific Folder or File

# Target specific styles folder:
cssforge interactive ./src/css

# Or target a single file:
cssforge interactive ./src/styles.css

3. Fast Headless CLI for CI/CD & Automation

# Analyze CSS files and report modernization opportunities:
cssforge analyze ./src

# Analyze and emit structured JSON for pipelines:
cssforge analyze ./src --json

# Apply modern preset to new (*.modern.css) files:
cssforge apply ./src/app.css --preset modern --output new-file

# In-place overwrite with automatic safety backup (.bak):
cssforge apply ./src/app.css --output overwrite-with-backup --yes

📁 Smart File Discovery & Default Ignored Conventions

When scanning directories recursively, CSSForge applies zero-config safety ignore filters to prevent accidental corruption of generated files or vendor libraries:

  • Build & Dependency Folders (node_modules/, vendor/, target/, dist/, build/, out/, .next/, .nuxt/, .turbo/, .svelte-kit/): Ignored to protect compiled bundles and 3rd-party dependencies.
  • Version Control Internals (.git/, .hg/, .svn/, .cache/): Ignored to keep repo metadata safe.
  • Generated Modern Files (*.modern.css): Ignored to prevent recursive re-modernization loops on newly generated output.
  • Minified & Chunks (*.min.css, *.bundle.css, *.chunk.css, *.map.css): Ignored because minified code is not meant for source AST refactoring.
  • Safety Backups (*.bak.css, *.backup.css): Preserved as untouched recovery snapshots.

Note: If you explicitly pass a file path (e.g. cssforge interactive ./dist/custom.min.css), CSSForge honors your explicit request. Standard .gitignore and .ignore files are also respected automatically.

🖥️ Interactive TUI Visual Workbench

CSSForge features a full-screen, responsive terminal interface powered by Ratatui and Crossterm:

cssforge — Step 1 of 4: Select Files
  ____ ____ ____  _____ ___  ____   ____ _____ 
 / ___/ ___/ ___||  ___/ _ \|  _ \ / ___| ____|
| |   \___ \___ \| |_ | | | | |_) | |  _|  _|  
| |___ ___) |__) |  _|| |_| |  _ <| |_| | |___ 
 \____|____/____/|_|   \___/|_| \_\\____|_____|
Forward semantic CSS modernization • nest • refactor • review

Choose the CSS files you want to modernize. Press [Space] to toggle, [a] for all:

[x] src/components/card.css
[x] src/components/button.css
[x] src/styles/theme.css

[Enter] Next: Rules → [Space] Toggle [p] Preset: Modern [d] Live Diff [q] Quit
CSSForge TUI keyboard shortcuts
Keybinding Action
[Enter] Advance to next step / Apply & Finish
[Space] Toggle highlighted file, rule, or setting
[a] Select / Deselect All
[p] Cycle presets (Conservative ➔ Modern ➔ Refactor ➔ Aggressive)
[d] / [v] Open live full-screen split/unified code diff viewer
[Esc] / [b] Go back to previous step
[1]…[4] Jump directly to Step 1 (Files), 2 (Rules), 3 (Output), 4 (Done)
[q] Quit workbench

⚙️ 28 Transformation Rules Catalog

Explore the complete inventory of 28 automated refactoring and modernization rules, including dedupe-identical-declarations and nest-layer-by-selector. Version 0.6.1 keeps multiline values intact, preserves parent declarations in mixed @media gather, and does not concatenate identical selectors that conflict. Version 0.7.0 factors shared-state :is() lists such as a:hover, button:hover; review diffs because :is() can raise lower-specificity branches.

Showing all 28 transformation rules
nest-pseudo-class Safe
Nests adjacent pseudo-states (:hover, :focus, :active, :focus-visible) into the parent rule using &.
- .card:hover { border-color: blue; }
+ .card { &:hover { border-color: blue; } }
nest-pseudo-element Safe
Nests pseudo-elements (::before, ::after, ::placeholder) into the parent rule using &.
- .btn::before { content: ""; }
+ .btn { &::before { content: ""; } }
nest-attribute Safe
Nests attribute selectors ([disabled], [aria-*]) into the parent rule cleanly.
- input[disabled] { opacity: 0.5; }
+ input { &[disabled] { opacity: 0.5; } }
nest-compound Safe
Nests compound class variants (.item.active, .btn.primary) into the parent rule.
- .item.active { font-weight: bold; }
+ .item { &.active { font-weight: bold; } }
nest-descendant Safe
Nests descendant selectors into the parent rule without redundant string prefixes.
- .card .title { font-size: 1.5rem; }
+ .card { .title { font-size: 1.5rem; } }
nest-combinator Safe
Nests child (>), adjacent sibling (+), and general sibling (~) selectors.
- .nav > .item { display: inline-block; }
+ .nav { > .item { display: inline-block; } }
factor-selector-list Safe
Factors comma-separated selector lists sharing a base selector into a unified nested block.
- .dot, .dot::before { color: red; }
+ .dot { &, &::before { color: red; } }
nest-media Safe
Inlines matching @media queries directly into the parent selector block without breaking cascade.
- @media (min-width: 768px) { .box { padding: 2rem; } }
+ .box { @media (min-width: 768px) { padding: 2rem; } }
nest-supports Safe
Inlines @supports feature queries directly into the parent selector block.
- @supports (display: grid) { .box { display: grid; } }
+ .box { @supports (display: grid) { display: grid; } }
nest-container Safe
Inlines container queries (@container) directly into the target component block.
- @container (min-width: 400px) { .card { flex-direction: row; } }
+ .card { @container (min-width: 400px) { flex-direction: row; } }
nest-starting-style Safe
Inlines @starting-style transition entry rules directly into the target selector.
- @starting-style { dialog { opacity: 0; } }
+ dialog { @starting-style { opacity: 0; } }
consolidate-not Modernize
Consolidates chained legacy :not() selectors into a single comma-separated :not(a, b).
- .btn:not(.primary):not(.secondary)
+ .btn:not(.primary, .secondary)
modernize-is Modernize
Factors multi-selector clusters sharing identical bases into unified :is(...) blocks with nested rules.
- .a .title, #hero .title { font-size: 1.2rem; }
+ :is(.a, #hero) { .title { font-size: 1.2rem; } }
modernize-where Modernize
Factors default rules into :where(...) blocks for zero-specificity design system defaults.
- .card, .panel, .dialog { border: 1px solid #ccc; }
+ :where(.card, .panel, .dialog) { border: 1px solid #ccc; }
modernize-media-range-syntax Modernize
Converts legacy min/max-width queries to modern CSS Range syntax.
- @media (min-width: 768px) and (max-width: 1024px)
+ @media (768px <= width <=1024px)
merge-same-named-layer Refactor
Consolidates separated blocks of the same named @layer into their canonical first block.
- @layer comps { ... } /* later */ @layer comps { ... }
+ @layer comps { /* unified block */ }
merge-adjacent-media Refactor
Combines consecutive @media blocks sharing identical query conditions into a single block.
- @media (min-width: 600px) { .a {} } @media (min-width: 600px) { .b {} }
+ @media (min-width: 600px) { .a {} .b {} }
merge-adjacent-supports Refactor
Combines consecutive @supports blocks sharing identical feature conditions into a single block.
- @supports (display: grid) { ... } @supports (display: grid) { ... }
+ @supports (display: grid) { /* combined */ }
merge-adjacent-container Refactor
Combines consecutive @container blocks sharing identical query conditions.
- @container (min-width: 400px) { .x {} } @container (min-width: 400px) { .y {} }
+ @container (min-width: 400px) { .x {} .y {} }
merge-identical-scope Refactor
Combines consecutive @scope blocks sharing identical root and limit parameters.
- @scope (.card) { ... } @scope (.card) { ... }
+ @scope (.card) { /* combined */ }
merge-identical-starting-style Refactor
Combines consecutive top-level @starting-style blocks into a single block.
- @starting-style { ... } @starting-style { ... }
+ @starting-style { /* combined */ }
merge-adjacent-identical-selector Refactor
Combines consecutive style rules sharing the exact same selector into a single declaration block.
- .btn { color: red; } .btn { padding: 1rem; }
+ .btn { color: red; padding: 1rem; }
merge-identical-rule-bodies Refactor
Combines selectors sharing identical declaration bodies into a unified comma-separated rule.
- .a { color: red; } .b { color: red; }
+ .a, .b { color: red; }
factor-identical-states-with-is Refactor
Combines multiple states of the same element sharing identical declarations into &:is(:hover, :focus).
- .btn:hover, .btn:focus { background: blue; }
+ .btn { &:is(:hover, :focus) { background: blue; } }
gather-related-selector-rules Refactor
Gathers scattered related rules into the strongest existing parent within the same cascade layer. Does not move declarations between named @layer blocks. As of 0.6.1, mixed @media parent declarations are kept, and already-nested descendants are not cloned.
- .box { padding: 1rem; } /* later */ .box { margin: 0; }
+ .box { padding: 1rem; margin: 0; }
dedupe-identical-declarations Refactor
Removes exact-duplicate declarations and identical nested rule copies in the same block, keeping the first. Different values are left untouched so cascade order does not drift. Gather applies the same cleanup when merging related rules.
- display: none; display: none;
+ display: none;
nest-layer-by-selector Refactor
Factors the exact same selector living in multiple named @layer blocks into .sel { @layer a { … } @layer b { … } }. Preserves layer identity and first-declared layer order. Never nests @layer inside another @layer (that would create a child layer).
- @layer tokens { :root { A } } @layer base { :root { B } }
+ :root { @layer tokens { A } @layer base { B } }
prune-overridden-declarations Refactor
Removes dead properties and entire rules overridden unconditionally later in the cascade.
- color: red;
+ color: blue; /* kept winning override */

🔬 The 8-Point Safety & Proof Engine

CSS is order-sensitive and specificity-fragile. CSSForge verifies 8 strict mathematical invariants before applying any mutation:

Eight invariant proofs CSSForge checks before applying a mutation
Invariant Proof Check Guarantee & Protection
specificity_equivalent Guarantees that :is() factoring does not lift branch weights or break later cascade overrides.
declarations_preserved 100% exact retention of property names, values, vendor prefixes, and custom properties.
important_preserved Strict retention of all !important flags across nested or refactored rules.
cascade_order_equivalent Ensures no intervening rules are crossed during non-adjacent nesting or grouping.
at_rule_equivalent Ensures query expressions in @media, @supports, and @container are unchanged.
layer_precedence_equivalent Safeguards normal and important declaration ordering inside @layer blocks.
scope_boundary_equivalent Prevents invalid automatic conversion into @scope without explicit design boundaries.
syntax_lossless Splices replacements strictly into target byte spans. Untouched lines remain 100% byte-for-byte identical.

💡 Why CSSForge? & The Gaps We Filled

The CSS tooling ecosystem has long suffered from a structural asymmetry:

Ecosystem gap: lowering tools versus CSSForge modernization
Direction Ecosystem State Existing Tools The Dilemma
Nested ➔ Flat Lowering Mature Lightning CSS, PostCSS Nesting, esbuild, LibSass Deterministic, mechanical compiler lowering. Desugaring & into flat rules or :is() is grammatically straightforward.
Flat ➔ Modern Nested Fragmented NestYourCSS, stylelint-use-nesting, css-nesting-converter Cascade-sensitive refactoring problem. Existing tools either produce invalid CSS, break specificity, destroy code formatting, or only fold basic :hover states.

Critical Gaps Solved by CSSForge

🛡️
Dual-Layer Zero-Regression Engine
LightningCSS AST parser is used purely for grammatical validation. All file mutations are performed by a surgical Byte Patch Engine that replaces strictly target byte spans. Untouched lines, developer comments, custom indentation, and quote styles remain 100% byte-for-byte identical.
🔬
Mathematical Specificity Proofs
Calculates exact specificity vectors (a, b, c) for every input and modernized selector branch. Zero specificity drift guarantee ensures elements match with identical priority across browsers.
🚫
Refusal as a Core Safety Feature
When an AST transform would violate native CSS semantics (such as :is() lifting specificity on lower branches or invalid BEM string concatenation), CSSForge explicitly refuses the transformation instead of generating broken stylesheets.
✨
Multi-Selector Cluster Factoring
The first and only tool capable of gathering multi-branch rules sharing identical bases (e.g. .card .title, #hero .title + .card, #hero) into unified, clean :is() blocks with nested children.
🖥️
Zero-Config Terminal TUI Workbench
A full-screen, responsive keyboard-driven interactive workbench powered by Ratatui & Crossterm with live unified diff viewing ([d]), preset cycling ([p]), and 4-step guided refactoring.
🔒
Dirty Git Working Tree Guard
Detects uncommitted Git modifications before writing and automatically blocks destructive in-place replacements unless explicitly confirmed by the user.

🚫 Strict Non-Goals: What We Do NOT & Will NOT Do

To preserve 100% cascade safety and developer trust, CSSForge enforces clear architectural boundaries and will never attempt unsound heuristics:

❌ Anti-Goal

No BEM String Concatenation (&__element)

In native CSS, & is a selector token (desugars to :is()), NOT a Sass string concatenator. Writing .card { &__title { } } is invalid in native CSS. CSSForge strictly refuses BEM token concatenation.

❌ Anti-Goal

No Specificity Inflation / Lifting

:is(A, B) takes the maximum specificity of all branches. If wrapping rules into :is() would artificially lift the specificity of a lower-specificity branch and alter cascade matching priority, CSSForge refuses the refactor.

❌ Anti-Goal

No Destructive Minification or Re-Serialization

CSSForge is a semantic modernizer, not a destructive minifier. We will never re-format untouched code, strip developer comments, convert colors, or drop intentional browser fallback duplicate declarations.

❌ Anti-Goal

No Pseudo-Element Nesting Traps

Native CSS & cannot legally nest under parent selectors containing pseudo-elements (e.g. ::before, ::after, ::marker). CSSForge strictly identifies and prevents invalid pseudo-element nesting.

❌ Anti-Goal

No Arbitrary At-Rule Moving Across Barriers

We will never hoist or pack @media / @supports blocks across intervening selector barriers if moving them would invert the source-order cascade for overlapping properties.

❌ Anti-Goal

No Runtime Heuristic Guesswork

We do not guess at runtime JavaScript class additions or dynamically toggled DOM hierarchies. All refactoring operations are statically verifiable and mathematically provable.

📊 Global Market Comparison Matrix

A comprehensive architectural comparison of CSSForge against existing industry tools:

Comparison of CSSForge with Lightning CSS, NestYourCSS, stylelint, and related tools
Tool / Project Primary Focus Strengths Critical Gaps & Weaknesses CSSForge Advantage
Lightning CSS
(Parcel / Rust)
Nested ➔ Flat & Lowering Blazing fast (2.7M lines/s), browser-target aware. Not an aesthetic refactorer. Does not modernize flat CSS to nested CSS; reserializes entire AST. Lossless Modernizer Dual-layer patch engine preserving untouched lines & comments.
NestYourCSS
(@nycss)
Flat ⇄ Nested Balanced/Max depth strategies, Web GUI + CLI. Uses character lexer without semantic proof engine; cannot prove non-adjacent safety or at-rule safety. Mathematical Proofs Full 8-point invariant proof engine with zero specificity drift.
stylelint-use-nesting
(CSSTools)
Flat ➔ Nested Linter Safe, incremental autofix. Narrow scope: only folds immediate adjacent pseudo-classes (:hover). No :is() factoring or at-rule restructuring. 28 Rules Catalog Handles complex combinators, multi-branch clustering, at-rule merging, and layer-safe selector gathering.
css-nesting-converter Flat ⇄ Nested CLI PostCSS plugin, layer wrapping. Strips SCSS variables blindly; lacks specificity verification and cluster factoring. Safe Clustering Preserves all variables, prefixes, and declarations with zero loss.
postcss-nested
(Evil Martians)
Nested ➔ Flat (Sass-like) 14M+ weekly downloads, popular in build setups. Violates native CSS semantics. Employs Sass-style string concatenation rather than :is() tokens. Native W3C Standard Strictly emits spec-compliant native CSS nesting and modern selectors.
cssnano Minification Merges adjacent rules, discards dead code. Destructive by default: drops comments, strips intentional duplicate fallback declarations. Lossless Refactoring Retains duplicate declarations, custom properties, and fallback fall-throughs.

💾 7 Output Modes

CSSForge provides flexible output strategies for testing, safe local refactoring, and automated CI pipelines:

CSSForge CLI output modes and when to use each
Mode Flag Description Recommended For
--output dry-run Calculates all plans and displays diffs without touching any files on disk. Audits, exploratory reviews
--output new-file Writes modernized stylesheets to sibling *.modern.css files. Safe local trial (Default)
--output out-dir <path> Mirrors project folder structure into a designated build directory. Build tools, bundlers
--output overwrite-with-backup Overwrites the source file while creating an automatic *.bak copy. Local refactoring
--output overwrite Atomic in-place replacement (Requires Git clean tree check). Version-controlled codebases
--output patch Generates a unified *.patch file compatible with git apply. Code reviews, PR workflows
--output stdout Outputs modernized CSS directly to stdout for Unix pipeline piping. Command-line piping

📦 Crate Architecture

CSSForge is organized as a modular Rust workspace published across three official packages:

Published CSSForge workspace crates and their roles
Crate Name crates.io Documentation Role
cssforge v0.7.0 Docs.rs Standalone multi-platform CLI and TUI workbench executable.
cssforge-core v0.7.0 Docs.rs Pure Rust AST parser, specificity engine, and 28 transformation rules library.
cssforge-tui v0.7.0 Docs.rs Ratatui-powered terminal user interface component and visual diff viewer.