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
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.
"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.
> 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...
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.
If I'm using .btn-disabled everywhere, then its hard to be to write a CSS rule that says 'whenever a button follows another button, apply this style to it'. That's why we have this competition with BEM and `.btn .disabled`.
> This example just replaces BEM naming and namespacing with file naming and directory structure.
...Yes. That's the point.
The problem is that CSS is a global 'language', and everything that you write has the chance to impact all your other CSS. When I'm writing a particular component, there's no way to write CSS that's local to just that component.
We have things like BEM that is about establishing a _convention_ to write Pseudo-Local-CSS, but nothing is enforced. Everything is still global. 'CSS Modules' enforces your CSS to be 'local only' as a file-level, just like regular (Node) JS.
It's like how there's a convention of not adding a string to an integer, but adding a type system to your programming language enforces that on a technical, contractual level.
I really don't want a lack of hover or focus effects on initial page load, and I really don't want my entire page layout to change once the JavaScript-powered media queries kick in.
/* components/submit-button.jsx */
import { Component } from 'react';
import styles from './submit-button.css';
export default class SubmitButton extends Component {
render() {
let className, text = "Submit"
if (this.props.store.submissionInProgress) {
className = styles.inProgress
text = "Processing..."
} else if (this.props.store.errorOccurred) {
className = styles.error
} else if (!this.props.form.valid) {
className = styles.disabled
} else {
className = styles.normal
}
return <button className={className}>{text}</button>
}
}
That looks unwieldy -- 4 if/else clauses just to deal with CSS. Contrast that with the example in "Usage" over at the Radium page https://github.com/FormidableLabs/radium -- much simpler.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.
> 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.
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'
The variables argument is the only one I can truly get on board with. I really wanted variables. Find + replace in your editor for something like #fafaf0 isn't exactly error-prone or hard to do, so it wasn't ever a huge problem, but variables would have been very nice.
Pertaining namespaces, that's what the cascading part was made for. I had 4000+ line CSS files for large ecommerce websites where I had 5 different declarations of some classes (e.g., .item), but using proper selectors and understanding specificity meant never having a problem.
Pertaining large files, @import(...) always worked for me. For the most part, I'd use one large file (hence 4000+ line files mentioned above) because it helped with load speed, which did suggest that there was value in a build tool that would read @import(...) statements and pull those files into one large file after the fact. But this didn't mean we needed a framework with its own syntax, etc.... even today, tools like django-pipeline will pull in and combine CSS files for you, in the order given, so that you can write your files separately and have them combined after.
Pertaining z-index, I never had an issue with this, except with drop-down menus, but even then it was intuitive and consistent that the next element down will be 1 layer above the one directly above it, and it was also intuitive that nested elements' implicit z-indexes would be relative to their parents, etc.
You're right that there's a bewildering number of technologies and approaches out there, but I think we're actually converging towards something sane. Certain waypoints are basically being agreed upon. Sure, we have SASS/LESS/Stylus/PostCSS, but ultimately people use them 95% identically, and that commonality was a huge win. BEM has similarly been a huge win, wand widely adopted. Reading the OP, I was nodding my head along as I read, and I think they're on to something really good. We're getting close to a point where we'll have easy to write, easy to employ, semantically meaningful styles with a more or less transparent build process.
I'm still in the business of full-custom HTML+CSS+JS craftsmanship.
And that's all it was - promise. There is a reason why CSS Zen Garden was just an example using a single page as a template.
In reality, people now make webapps of incredible size. A lot of what CSS Modules seems to be trying to fix is exactly that - how to maintain a lot of styles without namespace clashing, one giant CSS file, etc. etc.
I spent a lot of time dealing with all that, so I don't look back at the past in a positive way. I'm not saying CSS Modules is the answer, but it's interesting at least.
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.
The shadow dom solves the css modularity problem. And also comes with better "componetization of the web". React is imho not the end solution, and the virtual-dom and the diffing of nodes should actually be built into browsers, and not into a javascript framework...
And while HTML was initially created for hypertext documents, it doesn't mean it can't be used for somthing else. I disagree that it's an ugly hack. Some parts may not be suitable for GUI apps (uhm, like <blink>), but then simply don't use them in your application. Or implement something better on top of the good parts (like React did). Eventually the good parts may be come standard.
Or, alternatively, you can just use Flash, instead of cloning it.
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
.Button--disabled copied in the browser, will find exactly 1 definition in the project. .components_submit_button__normal__abc5436 will not find you anything.
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/stylingWriting 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.
loaders: [
...
{
test: /\.css$/,
loader: 'css?localIdentName=[name]__[local]___[hash:base64:5]'
}
]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.
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.
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.
.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'). .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.https://en.wikipedia.org/wiki/Name_mangling#Name_mangling_in...
(further commentary prudently withheld)
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.
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.
I found interesting how the whole thing is specifically related to someone reading the CSS and not used to SASS.
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...
[2] http://pressupinc.com/blog/2014/11/dont-overextend-yourself-...