Stepping Away from Sass
cathydutton.co.uk
cathydutton.co.uk
There's a few ways to combine Sass variables and CSS variables:
- make all Sass variables available as CSS variables as well, so $primary will also exist as --primary
- assign a CSS variable to a Sass one: $primary: var(--red)
- assign a Sass variable that was defined as a CSS one: $red: var(--red) and then $primary: $red
Color functions are one aspect that isn't well supported yet by CSS only.There's also a more "philosophical" question: why offload the variable resolution to the client side when it can be done at compilation time? If you're not gonna update a variable's value at runtime, it doesn't really need to be available as a CSS variable.
It's like single page apps that always render the same content, and are better off rendered once on the server side and delivered as static HTML, instead of being rendered thousands of times by each client.
But to be fair, CSS variables have other benefits, like creating color variations very quickly:
// Sass
.is-success
background-color: $green-invert
border-color: $green
color: $green
&:hover
background-color: $green
border-color: $green-invert
color: $green-invert
&:active
background-color: $green
border-color: $green
color: $green-invert
// CSS
.is-success
--color: var(--success)
--invert: var(--successInvert)
Even with CSS variables, Sass still has a lot of benefits listed by other commenters here, like reusable mixins and nesting. And one of my favourites: @extend!Imagine I have a property that I need to change depending on screen resolution in order to make a responsive layout.
In my CSS rule I specify it to have a fallback default and to take a CSS variable that over-rides it.
Elsewhere in my CSS I define the CSS variable with a media query.
The CSS rule is quite clear in that it shows that something changes or can change.
In the olden days I would be declaring the CSS rule twice. Once for the default and once inside the media query.
With the old fashioned approach it would not be clear looking at the CSS rule that it had an over-ride somewhere else in a media query.
By using the CSS variable with a fallback in just the one rule I know that something in the rule changes. I can easily find the CSS variable, regardless of how it has been scoped to see what makes it get set. Since I know the convention for most of the CSS in the stylesheet, e.g. mobile first, I can guess that the variable is for the desktop setting.
The advantage is that I have code that I can hand on to the next guy and feel happy that even if they are not yet up to speed on CSS variables they will be able to easily work out what is going on with individual rules.
There is no point in Sass variables now, they are defunct. All of the functionality is superseded and there is no point learning two ways to achieve the same task when you can just master one. This trumps any 'compilation' speed ups, resolving variables is something that does not slow page load times.
The only use case for not using CSS variables is if you have to support people using extremely old browser.
So I can use Stylus to override one CSS variable and change the appearance, instead of having to save the website's entire css file, find all rules set to the "variable value" (aka a particular font stack), and redefine every rule to use my personal font stack.
Variables are still new and not fully supported.
Being able to nest css selectors is major win for me.
Reusable mixins is another.
I just don’t see a compelling enough use case today to switch to pure css for non trivial applications.
* Duplicating the effect of mixins by just... having another css class for the mixed-in stuff works well enough for many cases (though this hurts most where you're trying for semantic selector naming conventions, with mixins adding in rulesets behind the scenes).
* Nesting selectors save thinking and some typing regarding where styles cascade out to, but... tbh, yanking a high-resolution selector into the buffer and chucking it out onto another line was never a burdensome part of my pre- processor workflow.
* Pretty sure free-use of nested selectors and mixins is trading larger CSS files for developer attention (which may or may not be a valid trade).
* Variables... yeah, they're handy. Search-and-replace only goes so far. IE seems to be the holdup.
For small or disciplined teams with the freedom & skill to really think about their CSS starting from a style-guide-first perspective rather than every-page-a-custom-layout perspective, I can see making the choice to do without. Especially if you've already ditched IE 11 support for other reasons.
Where SASS shines is when you need to do a coordinated change, e.g. these things become gray instead of black. You have a ton of black things so no search and replace. And once you've painstakingly determined and edited the classes, the hard part is to be sure that you only changed what you had to, and nothing unrelated.
SASS preserves semantics which plain CSS is unable to express. It's like C vs assembly.
Because I've seen sass refactors go just as badly.
They also aren't a full sass/less replacement, just one aspect
At the very least support of nesting is a huge convenience, but in practice I've also found that it saves me from all sorts mistakes, whether it's because I'm being stupid, or refactoring things.
That said, overusing nested selectors can be a problem too. I generally try to limit myself to two levels of nesting (component -> element), and then a third for pseudo-classes (:hover, :selected, etc.) and pseudo-elements (:before, etc.).
I just feel that nesting (which Sass encourages) leads to complexity and fragility.
Assuming you're using a component framework like React, Vue, or Angular (and not a shadow dom based system like Polymer), how do you isolate your component CSS?
When I make a component 'MyComponent', I give it the className 'MyComponent'. Then I can create a SCSS file and scope its entire contents:
.MyComponent {
// all the css specific to MyComponent
}
Without nesting, how do you keep your component-specific CSS from polluting the rest of your app? Do you scope every rule every time? Isn't that tedious?In Vue and Angular component styles are automatically encapsulated at the component level by scoping (I think) so nesting isn't really necessary to target specific components. Angular also recently deprecated a method of breaking out of component styling and soon it will be completely removed from the framework.
Although I'm a critic I'm not totally against nesting, I just feel that SASS encourages it, so without discipline things can get very complex and fragile. One level of nesting is OK with me though I suppose...
In what way(s) is SASS different than anything else in this regard?
These solutions all seem quite a lot more complicated than nested scopes! And at least for styled components (angular, I presume) and styled-jsx (react), they're framework-specific. Why are these solutions better?
I found scoped css like component or BEM just make sense, which only encourage you nest one level in block namespace.
https://stackoverflow.com/questions/13829959/nested-selector...
It's about Less but the same rule applies to CSS and Sass.
You’re manually creating nesting hierarchies and naming them (something a computer should be doing).
And yes, the moment you move something, everything breaks. Because instead of moving/renaming just the parent selector you have to move/rename all the related child selectors.
And BEM is a workaround of names pace, not nesting. Nesting is not what CSS lack of. Instead what CSS really lack for is basic abstraction like function (same as function component in react to HTML). Given CSS is basically similar to key value pair (data), a first class function model would solve most of the problems - reusing, encapsulation, abstraction etc.
In some cases I can preclude the necessity of having to declare class names.
section {
h1 {},
p {},
img {},
}In other cases I have an index with easy-to-find classes.
.hero {
.gradient {},
.description {},
.signup {},
}EDIT: I meant to reply to a different comment but oh well.
Is there another kind of CSS?
Terminology is important but I see it's not important among some people and other hobbyists online.
There is a meme within the community about 'vanilla JavaScript' called VanillaJS:
https://stackoverflow.com/questions/20435653/what-is-vanilla...
The overwhelming majority of front-end projects and developers that I encounter use some kind of library, framework, or compilation/build tool. It's common enough to basically be the default.
If you use 'plain' or 'vanilla' javascript (or CSS), you're doing something less common, at the very least around these parts, and so qualifying this makes sense. The mere fact that many people clearly seem to feel a need to point this out makes it so. Why would they otherwise?
Judging by your comment history, the vast majority of your contributions here boil down to some kind of dig at how 'people these days' are 'doing it wrong', and how obviously you are superior to that and how you don't see the need for any of this new-fangled stuff. I'm honestly curious whether that's just role-playing, or if that kind of superiority about being older and wiser is somehow important to you.
I'd very much prefer it if you shared the wisdom you've accrued in a more constructive way, because as far as I can tell you're not actually wrong about a lot of this stuff.
Older and wiser curmudgeons have played a valuable role in my development, but your approach strikes me as self-serving and somewhat poisonous to the/any community. But of course you're free to do as you please.
https://stackoverflow.com/a/40115909/135845
Unfortunately, I see that "The Ubuntu Web Team" decided to shit the bed and steal the name for the CSS framework they created. So fsck me I guess.
I've just spent yesterday getting sass to work with my Golang project.
I used to have a nice simple makefile task, that compiled the project (in a split second) and launched it on localhost so I could find out where the problems were. My only dependency was the Go compiler. If I changed the css, the next page refresh sorted it.
Now I have a "sass watch" task, which compiles the scss to css. But that requires node.js, and npm (there are golang scss compilers, but all of them are either not being maintained, or have problems, or only support older versions of scss). I have to run this in a separate terminal so I can stop it when I need to.
I've deliberately not looked at my global node_modules directory. If I don't look, I don't have to worry about how many js libraries just got installed on my machine, and I don't have to think about who's maintaining them, and if they're now malicious. As long as sass only touches the css files, and css isn't Turing-complete (yet), then I don't need to audit any of that, and I can assume that it's not injecting malicious code into my application.
Though there are possible attacks on my site that could originate with code injection from a $malicious_left_pad js library, even via css, but it's enough of a stretch that my paranoia can live with it. I can check out the compressed css every now and again and see that it's not importing anything it shouldn't, and be reasonably sure that I haven't just compromised all my users' security.
So yeah, it's not so much that a preprocessor is a complication, it's that a preprocessor allows a malicious package to compile whatever it likes into files that I will then serve from my domain to my users, who trust that I'm not serving them malicious files. That's policed not at all by npm, or any of the package managers, who cheerfully assume that all javascript developers are honest.
SassC is just a cli tool that uses the C-language SASS library. It's an alternative to using whatever ridiculous NodeJS solution was/is being used.
You don't need to assume, npm has an audit command that helps figure out if any of your package dependencies have reported vulnerabilities: https://docs.npmjs.com/cli/audit
Does Go?
No, Golang doesn't, but then it doesn't have a registry like npm in the first place. Gophers tend not to use third-party libraries if at all possible.
That sounds weird to me. It's pretty rare (almost never?) for applications to do useful things without third party libraries. eg any database access, input validation, making sure HTML output isn't malicious, etc.
Maybe that's just me though?
There's also a set of "semi-official" libraries for things like crypto that can't be written by amateurs ;)
There are some other libraries that are commonly used, but you can count them on one hand.
I'm a massive convert to this. Instead of spending my time working out how to get two 3rd-party libraries to talk to each other, I spend my time writing domain-specific code. and my debugging time is spent in repo's that I have a hope of understanding because I wrote them ;)
It doesn't suit everyone, and there are frequent rants on golang forums about people not importing dependencies. But every gopher seems to go through the same journey, and end up at a place where they just write their own code rather than importing it.
Just got sassc working instead, so happy again hehe
I still love SASS since it solves many existing problems at once like you mentioned.
In a corporate environment, too, you need to proxy many of these, and set an environmental variable in the shell for SASS to install properly. Over time, this adds significant cost, pain, and stress to the install process.
My personal solution to avoid running watchers is to have a run-on-save task in VSCode that automatically compiles any "root" .scss file (eg */index.scss) into a .css file with an accompanying .css.map file.
Nowadays I just do functional CSS (Tachyons, Tailwinds, Fractures, etc). It has some drawbacks but you can go faster and change things more atomically without breaking everything when changing a class, and you have the safety given by the fact that every property have the same level of specificity (which can be overriden by JS generated style attributes, in case you need to manipulate the DOM directly)
Also, you can address your rendering needs atomically which works very nicely when you're writing components in isolation. They also are an excellent approach when doing server-side rendering since you can store classes combinations in variables that can be passed from one template to another or used and overriden in the same template as it gets evaluated by the server. It's almost like a pre-processor without the hassle of recompiling your sheets.
As mentioned, theming and consistent ux requires far less writing and copy paste with a preprocessor than in pure CSS.
I think React's approaches make CSS feel simpler but often just sweep the inefficiencies under the rug.
A lot of this is being fixed with CSS4, so these will go the way of jQuery. And while you don't need jQuery today, it served as an important polyfill for a long time.
CSS4 won't have nesting. SCSS isn't going away as long as nesting isn't a thing in native css.
(Also css variables are awful to use. Honestly, CSS-WG should have just copied most of scss's syntax)
I'm unsure about the merits of nesting. Tends to make for some hard-to-read code.
.a-long-class-name {
background: firebrick;
&:hover {
background: red;
}
}
This approach has some clear benefits (less verbose and it groups related code together) with little or no downsides.& > div, & a, & a { &:hover } etc.
.some-class {
> div, a {
background: blue;
&:hover {
background: red;
}
}
} .selector .child_one,
.selector .child_two {
color: #000;
}
instead of, .selector
.child_one,
.child_two
color #000
I no longer type a semi colon when developing a web site thanks to preprocessor and transpilers.FYI the CSS Working Group approved a working draft of native CSS Nesting last month; it's in "stage 1."
https://drafts.csswg.org/css-nesting-1/
There's a PostCSS polyfill you can use for it today. https://github.com/jonathantneal/postcss-nesting
sadly this use case seems to be missing from the coming spec :(
> Yeah, very intentionally not allowed. It causes parsing issues, for one (you can't arbitrarily split up an ident and be guaranteed that both halves are still idents), and it's grammatically ambiguous with tagname selectors.
There's an open issue https://github.com/w3c/csswg-drafts/issues/3748 "add note explaining that BEM `&__element` class accumulation isn't supported and why"
I already have something better than CSS in 5 years when most browsers in the market get the newer CSS, so I don't see a point in following CSS now.
Better to spend time making the preprocessor more accessible to developers.
CSS is the #1 place where I spot those cases. And I feel that Sass and other CSS preprocessors mostly lend themselves to shooting yourself in the foot with abstracting similar things that aren't actually of the same category.
A better abstraction in my opinion is a utility CSS library like Tailwind.css. It feels strange at first, but compose-able utility styles eventually feels like a good abstraction, with less shots to your feet.
Exchanging sass with postcss based framework in my book is the worse abstraction for almost any purpose.
I feel postcss-based styling is simply more predictable and easier to refactor. And the abstraction, which can be clunky at first, becomes a sort of intuitive DSL which let's you develop faster...
My point is that the libsass library should be ported to WASM to ease the installation in a CI environment.
It has loops, functions, namespacing, arrays, maps, and tons of utility functions (especially around color).
If you treat it as CSS+ then you're not really benefitting from it's true potential.
That sounds like a solution looking for a problem... The vast majority of real SASS I've seen has been much like the stuff this article mentions.
> If you treat it as CSS+ then you're not really benefitting from it's true potential
People aren't looking for "potential", they're looking for a way to accomplish their design goals whilst keeping their stylesheets maintainable. The real use cases where SASS is actually worth the complexity overheads are getting fewer and fewer.
Is this a common pattern in industry as well?
OTOH, css frameworks go the other way. The more you have to do, the more apparent that bootstrap etc. get in your way, largely because css isn't a "complete" language.
I know some few who swear by them, but the majority of people I know who did html/css exclusively for years feel they can write by hand anything they would want from bootstrap in less time than they would waste in fighting things like selector specificity and other inflexible choices.
YMMV of course, and you can pry postcss + preset-env + mixins from my cold, dead hands.
It is on the server-side (php, python). Sophisticated apps can easily outgrow a particular framework. Though, I think php frameworks are pretty mature these days and this is less of an issue there than it once was.
For me, as an SRE with higher priorities, you can pry Bootstrap from my cold, lifeless fingers :) I have zero interest in keeping up with latest and greatest in CSS developments; like you said, Bootstrap gets you something up and running fast without much fuss. It’s good enough and it allows me to focus on other important areas.
An actual 'good' approach is to mostly utilise vanilla tooling like you suggest, but there are some exceptions, you can't be absolutist about it.
I think for a decently sized web application, React (and only React, not all the cruft people usually include with it) will save you a lot of effort, and not using it would be a mistake. You could go with something like Vue instead but React does a much better job at abstracting away the huge amount of utility it provides. It has a smaller API surface by an order of magnitude.
I think a good thought exercise is to think about what a 'perfect' implementation of the library you're thinking of using would look like for your exact use case, and compare it to using the actual library. Even if my use case doesn't include any performance requirements, I don't really think my implementation of a React-style solution would have less than 1k-2k LoC, and the implementation wouldn't be very simple. Contrast that to something like Redux which could be roughly implemented in ~20-100 LoC for most use cases and is conceptually very simple.
Is it considered bad practice now? How did it rise so quickly, how did it fall so quickly, what's going on in the web dev world?
Some novel things like a primaryColor variable to easily adust your theme is easily replicated with a traditional find/replace.
The fewer frameworks / compiles-to X languages you have to learn the better.
IMO, a little redundancy is better than a little complexity or a little dependency.
The original reason I switched was for nested structures so I could stop repeating myself. E.g.
div {
&:hover {}
.class ul {}
}This made it much more pleasant to write in sass.
The other thing is mixins. If vanilla css doesn’t support this then it’s still a no-go.
Lastly I’m confused why sass of all things is an issue. It’s basically css, and very lightweight. You just add one step in your preferred build tool to transpile sass -> css. “vanilla is magically better” is not an argument.
CSS now has variables (if you really need them -- chances are you don't).
The redundancy of writing a selector multiple times is sightly annoying, but I don't think it rises to the level of value I need to include a new dependency in my app or build pipeline.
[0]: https://css-tricks.com/custom-user-mixins/
These are preferable because you can compose them as needed for elements, instead of having to make extra classes.
> CSS now has variables (if you really need them -- chances are you don't).
I fail to see how you could -not- need variables. Not many, mind you, but using none at all boggles the mind. For starters, DRY code is fairly fundamental. When you update a referenced variable you only need to do it in 1 place. Relying on "find and replace" leads to "oops I accidentally missed one, now we have a bug that could have been totally avoided".
It also helps with consistency for site theming/branding. You can define $primaryColor, $primaryHighlight, $secondaryColor, $textColor, $backgroundColor, etc, and reference these down the line instead of copy/pasting and getting bugs if the specs change.
Isn't that the point of selecting classes in the first place? That said, I understand the utility in using it for things like individual colors, as you described.
Any self respecting programmer will find this "solution" abhorrent and inelegant.
There's a trend to throw a library at the problem, but that comes at a cost.
I don’t work in a tech-focused business, because I work in the public sector. That means our funding is fairly limited, even though 90% of our workforce spends 5-8 hours a day on some for of smart device or pc. Because we’re limited, however, we need to be careful about how we spend our resources, and that means we simply can’t keep up with the modern frontend environment.
If all you do is angular, then the transition from AngularJS to angular 2 might have been smooth, but it sure wasn’t for us, and neither would the big react-versions be.
We also can’t really take advantage of the package/library environment because we’re not as fault tolerant as others. We can’t have security issues, but we also can’t code-review 70 packages/libraries every week because there was an update.
As a result we’re back to using the old MVC frameworks that don’t change every day and have solid standard libraries. We did buy a frontend “platform” so we can have things like editable grids without constant page-reloads, but in general, JS is something we add to a specific component only if it’s absolutely necessary.
I am looking forward to when Flutter finally has the ability to build websites. Because then we’ll have both mobile platforms, web and desktop frontends covered in one tool. Which is frankly exactly what we need to be productive in 2019, that or we’d need to hire 2-3 people, and the latter is just not happening.
Not like C/C++ programs, where we have a super simple setup of Make, config, and autoconf. Like checkout this easy-peasy Make file: https://github.com/apache/httpd/blob/trunk/Makefile.in . Even a child could understand it.
Or look at Java. Who has ever seen a complicated ant or pom file? No one ever. This Lucene ant file practically wrote itself: https://github.com/apache/lucene-solr/blob/master/lucene/bui...
/s
I guess my snarky point is that build systems are complicated. It's like the Bjarne Stroustrup about programming languages. There are two kinds of build systems: the ones people complain about the ones no body uses. Robust build systems have to handle the nearly endless combinations of different requirements each app brings to the table...complexity is table stakes.
Modern web apps are built to be able to run on a myriad of different platforms, as they have to maintain compatibility between tons of version of several different browsers running on a slew of different device types running different operating systems. Did we think that was going to be easy?
People like to shit on webpack, but I don't get it. It's a well thought out tool that works extremely well.
And what are the alternatives? Yes, yes, yes, I know, your blog/website/thing you have is just plain ol' html and and you stick some javascript in a script tag or whatever. No need for any of these fancy build scripts, blah, blah. So what? That's like telling the people at lucene that all this arcane index stuff is overkill because you just search your hard drive using ripgrep.
You can't make a real app like Slack, Spotify, VS Code, or Google Docs without a serious build process. People are making photoshop, for the browser. They're not going to do it with ES3 they write directly into a script tag.
The problem isn't that webpack is bad but too many projects use it where there's not need for it. Also for newcomers to the js world it looks terrifyingly complicated. Sure those bootcamp rookies eventually discover how terrible C++ buils are but who cares since this is more like comparing a spoon with a shovel.
Well, I feel that bit of nuance gets lost most of the time. It’s usually just simplified to “all these JS tools are a mess.”
It’s like if people all started complaining that rust is bad because they saw someone write a rust program when that person should have just written a bash script.
I hate writing media queries, but
@include media("<desktop", ">tablet") {
}
Takes most of the pain away.Your font is too big.
For SPA I found it's easier to use CSS-in-JS solution like styled-component or emotion. Or for Typescript I just use typestyle.
For non SPA I usually go vanilla CSS for simple stuff and stylus for complex stuff.
However it violates the principle of least astonishment. How come a button inside .header differs from the button inside a .footer, then where are the differences defined, inside the button.scss or header.scss? Then one must take into consideration CSS Specifility and sprinkle !important everywhere.
Instead, use modifier (in BEM) or create two buttons (.header-button and .footer-button) or create 2 utility class (.is-header-btn, .is-footer-btn) or just use TailwindCSS and you're good to go for all kinds of requirements, without ever resort to !important
That's true. On the other hand, who is still trying to figure those things out without using their browser's dev tools and source maps?
Using Sass also doesn't mean that you can't use two button classes or utility classes.
Chrome Dev Tools help to find what's glitchy when styling misbehaves.
I'll still use Sass for the convenience of nested selectors, though.
Sass certainly pushed things forward in CSS world. Without Sass and LESS, Stylus et al. I am quite certain we wouldn’t have CSS custom properties and colour functions. Great additions to the language.
That said, I left Sass specifically some years ago. https://benfrain.com/breaking-up-with-sass-postcss/
Tangentially related — in terms of dealing with CSS tooling/output for large projects with many devs I have found the PostCSS ecosystem indispensable.
I wrote this a few years back after setting things up with PostCSS at bet365.com this way: http://ecss.io/chapter9.html
The good thing about PostCSS is you can reduce the features as CSS becomes more capable and easily incorporate extra tooling like autoprefixer.
> I also unintentionally, (at least at first) removed all traces of Sass from my codebase.
Especially this part, still thinking that was "SaaS" :)