User Rating 0.0 โ˜…โ˜…โ˜…โ˜…โ˜…
Total Usage 0 times
Category CSS Tools
CSS Input 0 lines ยท 0 chars
SCSS Output 0 lines ยท 0 chars

      
Is this tool helpful?

Your feedback helps us improve.

โ˜… โ˜… โ˜… โ˜… โ˜…

About

Manually refactoring flat CSS into nested SCSS is error-prone. A misplaced brace or a wrong & reference breaks the entire compiled output, and debugging generated CSS back to its SCSS source wastes hours. This converter parses your CSS into an abstract syntax tree, identifies selector relationships via longest common prefix matching, and reconstructs the rules as properly nested SCSS with parent selectors (&). It handles pseudo-classes, pseudo-elements, BEM patterns, combinators, media queries, and @keyframes. The tool approximates optimal nesting depth assuming standard specificity practices. Note: it does not infer @mixin or @extend usage since those require design-intent knowledge the source CSS cannot provide.

css to scss css to sass scss converter sass converter css nesting scss generator css preprocessor

Formulas

The converter operates on a deterministic parsing pipeline rather than mathematical formulas. The core logic follows three stages:

tokenize(css) โ†’ tokens[] โ†’ buildAST(tokens) โ†’ tree โ†’ nestSelectors(tree) โ†’ scss

Selector nesting uses a Longest Common Prefix algorithm. Given two selectors S1 and S2, the shared prefix P is extracted at the compound-selector boundary level:

P = LCP(parts(S1), parts(S2))

where parts splits a selector by descendant combinators (spaces) into an ordered list. The remainder after removing P becomes a nested child rule. BEM suffixes (__ and --) trigger the parent reference operator & concatenation instead of descendant nesting. Pseudo-selectors (: and ::) are detected via regex pattern /^[:&\[]/ and always use & joining.

Reference Data

CSS FeatureSCSS EquivalentExample InputExample Output
Descendant selectorNesting.nav .link { }.nav { .link { } }
Child combinatorNested with >.nav > .link { }.nav { > .link { } }
Pseudo-classParent selector &.btn:hover { }.btn { &:hover { } }
Pseudo-elementParent selector &.btn::before { }.btn { &::before { } }
BEM elementParent selector &.card__title { }.card { &__title { } }
BEM modifierParent selector &.card--dark { }.card { &--dark { } }
Adjacent siblingNested with +.a + .b { }.a { + .b { } }
General siblingNested with ~.a ~ .b { }.a { ~ .b { } }
Media queryNested @media@media (max-width: 768px) { .nav { } }.nav { @media (max-width: 768px) { } }
KeyframesPreserved at root@keyframes fade { 0% { } }@keyframes fade { 0% { } }
Multiple selectorsSplit & nested individually.a .b, .a .c { }.a { .b, .c { } }
Attribute selectorParent selector &.input[type="text"] { }.input { &[type="text"] { } }
Root-level propertiesPreserved at root:root { --color: red; }:root { --color: red; }
CommentsPreserved in place/* header */ .h { }/* header */ .h { }
@importPreserved at root@import url('a.css');@import url('a.css');
@font-facePreserved at root@font-face { font-family: X; }@font-face { font-family: X; }
Nesting depth limitMax 4 levels recommendedDeep selectorsFlattened beyond limit

Frequently Asked Questions

Media queries are preserved at root level in the output by default. SCSS supports both approaches, but root-level media queries produce output closest to the original CSS structure and avoid duplicated @media blocks that can bloat compiled output. If a media query contains selectors that share a common parent with rules outside the query, you can manually move the @media block inside the parent selector after conversion.
Vendor-prefixed properties are preserved exactly as written. The converter does not collapse them into @mixin calls because that would require assumptions about your mixin library (Bourbon, Compass, or custom). After conversion, you can manually replace groups of vendor prefixes with your preferred mixin. Look for repeated patterns like -webkit-, -moz-, -ms- clusters in the output.
The current implementation preserves values as-is without extracting $variables. Automatic variable extraction requires semantic understanding of design intent - for example, two elements sharing color #3A86FF might use the same design token or might coincidentally match. Extracting variables incorrectly creates false coupling. The output is clean SCSS that you can then refactor with variables based on your design system knowledge.
The converter follows the actual selector structure from your CSS. If your CSS has a selector like .a .b .c .d .e, the output will nest 5 levels deep. The SCSS community guideline (the Inception Rule) recommends a maximum of 3-4 nesting levels. Over-nesting produces overly specific compiled CSS. Review the output and flatten any nesting beyond 4 levels by extracting inner rules to their own root-level blocks.
Yes. :root selectors and CSS custom properties (--variable-name) are preserved exactly as written. They remain at root level in the SCSS output. The converter treats -- prefixed properties as standard declarations and does not confuse them with BEM modifier syntax (--modifier) because BEM detection only applies to selector names, not property names.
Complex comma-separated selectors with mixed ancestry (e.g., .header .nav, .footer .links) cannot share a common parent and will remain as separate rule blocks. Selectors using :is(), :where(), or :has() pseudo-functions are treated as atomic units and nested via the & operator. @supports blocks are preserved at root level. @layer declarations are passed through unchanged. If you use CSS nesting syntax (the native & operator), the converter will treat it as standard CSS and may produce double-nesting.