To be honest, the whole "it's hard to tell what's used by what" in plain CSS is already solved with BEM.
BEM scales very nicely.
So by using new CSS variables with CSS modules to create locally scoped overrides you can still create runtime themed styles.
Yes your "frontend" Devs will need to learn CSS instead of just treating it like the Typescript autocomplete object faceroll experience that current cssinjs promotes.
Other unpopular opinions I hold:
Nested CSS rules made popular by lesscss are a maintenance nightmare. Don't use them.
"Nested CSS rules ... are a maintenance nightmare."
Use things they way they're intended. If css rules are only intended to work in, say, tables, then nest them inside of tables. Or, just certain classes of tables. If they are to be general, then don't nest them.
These three .hilite classes will not conflict with each other; the deeper ones will always override the shallow one at the bottom, but only inside .french or .italian tables. Hilite will be orange in french tables; green in italian tables, and blue everywhere else in your whole app. If you want .hilite to do something else outside of your zoot-suit panel, write it that way:
Any HTML outside of the .zoot-suit panel will have to make their own style rules; they can use .hilite freely without any orange, green or blue bleeding in.
Only issue I have with this is that then you don't know if .hilite is something global, is it a utility class, is it something specific to that table only when you encounter it in HTML/JSX. If using .table__cell--hilite for example you know that it's a highlighted table cell and you can get all that data without having to look at the source code. And you know that it should be a table only thing here since it's kinda namespaced with table__
Also, your example uses same class name for global selector and then selector that is more specific to 2-3 scenarios which now makes that selector super situation specific and it doesn't necessarily convey its meaning just by reading it. In my opinion it also raises CSS selector specificity unnecessarily.
But to each their own, I have my preferences and you have yours. Lot of this stuff is of course also situational and cannot really be applied to 100% of projects/situations.
A developer with M.Sc. in Computer Science. Working professionally since 2010. In my free time I make music and cook.
Also I don't and after the recent events will not have Twitter.
Location
Budapest
Education
Eötvös Loránd University (ELTE - Budapest Hungary) Computer Science M. Sc.
Mixins... I FKN love them. They are excellent at naming certain CSS "hacks": things that only make sense together, things that must act together otherwise the effect you want won't happen. Now you could write comments around it, but named mixins equate well-named, descriptive function names from clean coding.
A developer with M.Sc. in Computer Science. Working professionally since 2010. In my free time I make music and cook.
Also I don't and after the recent events will not have Twitter.
Location
Budapest
Education
Eötvös Loránd University (ELTE - Budapest Hungary) Computer Science M. Sc.
To me both HTML-in-JS and CSS-in-JS look ugly, because you can't separate presentation from logic, hence can't divide this work between different developers without permanent merge conflicts.
I love BEM and it's what I use when writing regular CSS for my own use.
The problem with BEM isn't BEM itself, it's getting your coworkers, new developers
and external agencies to understand and follow the syntax. We implemented BEM at a previous workplace and it worked perfectly until some core developers quit and new ones were hired, and then the styles just quickly deteriorated.
CSS modules and CSS-in-JS are locally scoped by default, and you have to specifically add syntax to make things global. As much as I dislike Tailwind syntax, that too has the advantage of keeping styling local to the place it's applied at least.
The problem with BEM isn't BEM itself, it's getting your coworkers, new developers and external agencies to understand and follow the syntax
This is my experience as well. BEM can scale really well, but keeping it consistent is a problem when you have multiple people working in a codebase. I mean same can be said for any tool. Getting everyone on board is usually the hardest part.
People are also detracted from BEM because of it's syntax and because it "looks ugly" to them, but once you actually start using it, your CSS becomes a breeze to use and read because its "standardized" in some way. It also produces flat styling without horrible nesting which is a big plus in my book. Simpler flat styling allows easier per situation override if needed and you don't have to battle CSS selector nesting and specificity that much at all.
When using BEM, I've usually went with BEM + utility classes approach. Some more common things like spacing (padding, margin), colors, and maybe some flex/block properties would be mostly in utility classes and everything else would be BEM. It worked quite nicely for projects that I've been working on. It also allowed me to build UI faster because I wouldn't have to apply same spacing values all over again for each component, but could just apply a utility class name to some wrapper element and get the spacing that I want.
These days when working with React, the projects that I usually work on use SASS Modules and that also scales nicely. It's easier to get people on the same page because there is no specific naming convention like BEM and people don't have to worry about naming things because everything is locally scoped. This approach works nicely in most situations except few edge cases where styles need to be shared or something kinda needs to be global, then it's not so elegant anymore and in a single file. But other than that, it works really well and I don't have to leave comments on PRs like "hey the name of this class doesn't adhere to BEM principles, it should be this and that etc."
I still prefer SASS Modules compared to CSS-in-JS (emotion, styled-components) because you are still "mostly" writing just regular CSS that is closer to the native web.
I'm also working on a project that uses styled-components and even though I do like type safety we get with that because we use Typescript, it also makes every little styling its own component and to see any conditional logic for styling I always have to jump to source of a styled component to see what's going on. At least with SASS Modules and conditionally applying classNames I can immediately see what class is being applied for a certain condition and just by its name I can probably tell what it does without having to go into its source.
When you refer to Sass Modules are you referring to this. How does that relieve you of having a specific naming convention like BEM? I use the new module system extensively but I still find the need for BEM. For e.g. I'm using the module system and BEM here. To me if in one module (say the button module) you have a class named .primary and in another module (say the link module) you also have a class named .primary then those classes will class with each other once the Sass is compiled to CSS. Can you elaborate more on that?
Sure. I'm referring to CSS Modules, but just using SASS files because I still love SASS nesting features github.com/css-modules/css-modules
It means you can import style files directly into the component file and classes will he hashed in some way to make them unique. That way you can have classes like .primary multiple times, but they won't clash because they are locally scoped by name.
Did the new developers read the BEM documentation? In my experience a lot of people find that BEM "looks ugly" at first sight and then don't even try to understand it. While it solves so many problems.
Same (sorta), I will forever be in love with SASS and BE (BEM without the modifiers).
I satisfy all 3 of of the mentioned pro's of CSS in JS with: SASS+BE, custom HTML elements, and CSS variables.
I approach everything as a component and use custom HTML elements with a corresponding sass file with mixins only used to tap into our global design systems like our grids/buttons/fonts.
To me using BEM's modifier classes are equally as ugly (and annoying .block__element--modifier??? blegh!) as using utility classes and I absolutely hate referencing them in my JavaScript files. I restrict the use of modifiers for side-effects of user interactions which are modified via JS so I use custom HTML attributes as modifiers instead and do not worry about it bleeding into other components since I am taking advantage of SASS's locally scoped nature. I also keep SASS Elements one level deep in terms of nesting. My SASS components are usually flat and I only nest when doing something like the lobotomized owl.
For getting colors or dimensions from JS into the stylesheet, i inject custom css variables into the DOM element's style attribute and reference it in the css:
Nesting media queries is one of the reasons I still love and use SASS. It allows me to immediately see all behavior for a single piece of styling. I don't have to go into 3-4 files (like in your case) in order to see how this component behaves on each resolution.
But to each their own, we all have our own preferences when it comes to some of this stuff. To me personally it's just easier to piece all of this stuff together when it's all in the same file, I can just immediately see "Oh this selector behaves like this on mobile, after 'md' and after 'lg' breakpoints".
With this pattern you don't know how a component behaves on different breakpoints. If the component is complex is even more so, since when you change one element you have to check 4 files to make sure that it works as you intended. I highly discourage this pattern, nested media queries seems the natural way of styling a component IMHO.
Indeed, it's much easier to work with media queries that are simply called whenever they needed on a small bit of styling; than splitting entire stylesheets based on those same media queries.
Yes, you might up with a few more media queries but at least it's very easy for each developer to see what's going on. Having to look into separate files for the styling of a single component, as well as remembering what's happening for every single viewport, is a tremendous pain and unscalable nor maintainable.
but I also don't mind the convenience of using include-media to specifically target mobile if I don't want to use the bottom up approach. it isn't hard at all to reason about and I use it sparingly, only when I know it's going to be mostly mobile only styles.
&__container{@includegrid-container;grid-template-columns:1fr;margin-top:16px;// mobile only@includemedia('<md'){padding:80px0120px;}@includemedia('>=md'){margin-top:24px;}}
Are you using custom elements to create your own Web components or using customized custom elementsi.e. class MyButton extends HTMLButtonElement and customElements.define() takes a third argument - {extends: 'button'}?
Do your custom elements work perfectly fine in Safari?
In your custom elements, how are you rendering the markup? Are you using innerHtml (vulnerable to injection attacks) for two-way binding, or a better option?
This is a unrelated question to your stack. I am using EmotionJS in my React component library, how can I migrate my EmotionJS styles to SASS styles?
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
To be honest, the whole "it's hard to tell what's used by what" in plain CSS is already solved with BEM.
BEM scales very nicely.
So by using new CSS variables with CSS modules to create locally scoped overrides you can still create runtime themed styles.
Yes your "frontend" Devs will need to learn CSS instead of just treating it like the Typescript autocomplete object faceroll experience that current cssinjs promotes.
Other unpopular opinions I hold:
Nested CSS rules made popular by lesscss are a maintenance nightmare. Don't use them.
Extends is also bad.
Mixins... Yep I hate them too.
"Nested CSS rules ... are a maintenance nightmare."
Use things they way they're intended. If css rules are only intended to work in, say, tables, then nest them inside of tables. Or, just certain classes of tables. If they are to be general, then don't nest them.
These three .hilite classes will not conflict with each other; the deeper ones will always override the shallow one at the bottom, but only inside .french or .italian tables. Hilite will be orange in french tables; green in italian tables, and blue everywhere else in your whole app. If you want .hilite to do something else outside of your zoot-suit panel, write it that way:
Any HTML outside of the .zoot-suit panel will have to make their own style rules; they can use .hilite freely without any orange, green or blue bleeding in.
Only issue I have with this is that then you don't know if
.hilite
is something global, is it a utility class, is it something specific to that table only when you encounter it in HTML/JSX. If using.table__cell--hilite
for example you know that it's a highlighted table cell and you can get all that data without having to look at the source code. And you know that it should be a table only thing here since it's kinda namespaced withtable__
Also, your example uses same class name for global selector and then selector that is more specific to 2-3 scenarios which now makes that selector super situation specific and it doesn't necessarily convey its meaning just by reading it. In my opinion it also raises CSS selector specificity unnecessarily.
But to each their own, I have my preferences and you have yours. Lot of this stuff is of course also situational and cannot really be applied to 100% of projects/situations.
Sass paved the way but it's time to ditch it.
Always good to see some contrary opinions - not everything that "shines" is made of gold :)
Mixins... I FKN love them. They are excellent at naming certain CSS "hacks": things that only make sense together, things that must act together otherwise the effect you want won't happen. Now you could write comments around it, but named mixins equate well-named, descriptive function names from clean coding.
if they could be strongly typed I might agree with you, but all they end up doing is hiding complexity.
Sorry, I don't get it. Do you have an example?
Good old BEM days with Knockout JS, not many React devs would know about BEM styling
BEM is really good i learnt from jonas sass course and it was really fun utilizing block, element and modifier classes
To me both HTML-in-JS and CSS-in-JS look ugly, because you can't separate presentation from logic, hence can't divide this work between different developers without permanent merge conflicts.
I love BEM and it's what I use when writing regular CSS for my own use.
The problem with BEM isn't BEM itself, it's getting your coworkers, new developers
and external agencies to understand and follow the syntax. We implemented BEM at a previous workplace and it worked perfectly until some core developers quit and new ones were hired, and then the styles just quickly deteriorated.
CSS modules and CSS-in-JS are locally scoped by default, and you have to specifically add syntax to make things global. As much as I dislike Tailwind syntax, that too has the advantage of keeping styling local to the place it's applied at least.
This is my experience as well. BEM can scale really well, but keeping it consistent is a problem when you have multiple people working in a codebase. I mean same can be said for any tool. Getting everyone on board is usually the hardest part.
People are also detracted from BEM because of it's syntax and because it "looks ugly" to them, but once you actually start using it, your CSS becomes a breeze to use and read because its "standardized" in some way. It also produces flat styling without horrible nesting which is a big plus in my book. Simpler flat styling allows easier per situation override if needed and you don't have to battle CSS selector nesting and specificity that much at all.
When using BEM, I've usually went with BEM + utility classes approach. Some more common things like spacing (padding, margin), colors, and maybe some flex/block properties would be mostly in utility classes and everything else would be BEM. It worked quite nicely for projects that I've been working on. It also allowed me to build UI faster because I wouldn't have to apply same spacing values all over again for each component, but could just apply a utility class name to some wrapper element and get the spacing that I want.
These days when working with React, the projects that I usually work on use SASS Modules and that also scales nicely. It's easier to get people on the same page because there is no specific naming convention like BEM and people don't have to worry about naming things because everything is locally scoped. This approach works nicely in most situations except few edge cases where styles need to be shared or something kinda needs to be global, then it's not so elegant anymore and in a single file. But other than that, it works really well and I don't have to leave comments on PRs like "hey the name of this class doesn't adhere to BEM principles, it should be this and that etc."
I still prefer SASS Modules compared to CSS-in-JS (emotion, styled-components) because you are still "mostly" writing just regular CSS that is closer to the native web.
I'm also working on a project that uses styled-components and even though I do like type safety we get with that because we use Typescript, it also makes every little styling its own component and to see any conditional logic for styling I always have to jump to source of a styled component to see what's going on. At least with SASS Modules and conditionally applying classNames I can immediately see what class is being applied for a certain condition and just by its name I can probably tell what it does without having to go into its source.
But that's just me and my personal experience.
When you refer to Sass Modules are you referring to this. How does that relieve you of having a specific naming convention like BEM? I use the new module system extensively but I still find the need for BEM. For e.g. I'm using the module system and BEM here. To me if in one module (say the button module) you have a class named
.primary
and in another module (say the link module) you also have a class named.primary
then those classes will class with each other once the Sass is compiled to CSS. Can you elaborate more on that?Sure. I'm referring to CSS Modules, but just using SASS files because I still love SASS nesting features github.com/css-modules/css-modules
It means you can import style files directly into the component file and classes will he hashed in some way to make them unique. That way you can have classes like
.primary
multiple times, but they won't clash because they are locally scoped by name.There's linting you can use to enforce this.
Did the new developers read the BEM documentation? In my experience a lot of people find that BEM "looks ugly" at first sight and then don't even try to understand it. While it solves so many problems.
Same (sorta), I will forever be in love with SASS and BE (BEM without the modifiers).
I satisfy all 3 of of the mentioned pro's of CSS in JS with: SASS+BE, custom HTML elements, and CSS variables.
I approach everything as a component and use custom HTML elements with a corresponding sass file with mixins only used to tap into our global design systems like our grids/buttons/fonts.
To me using BEM's modifier classes are equally as ugly (and annoying
.block__element--modifier???
blegh!) as using utility classes and I absolutely hate referencing them in my JavaScript files. I restrict the use of modifiers for side-effects of user interactions which are modified via JS so I use custom HTML attributes as modifiers instead and do not worry about it bleeding into other components since I am taking advantage of SASS's locally scoped nature. I also keep SASS Elements one level deep in terms of nesting. My SASS components are usually flat and I only nest when doing something like the lobotomized owl.For getting colors or dimensions from JS into the stylesheet, i inject custom css variables into the DOM element's style attribute and reference it in the css:
and in your SASS:
Works beautifully.
I don't care about things being ugly.
My main goal is scaling in large teams and maintenance long term, so I care more about understanding dependencies and avoiding the cascade.
Looks like you've also dived deep into the problem 👍️
i guess it's the designer in me, but, to me, if it ain't beautiful, it ain't worth looking at.
Another thing I would mention is that your nesting of media queries is also a thing i loathe.
the pattern I follow is :
thing/index.scss
This way, you're following a mobile first approach and then you don't end up with this :
It's a pretty simple example, but I've seen worse.
Nesting media queries is one of the reasons I still love and use SASS. It allows me to immediately see all behavior for a single piece of styling. I don't have to go into 3-4 files (like in your case) in order to see how this component behaves on each resolution.
But to each their own, we all have our own preferences when it comes to some of this stuff. To me personally it's just easier to piece all of this stuff together when it's all in the same file, I can just immediately see "Oh this selector behaves like this on mobile, after 'md' and after 'lg' breakpoints".
same! multiple sass files for a single component? seems like a step backwards to me, harder to see the whole picture.
With this pattern you don't know how a component behaves on different breakpoints. If the component is complex is even more so, since when you change one element you have to check 4 files to make sure that it works as you intended. I highly discourage this pattern, nested media queries seems the natural way of styling a component IMHO.
Indeed, it's much easier to work with media queries that are simply called whenever they needed on a small bit of styling; than splitting entire stylesheets based on those same media queries.
Yes, you might up with a few more media queries but at least it's very easy for each developer to see what's going on. Having to look into separate files for the styling of a single component, as well as remembering what's happening for every single viewport, is a tremendous pain and unscalable nor maintainable.
also, they implied that we aren't doing mobile first using this approach which we 100% are and it's very easy to see that we are.
if you are a stickler for order then you would start from the bottom up and declare the padding at the mobile level and later reset for wider devices
but I also don't mind the convenience of using
include-media
to specifically target mobile if I don't want to use the bottom up approach. it isn't hard at all to reason about and I use it sparingly, only when I know it's going to be mostly mobile only styles.Hello @eballeste ,
Few questions on your implementation -
Are you using custom elements to create your own Web components or using customized custom elementsi.e. class MyButton extends HTMLButtonElement and customElements.define() takes a third argument - {extends: 'button'}?
Do your custom elements work perfectly fine in Safari?
In your custom elements, how are you rendering the markup? Are you using innerHtml (vulnerable to injection attacks) for two-way binding, or a better option?
This is a unrelated question to your stack. I am using EmotionJS in my React component library, how can I migrate my EmotionJS styles to SASS styles?