I still use Sass for some projects and find it perfectly sufficient in most cases. Because of this, I don't really see moving away from it anytime soon: it feels like a nice little addition on top of CSS, that's not overbearing and most people can also get up to speed with the features that it provides.
Nesting being one of the things that I think CSS should have had out of the box, but that might change for the better eventually: https://www.w3.org/TR/css-nesting-1/
That said, various CSS component frameworks, utility class frameworks (or libraries) also seem nice, from Tailwind or maybe PrimeFlex (which feels nice with PrimeVue or PrimeReact), all the way back to aspects of that approach in Bootstrap.
Things can get a little bit messy when you mix both of the approaches a lot, though. Parts of the style are in the utility classes but for some reason you also have your own custom classes and so on, which I've seen sometimes.
I feel obliged to share a gotcha regarding nesting...
The idea of nesting in SASS/SCSS is that you need a combination of selectors, and you don't want to repeatedly type out the first one.
What I often see though is developers using it as grouping structure, closely mimicking the nesting of the markup they're working with. This is massively convenient from an authoring point of view, but it leads to issues.
For complicated components, I've seen output consisting of 12 selectors after each other, whilst only 2 would be needed. Not only does this lead to verbose output, it also makes that style rule extremely specific. In case you ever need to overrule it, you'd need 13 selectors or an !important.
To illustrate:
.selector1 .selector2 ........all the way up to .selector 12
Or:
.selector1 .selector12
Example 2 is what you want. You got your parent scope and after that it doesn't matter where .selector12 is in the markup, assuming you use something like BEM. The 10 nesting levels in between are completely pointless.
> Tailwind adherents say this isn’t a problem because, once you recognize that you’re re-using certain Tailwind classes too frequently, you then simply extract them to reusable components and then @apply them, as the Tailwind documentation suggests. I agree that this is a workable strategy, but my own experience suggests it can get out of hand quickly, even in a small project.
The @apply approach is really only for single-div components or as a last resort. The default way you should be reusing styles when using utility classes is to extract each component into its own HTML template file so you can update its styles from a central place e.g. your hero header might go in a file called hero-header.html along with all the relevant HTML + utility classes so you can update it from a central place.
I think this is really central to understanding Tailwind's claim of being scalable when compared to the traditional way of using CSS classes. The examples here are worth considering carefully (especially if your knee-jerk reaction is "it's just inline styles and inline styles are bad practice!"):
> There are numerous pieces on the web saying that one of the biggest hassles in dealing with code is naming things (e.g., variables, functions, and CSS classes), and that this is a pain which Tailwind can remedy by keeping you from having to name more than a handful of CSS classes.
You'll still have names for your component files and you can add comments to the HTML if needed. Will you really miss classes like `hero-inner-container-wrapper' that you were forced to add to a `div` to apply a single class? A lot of `div`s like this won't have meaningful names because they're purely presentational or were only added to get around some CSS limitation.
two things that sass has that i want to see in plain ol' css:
1. nesting - but this is coming!
2. mixins - not even static mixins are coming =(
static mixins might seem to be limited in utility at first, but it'd alleviate a lot of duplicate css by allowing you to have orthogonal groups of styles. so you could have sets of static mixins for borders/shadows, spacing, layout, fonts, etc. that you could mix and match into both element and class styles as you need.
too much nesting can lead to a rat's nest of css (ha), but shallow nesting (1 level mostly, 2 levels on occasion) adds clarity and structure.
Nothing, because also PostCSS is a specific program, while there’s a ton of ways to compile SCSS… Ruby, node-js, built-in compiler in the IDE, GUI frontends, etc.
Posted on another thread and haven’t heard an answer, so I’ll post it here too… Serious question: how do you refactor code when using nesting? In my experience it makes styles impossible to audit.
I tend to use nesting in combination with some form of scoped CSS (e.g. CSS modules, or the built-in forms available in Vue/Svelte). For me, this helps a lot with refactoring code, because each style is only relevant to a single component. Nesting is then useful within that component for handing specific states (e.g. `.active`, or indeed `:active` and other pseudo-classes), but it can never get too out of hand, because the CSS as a whole is very connected to its component. If I change the component markup, I know where to look up change the CSS (and vice versa), and if I'm making broader changes then I know quite easily when a particular file or declaration is no longer necessary.
Mixins could be done, but I believe we'd need some sort of lexically-scoped reference first, so that we're not trying to apply mixins that can dynamically based on style resolution, which is what sunk @apply. See https://github.com/w3c/csswg-drafts/issues/3714
yah, i've read that thread before, as well as tab atkins' "abandoning @apply" post, and i think both are trying to solve problems that are beyond css itself (js-style scoping in the first, and shadow dom styling in the latter).
what i'm imagining is something more like this very contrived example:
:root {
--thick: 4px;
--border-color: black;
}
$standard-border {
border: 2px solid var(--border-color); /* not evaluated here */
}
div {
@apply $standard-border; /* evaluated here */
}
/* somewhere else, perhaps a different file */
.bordered {
--border-color: grey;
@apply $standard-border; /* evaluated here */
border-width: var(--thick); /* an override */
}
here, the string would simply be substituted into place and then evaluated as if the substitution had been written there all along. this seems like it would avoid the multiple evaluation and circularity issues that have sunken prior proposals.
yah, as i noted in my other reply, tab in that post seems to have been trying to solve the problem of sharing css across regular dom and shadow dom, rather than focusing on regular dom css and trying to remove the need for js and css preprocessors (which is what i'm more interested in). i could be wrong, as it's been a while since i read it.
> A day might come when we'll be able to just write vanilla CSS but we're not there yet.
Ummm... what? You can already just write vanilla CSS. It doesn't have all the features of Sass, but it's perfectly usable, especially if cascading is used sparingly.
Lots of people write large-scale vanilla CSS today, especially in the Lit community where we have native style scoping and where CSS strings are embedded in JS modules so we get proper import-based loading, sharing, and bundling.
Nesting being one of the things that I think CSS should have had out of the box, but that might change for the better eventually: https://www.w3.org/TR/css-nesting-1/
That said, various CSS component frameworks, utility class frameworks (or libraries) also seem nice, from Tailwind or maybe PrimeFlex (which feels nice with PrimeVue or PrimeReact), all the way back to aspects of that approach in Bootstrap.
Things can get a little bit messy when you mix both of the approaches a lot, though. Parts of the style are in the utility classes but for some reason you also have your own custom classes and so on, which I've seen sometimes.