
CSS Modules - geelen
http://glenmaddern.com/articles/css-modules
======
aleem
It's wonderful that CSS problems have been distilled so clearly, it's been a
long time coming. Radium[1] is really worth checking out, it's simple and
clear.

From the article:

    
    
      /* BEM */
      .normal { /* all styles for Normal */ }
      .button--disabled { /* overrides for Disabled */ }
      .button--error { /* overrides for Error */ }
      .button--in-progress { /* overrides for In Progress */
    
      /* CSS MODULES */
      .normal { /* all styles for Normal */ }
      .disabled { /* all styles for Disabled */ }
      .error { /* all styles for Error */ }
      .inProgress { /* all styles for In Progress */
    
      > In CSS Modules each class should have all the styles needed for that variant
    

This seems like trading one set of problems for another. It violates DRY and
consequently impacts code maintainability. The corollary is that .disabled
should @include .normal and then define the overrides so you only have to
define the base styles once in .normal... but that also requires discipline
and code wrangling.

    
    
      > [BEM] requires an awful lot of cognitive effort around naming discipline.
    

But the proposed alternative is not too different either:

    
    
      /* components/submit-button.css */
      .error { ... }
      .inProgress { ... }
    

This example just replaces BEM naming and namespacing with file naming and
directory structure.

[1]:
[https://github.com/FormidableLabs/radium](https://github.com/FormidableLabs/radium)

EDIT: On second reading, the stuff in there is definitely not "Welcome to the
future" exploring the "adjacent possible" realm--those are quite grandoise
pretexts to a solution that is just as unwieldy.

~~~
merrywhether
Regarding your first point, he addresses that in the next section:

"The composes keyword says that .normal includes all the styles from .common,
much like the @extends keyword in Sass. But while Sass rewrites your CSS
selectors to make that happen, CSS Modules changes which classes are exported
to JavaScript."

And isn't file (module) naming and directory structure what we use to organize
regular code? I like the idea of going to the components folder and looking
for the 'submit button' file when I need to alter something much more than
searching a 20,000 line css file or randomly split scss files. Even nicer is
the concept of including your styles in the same directory as the components
they define, further mirroring normal code organization. It seems like this
would allow for much easier onboarding into a project.

~~~
aleem
I am not saying it's a bad idea. However, it trades one set of problems for
another. What point is there in highlighting a problem and then proposing a
remedy which introduces the same problems in a different form?

> I like the idea of going to the components folder and looking for the
> 'submit button'

All existing frameworks use the same approach (Bootstrap, Foundation, et al).
GetMDL.io for example uses BEM and clean files to organize code
[https://github.com/google/material-design-
lite/tree/master/s...](https://github.com/google/material-design-
lite/tree/master/src)

Dealing with that on any reasonably sized project still requires "naming
discipline" and "cognitive effort", both of which the author highlights as
problems ailing BEM with a remedy that still suffer from the same. It didn't
read very convincingly.

------
Kequc
It feels to me that this conversation is quite old I'm finding myself not
convinced by the examples put forward in the article. It looks like a lot of
unnecessary complexity. You can have a lot of success by simply picking a
common css convention.

    
    
      .mybutton
        /* all styles for Normal */
      .mybutton.disabled
        /* overrides for Disabled */
      .mybutton.error
        /* overrides for Error */
      .mybutton.in-progress
        /* overrides for In Progress */
    

This is simpler and works incredibly well with javascript because I don't have
to find replace remove a subset. I just addClass('disabled')
removeClass('disabled').

~~~
prezjordan
Now what if you wanted to bring in another button - a very similar one - with
_slightly_ different styles?

~~~
Kequc
You extend the styles now you perhaps have:

    
    
      .mybutton
        /* all styles for Normal */
      .mybutton.skewed
        /* overrides for Skewed */
      .mybutton.disabled
        /* overrides for Disabled */
    
      class="mybutton skewed disabled"
    

The suggestion in the OP would be that each one of those classes contain all
of the styles for the button. That isn't necessary. Plus it requires, what I
count, a minimum of two additional language abstractions in order to do it.

~~~
talmand
Excellent answer. I love these "gotcha" examples when taking something that
should be relatively simple and trying to make it more complicated to prove,
um, something.

I wish people would realize that making their CSS more complicated and bloated
is not necessarily the answer.

~~~
NathanCH
I agree, a lot of these solutions come from bad CSS. Not because CSS is bad.

~~~
namuol
That so much CSS is "bad CSS" indicates that the problem isn't the craftsman
so much as the tool...

~~~
talmand
Ah, I see, so when I attempt to use a hammer as a wrench I can claim the
hammer is a horrible tool.

CSS does exactly what it was designed to do and does it quite well. It only
goes "bad" when people attempt to make it do things it wasn't designed to do.
Blaming the tool for that is the sign of a bad craftsman.

~~~
namuol
Exactly: CSS wasn't designed for the problems we're trying to solve. We're
trying to invent the proverbial wrench.

~~~
talmand
Then stop blaming the tool for bad craftsmanship. Maybe if we lay the blame
where it belongs then something constructive might appear.

------
ploxiln
So... CSS has the same global namespace issue as C, and this CSS Modules is
the same solution as C++ namespaces and classes. They call it "mangling".

[https://en.wikipedia.org/wiki/Name_mangling#Name_mangling_in...](https://en.wikipedia.org/wiki/Name_mangling#Name_mangling_in_C.2B.2B)

(further commentary prudently withheld)

~~~
saidajigumi
But just as with C++, that's effectively an implementation detail, and
irrelevant to the larger issues addressed. BEM itself is just name-mangling
structured for humans to execute manually. (wait, WAT.) We're talking about
the cognitive model and load that designers and developers have to deal with
when authoring styles.

I see CSS Modules (and Radium and ...) as being a space for experiments that I
hope will ultimately point the way to better "baked in" solutions. Similar to
how ideas from Coffeescript were "merged back upstream" into ES6/ES7.

------
mangeletti
I might be a majority shareholder of the following opinion, but I feel like it
needs to be said.

CSS was an art, and a science. Websites like csszengarden.com showed us the
promise of style sheets, and the galleries of "CSS sites" during the mid to
late 2000s demonstrated what amazing works could be taken from photoshop (and
Fireworks!) and made into beautiful, pixel-perfect layouts. I always felt
proud to make a website with only semantic HTML and CSS, and it always worked
well. Even with all the browser issues (namely those of IE6), it was easy to
write cross-browser compatible code that was clean and beautiful. The
(C)ascading part of CSS was still considered a feature, and deeply
understanding its implications was an acceptable challenge.

Then came The Age of Great Coupling & Frameworks (made that name up) wherein
CSS began to be regarded as a hindrance. Frameworks like Bootstrap became the
status quo, because it meant you could go from 0% to 60% in half the time.
But, no longer did anybody decide to go to 100%. The coupling I speak of isn't
just a coupling of backend to frontend technologies, but a coupling of backend
to frontend workflows. In a subconscious effort to become more efficient, we,
as software engineers, began to find ways to incorporate automatic solutions
for every problem (e.g., LESS). This is partly due to the very slow nature of
completing and proliferating new CSS specifications, but in the long run all
of these tools and frameworks have grown the barriers of entry, and have
bifurcated the community in many ways (e.g., LESS vs SASS, responsive
framework vs pure CSS, non-standard with polyfill vs standard, etc.).

These differences are not the kinds of differences that cause anger (usually),
but they make it hard to hire and train, and they make it hard for frontend
engineers to work together easily. At the end of the day we're all building
CSS and HTML with JavaScript, but if I strike up a conversation with 3
different frontend engineers, each will be as different as if I had chosen to
talk to an astronaut, a vet tech, and a professional swimmer. That's almost no
exaggeration. Diversity of skills and tasks within an occupation is a good
thing, but at some extreme it decreases mobility. When I felt like I had
learned CSS, well years ago, the thought never crossed my mind that someday
choosing a CSS-related tool would be as much a career decision as choosing a
primary programming language to focus on.

FTR, I don't have solutions for the problems I pointed out, nor do I think we
should somehow reset and go back to the past, but I feel strongly that we've
made things more complicated than they need to be.

~~~
crazygringo
I feel like you're looking at the past through rose-colored glasses, my
friend.

> _Websites like csszengarden.com showed us the promise of style sheets_

That site was always highly artificial -- huge deficiencies in CSS meant that
it always wound up being tightly coupled to the HTML structure for any non-
trivial site.

> _I always felt proud to make a website with only semantic HTML and CSS, and
> it always worked well. Even with all the browser issues (namely those of
> IE6), it was easy to write cross-browser compatible code that was clean and
> beautiful._

I don't think any language has ever frustrated me more than CSS. It _never_
worked well (float bugs? box models? browser hacks? z-index problems? vertical
centering? insane precedence rules?) and was anything but clean and beautiful.

The reason for the proliferation of tools like LESS or frameworks has been
exactly to address some of the huge warts and problems of CSS (like no
variables, no local namespacing, etc.) -- because these bring some
desperately-needed sanity back to the CSS codebase for a large site.

I agree that there are way too many choices and not enough standardization
around CSS best practices -- but the original problem here was the gigantic
deficiences in CSS in the first place. CSS was never "clean and beautiful", it
was and continues to be an eternal headache.

~~~
madeofpalk
> It never worked well

Don't confused 'it never worked well' with 'i never understood how it worked'.

'Good'[1] front-end/CSS developers have a very good understanding of float
quirks, the box model and how to properly vertically centre things.

Just because I don't understand how pointers in C work doesn't mean that C
doesn't work well.

But yes - CSS isn't ideal.

[1] Where 'good' is similar to 'stockholm syndrom'

~~~
ssalazar
> Don't confused 'it never worked well' with 'i never understood how it
> worked'. 'Good'[1] front-end/CSS developers have a very good understanding
> of float quirks, the box model and how to properly vertically centre things.

Great. How many "good" CSS developers are there, and what is their hourly
rate? If average developers still can't figure it out after its been around
for a decade (see [1]), then the language has failed. At the end of the day
CSS needs to usable for regular programmers, or its not very useful as a core
framework for the Web.

[1] [http://howtocenterincss.com/](http://howtocenterincss.com/)

------
andrewingram
I've been using CSS Modules in a couple of projects (combined with PostCSS),
am pretty happy so far.

I think you could actually go one step further and embed the stylesheet in the
React component's module using a tagged template string, I haven't looked into
how you'd get webpack to extract this into CSS files, but I'm sure it's
possible.

Eg:
[https://gist.github.com/AndrewIngram/e3af5e8b70fd89a9a0d3](https://gist.github.com/AndrewIngram/e3af5e8b70fd89a9a0d3)

~~~
andreypopp
That (extraction) can be done (React Style does that) but for it to be
consistent with JS semantics you need to evaluate code at build time because
such template strings can contain interpolations from JS world.

~~~
andrewingram
Ah yes, forget about the interpolation issue. I knew there was a reason I
stopped digging into it

------
spankalee
Shadow DOM completely solves this problem in a better way by scoping styles to
a shadow root. The scoping allows the browser to be smarter about style recalc
as well.

I do really like the idea of importing CSS into JS as a module to access the
classnames and IDs though. I would like to do a similar thing with HTML
imports as modules: import references to elements based on id.

~~~
ivanca
Unfortunately Shadow DOM inherits CSS properties such as color;font-family.

~~~
TeeWEE
Where do you read that? I just checked the spec and coundt find this exception
to the rule.

~~~
ivanca
I didn't, it is how it works in latest Chrome.

In all fairness, that's how a blank browser works by default (e.g. Times News
Roman is the inherited font-family if you don't specify one) so is very likely
the intended behavior.

------
fuzionmonkey
One thing worth mentioning about class composition with CSS Modules is it
allows for the convenient ergonomics of Sass's @extend without any of its
problems.

Sass @extend is actually counterproductive in terms of reducing filesize [1]
and results in unwieldy, unreadable CSS rules [2]. In contrast, CSS Modules
achieves true class reusability and optimal de-duplication of styles in a
manner impossible with Sass, resulting in a nice Chrome Inspector view because
the original classes are preserved and their composition is naturally
represented.

[1] [https://tech.bellycard.com/blog/sass-mixins-vs-extends-
the-d...](https://tech.bellycard.com/blog/sass-mixins-vs-extends-the-data/)

[2] [http://pressupinc.com/blog/2014/11/dont-overextend-
yourself-...](http://pressupinc.com/blog/2014/11/dont-overextend-yourself-in-
sass/)

------
egze
The advantage of BEM naming is that it takes 0 effort to find your style
definitions in code.

.Button--disabled copied in the browser, will find exactly 1 definition in the
project. .components_submit_button__normal__abc5436 will not find you
anything.

~~~
dantillberg
But on the flipside, in the latter case, you don't even need to grep for it --
"components_submit_button__normal__abc5436" reads as
"components_{filename}_{classname}...", so you know right away to go look in
"submit_button.(css|less|etc)" for ".normal {...".

~~~
egze
This is certainly a less convenient approach. You always need to parse the
class name in your head to open the correct file.

~~~
lojack
As a counter argument, when grepping files you still need to parse the output
in order to open the correct file.

And, yes I understand that you can have grep output a path name and pass that
directly in as an argument to your text editor. Similarly, it'd be relatively
trivial to write a bash function that'd parse the class name and find the
correct file to open.

Something like:

    
    
        function lookupcss() {
            find . | grep $(expr "$1" : '\(.*\)__.*' | sed 's/_/.*/g')".*\.css$";
        }

------
andreypopp
Also see styling[0] which is a loader for webpack which allows to generate CSS
modules with JS.

Files configured to use with this loader (usually `.style.js`) are executed at
build time and produce CSS modules. You can use any JS abstractions for that
(functions, modules, variables, ...) and any npm package available (color
manipulation, typography, ...):

    
    
      import styling from 'styling'
      import {smallText} from './typography'
      import {colors} from './theme'
    
      export let caption = styling({
        ...smallText,
        color: colors.text
      })
    

[0]:
[https://github.com/andreypopp/styling](https://github.com/andreypopp/styling)

~~~
ekosz
This is a very cool library. Thanks for the link. Have you used this in any
projects yet?

~~~
andreypopp
We started to migrate away from pure CSS Modules to Styling in our apps. Code
didn't hit production yet but we are close.

Writing styles in JS instead of CSS gave us a huge expressiveness boost and
allowed to use existing JS tooling such as eslint. At the same time all CSS
tooling still can be applied to compilation result (such as autoprefixer).

Also would be fun to write styles in TypeScript — statically typed styles
sounds great.

------
lo_fye
"The composes keyword says that .normal includes all the styles from .common"
\-- great, except you have the meaning of the word reversed. In the context
above it actually means ".normal is part of .common", which is clearly not the
intent. If you wanted it to mean that ".normal includes all the styles from
.common" then .normal should be "composedOf: common"

~~~
martin-adams
Correct. Would the word 'comprises' be more appropriate?

~~~
bobwaycott
I stumbled upon the Github issue and then found the HN reference. I commented
with some additional thoughts and keyword options. I don't think 'comprises'
would be the best choice here; it has quite a stricter meaning than 'includes'
and its synonyms.

~~~
martin-adams
What about 'inherits' or 'extends' to match known programming terms?

------
troels
Abstractions are fine and dandy until they break down and you have to pry them
open. What I don't understand here, is why the generated code doesn't follow
the already established BEM conventions which the posts explores initially.

~~~
pauly
I'm with you, that generated css doesn't look right, something that generated
the css BEM style makes more sense.

------
yari_ashi_zero
HTML is for hypertext documents, and CSS an organ of the same organism called
'HTML/CSS'. It's made for text documents, and applying it to the
implementation of GUI-centric applications has always been an ugly hack. For
awhile (90s to 00s AJAX) it was necessary, and now the whole professional
ecosystem has built up tremendous inertia over the millions of accumulated
people/hours of perfecting this 'competency'/hack. Now though, I would
recommend to switch over to building 2D webapps entirely out of SVGs (say with
React + Flux) e.g.:
[https://github.com/Terebinth/Vickers](https://github.com/Terebinth/Vickers)
or, even better, to building them entirely out of WebGL. SVGs are easy and
would lead to a wonderfully efficient workflow, but I think WebGL holds the
advantage for performance considerations, given the way it goes to the
hardware.

~~~
Davertron
Moving entirely to svg for user interface has some of its own issues (i.e.
there's no default layout engine in svg; everything is essentially absolutely
positioned...) but would be interesting to think about. Moving entirely to
WebGL would only be viable if you don't care at all about accessibility
though. For some subset of applications that might be acceptable (i.e. games),
but for something like gmail or other similar applications it's definitely
not.

~~~
marcosdumay
You just have to create widgets libraries, compile them to WebAsm or something
alike, and export an event-based framework for the developers to use. It can
even be in another language, compiled to WebAsm.

Or, alternatively, you can just use Flash, instead of cloning it.

------
aldanor
Hasn't this been already done in webpack (css-loader with local scope)?

[https://github.com/webpack/css-loader#local-
scope](https://github.com/webpack/css-loader#local-scope)

~~~
andreypopp
Webpack's css-loader implements CSS modules specificaton, see
[https://github.com/webpack/css-loader#css-
modules](https://github.com/webpack/css-loader#css-modules)

------
chinchang
This seems like too much abstraction (magic) of things. Plus those auto
generated classes really look odd and would make debugging difficult.

~~~
mradmin
I assume soucemaps would solve this.

~~~
sic1
It's lovely to assume this, but I see no mention of sourcemaps anywhere. This
was my immediate gripe, debugging.

Yea, the class name should point you to the file you want to look at, but that
is still manual legwork. And the whole atomic css-like pitch he has with the
over-the-top `composes` functionality looks like its own nightmare.

I'll watch from over here and see if this pans out for ya'll.

~~~
mradmin
I can't speak from experience with regards to this particular approach, but
I've worked with css-modules with webpack (css-loader), and debugging the
class names is no problem at all, CSS source-maps point to the source, you can
view the source code within your developer tools. It shouldn't really be any
different than debugging Sass generated CSS, for example.

------
TeeWEE
Doesnt the shadow-dom solve the problems with css modularity. You just write
the css for your component, and it doesnt leak into anything else... Why do
you need something like css-modules?

~~~
gravity13
This is actually perfect for shadow dom, because it allows composition.

You gotta remember, Shadow Dom is a double edged sword, - while it prevents
styles from leaking in, it prevents styles from cascading too. And sometimes,
cascading was a good thing. Without it, you need to reapply base styles, and
that's annoying. With this approach, you can compose component styling by
declaring what to inherit.

------
tfb
I've been using JSS for everything. It makes the most sense to me. It's
minimal in size and function to the point that any extra functionality can be
quickly and easily added via `jss.use(plugin)`. And it's maximally modular.
Every class is namespaced, and exporting everything to a single sheet for
production builds is super simple, as it should be.

[https://github.com/jsstyles/jss](https://github.com/jsstyles/jss)

------
IlPeach
Aren't methodologies like SMACSS addressing such problems?

I found interesting how the whole thing is specifically related to someone
reading the CSS and not used to SASS.

~~~
IlPeach
Forget the last sentence, was referring to another link.

------
dfunckt
Right, but what about the name? This isn't Cascading Style Sheets any more.
Hmm let's see, Composable Style Sheets. There. Oh, wait...

------
AlexeyMK
I was first introduce to the 'CSS as code' concept by Blake at
[https://github.com/blakeembrey/react-free-
style](https://github.com/blakeembrey/react-free-style) \- worth taking a look
if you're considering using one of these tools.

------
pearjuice
Am I the only one who scrolled up and down a few times just to verify the
background was changing its color and it wasn't a bug in f.lux which kicked in
way too early for sunset?

------
danieltillett
How does a post get to number 1 here with only 5 up votes?

~~~
josephmx
There's extra weight to comments and how fast the upvotes are. If all votes
are instant it'll go higher than votes over a longer period.

~~~
danieltillett
This is not great. I know there are controls in place to stop chain voting,
but if it only takes 5 votes in 5 minutes to reach number 1 then the system is
wide open to abuse.

~~~
rgbrgb
Most systems are vulnerable if you poke around a bit. In my experience the HN
voting ring detector is pretty wily.

~~~
danieltillett
That I agree with :)

The problem is that no system can detect signal in five votes. The numbers are
just too small to be significant.

------
sepokroce
Surely not the future.

~~~
rdsubhas
Not sure why you're getting downvoted. Problems with CSS at Scale are very
real (as mentioned in the slide). But this is a super complicated solution.
Its like one of the head-over-heels approaches in JoelOnSoftware blogs. Surely
there has to be a simpler way.

Its good that the authors are trying. But just look at the picture on this
article captioned "This is how intensely we’ve been thinking about CSS".
That's where the whole complexity comes from I guess. Good solutions present
themselves and fit naturally.

~~~
kaoD
> Not sure why you're getting downvoted.

Because it doesn't contribute anything but nonconstructive criticism.

> Good solutions present themselves and fit naturally.

Complex problems require complex solutions.

Also, what's so complex and non-fitting about this solution? Explaining that
(and your alternative solution) would be much better than simply criticizing
as you and GP did.

> Surely there has to be a simpler way.

There's a technically simpler way: modules being part of the CSS spec. But
they're not, so even if technically simpler as a solution, it's a political
nightmare on which we developers have no control whatsoever.

Driving forward the status quo with hacks (like to-JS transpilers did) is
nothing but beneficial. Hacks drive standards.

> "This is how intensely we’ve been thinking about CSS". That's where the
> whole complexity comes from I guess.

How much have you been been thinking about CSS and fighting its flaws?
Obviously not a lot.

~~~
sepokroce
> Because it doesn't contribute anything but nonconstructive criticism.

No, it's just an opinion and a comment on the sensationalistic article
(sub)title.

