CSS was invented to style web pages. A typical web page contains many elements or components such as menus, buttons, input boxes, and so on. Styles defined in a CSS file are accessible to the entire web page in which that file is included. In other words, all style definitions have global scope. What if we want some styles to be visible only to one component of the page?
CSS was defined to style documents, not UI components. The lack of modularity in CSS language makes it hard to maintain complex or legacy code. Developers are afraid to make code changes since it's easy to break something when CSS definitions are global.
CSS Modules solves these problems by limiting the scope to components. CSS Modules is not an official standard. It's a community-led effort popularized by the ReactJS ecosystem.
What are the problems that CSS Modules solve?
The problem of global scope is solved by CSS Modules since class names are local to the component. The same class name can be used in another component with different styling. Even though CSS as a standard has only global scope for names, CSS Modules leverages on tooling. Tools convert local names to globally unique names.
Deeply nested CSS selectors result in what we call high specificity. This impacts performance. With CSS Modules, names are globally unique and locally specific. Hence, flat selectors are more than sufficient. Because CSS styles are now encapsulated within components, code becomes more maintainable. Code can be refactored. Dead code can be removed.
What are the essential features of CSS Modules?
- Local Scope: Class names and animation names are locally scoped by default.
- Global Scope: Using
:globalswitch, developer can choose to reuse some styles globally.
- Composition: A selector can extend styles from another, thus promoting reuse of styles within local scope. Composition can't be used for global scope. A selector can be composed from a selector in global scope or selectors in other files.
- Integration: CSS Modules should have tooling support to compile CSS to a low-level format called Interoperable CSS (ICSS). It should also work nicely with CSS processors (Less, Sass, PostCSS), bundlers (Webpack) and JS frameworks (Angular, React).
Could you explain how CSS Modules work?
CSS as a language doesn't support local scopes. All names have global scope. To overcome this limitation, developers use tools to automatically transform local names to globally unique names. These names are generated by the CSS Modules compiler. The compiler also generates a JS object to map old names to new names. Therefore, developers can continue to use local names in their component code.
For example, let's take a UI component called "Cat". Its styles are in file "Cat.css" where
.meowis defined. It's possible that the same class is used in another component, let's say, "WildCat". To avoid this name conflict, CSS Modules compiler makes each name unique.
CSS Modules is flexible. It doesn't impose a specific naming convention. In our example, the new class name is
.cat_meow_j3xk, where the module name is used as a prefix and a hash value is used as a suffix. The mapping from old to new names goes into a JS object.
What tools and plugins help with implementing CSS Modules?
Webpack's css-modules or PostCSS's postcss-modules can be used to implement CSS Modules. PostCSS can be used along with task runners such as Gulp or Grunt. PostCSS is really using JS plugins to transform CSS.
CSS Modules can be used within JS frameworks. For React, there's react-css-modules or babel-plugin-react-css-modules. CSS preprocessors such as SCSS can be used along with CSS Modules using sass-loader. Gatsby is another example where CSS Modules can be used.
To catch problems early, there's eslint-plugin-css-modules.
Are there alternatives to using CSS Modules?
Historically, CSS started as a single file for an entire app. Thanks to tooling such as Webpack's css-loader, it became possible to use one stylesheet file per component. Each component's folder contained its CSS and JS files. However, styles still had global scope.
Global scope was partially solved by OOCSS, SMACSS, BEM, etc. Block-Element-Modifier (BEM) brought some modularity to CSS via a naming convention. It solved CSS specificity problem. However, class names were long and had to be named by developers. With CSS Modules, naming is taken care of by the build tools. During coding, developers use simpler and intuitive names. We can customize class names, including the use of BEM-style naming if desired.
CSS in JS embeds CSS directly in a JS file. The CSS model is at component level rather than at document level. For example, in JSS, styling is defined as JS objects. In another library called styled-components, tagged template literals are used. Michele Bertoli has a curated comparison list of many CSS-in-JS techniques.
Isn't W3C standardizing CSS Modules?
No. CSS Modules is not a standard. It doesn't change the CSS specifications in any way. CSS definitions retain global scope but CSS Modules makes them "behave locally" by renaming them with unique names, which are then referenced by their components in HTML or JS. This is possible because of tooling support.
Beginners may confuse CSS Modules with another concept called CSS3 modules. In June 2011, CSS2.1 became a W3C Recommendation. However, this process took 13 long years, mainly because the specification was a monolith. When work started on CSS3, it was therefore decided to split the specifications into separate modules. Each module can take its own path of standardization. These modules are not related to CSS Modules.
A related concept is the use of namespaces in CSS. These refer to XML namespaces and how these can be used within CSS selectors. HTML
styleelement has the
scopedattribute for local scoping but this is now deprecated.
What are some criticisms of CSS Modules?
It's been said that CSS Modules does nothing more than automate class naming so as to avoid name collisions. Developers can still do wrong things without CSS Modules warning them about it. It's not clear if typo errors will be caught at compile time or runtime. You can't share constants across CSS and JS files. TypeStyle is one approach that solves some of these issues. It mimics CSS Modules pattern using JS objects.
The use of camelCase that's not typical of CSS naming convention is seen as a limitation. While some frameworks may have plugins to solve some of these issues (such as babel-plugin-react-css-modules) it may prove difficult to make CSS Modules work with others.
Christopher Chedeau, a frontend developer at Facebook, lists a number of problems with CSS at scale. He states that while JS recommends avoiding global variables, CSS still uses global names. Facebook initially solves many of the issues by extending CSS and later by using inline styles. Styles are specified as JS objects within UI components. Mixing content and styling might seem like a bad approach but every component encapsulates its own styling without relying on global variables. The common name for this approach is CSS in JS.
Webpack's css-loader starts supporting local scope with the use of
:local switch. A month later some folks implement a module for PostCSS called postcss-local-scope, so that CSS styles are local by default even without using the switch. Only globals need to be explicitly specified with the
The low-level file format that enables CSS Modules is specified separately as Interoperable CSS (ICSS). This becomes the common format for module systems. The specifications is meant for loader implementers, not end users. Meanwhile, ICSS is implemented in loaders including css-loader (webpack), browersify, and jspm.
- Ashwini, Amit. 2017. "What Is The Difference Between React.js and React Native?" Cognitive Clouds, September 09. Accessed 2019-02-25.
- Basarat. 2016. "CSS Modules are not the solution." Medium, April 21. Accessed 2019-02-24.
- BoltClock. 2011. "Is CSS3 an official standard?" StackOverflow, December 26. Updated 2014-05-04. Accessed 2019-02-24.
- Chedeau, Christopher. 2014. "React: CSS in JS." Speaker Deck, November 04. Accessed 2019-02-24.
- Cornilliac, Tom. 2016. "CSS Modules — Solving the challenges of CSS at scale." Medium, January 05. Accessed 2019-02-24.
- Dalgleish, Mark. 2015. "The case for CSS modules." ReactiveConf, on YouTube, November 16. Accessed 2019-02-24.
- Fenn, Katie. 2016. "Writing Modular Stylesheets with CSS Modules." Scotland CSS, June 01, on ScotlandJS YouTube, uploaded September 12. Accessed 2019-02-24.
- GatsbyJS. 2019. "Introduction to Styling in Gatsby." Tutorial, GatsbyJS, February. Accessed 2019-02-24.
- Goh, Max. 2018. "How I integrated CSS Modules with SCSS into my React application." freeCodeCamp, September 23. Accessed 2019-02-24.
- Heasley, Paul. 2018. "Using CSS Modules with Angular, TypeScript and Bootstrap." PHDesign, February 21. Accessed 2019-02-24.
- Irvine, Dave. 2018. "PostCSS with CSS Modules and React." Dev.to, June 14. Updated 2018-10-02. Accessed 2019-02-24.
- MDN Web Docs. 2018a. "@namespace." MDN Web Docs, Mozilla, October 29. Accessed 2019-02-25.
- MDN Web Docs. 2018b. "<style>: The Style Information element." MDN Web Docs, Mozilla, October 22. Accessed 2019-02-25.
- Madyankin, Alexander. 2016. "PostCSS-modules: Isolate ’em all!" Martian Chronicles, March 03. Accessed 2019-02-24.
- Masset, Philippe. 2016. "Modular CSS with React." Medium, April 06. Accessed 2019-02-24.
- Rahangdale, Trapti. 2018. "Handling CSS at scale." Medium, February 27. Accessed 2019-02-24.
- Rendle, Robin. 2016. "What are CSS Modules and why do we need them?" CSS-Tricks, April 04. Updated 2018-03-13. Accessed 2019-02-24.
- Rootfield, Louie. 2017. "Solve Your Specificity Headaches With CSS Modules." Envato Tuts+, August 29. Accessed 2019-02-24.
- Roselli, Adrian. 2011. "CSS 2.1 is Finally Final." June 07. Accessed 2019-02-25.
- Saring, Jonathan. 2018. "9 CSS in JS Libraries you should Know in 2019." Bits and Pieces, on Medium, July 10. Accessed 2019-02-25.
- Suttle, Kevin. 2016. "CSS Modules: A review from the front-end." June 03. Accessed 2019-02-24.
- Tiwari, Bharat. 2017. "Webpack Loaders, CSS and Style Loaders." Medium, May 11. Accessed 2019-02-24.
- css-loader. 2018. "css-loader." v2.1.0, on NPM, December 26. Accessed 2019-02-25.
- css-modules GitHub. 2017a. "css-modules/css-modules." October 28. Accessed 2019-02-24.
- css-modules GitHub. 2017b. "css-modules/icss." June 01. Accessed 2019-02-24.
- Maddern, Glen. 2015. "CSS Modules: Welcome to the Future." August 19. Accessed 2019-02-24.
- Ma, Theresa. 2018. "CSS in the Age of React: How We Traded the Cascade for Consistency." Engineering Blog, Yelp, March 05. Accessed 2019-02-24.
- Tatu. 2017. "Practical Guide to React and CSS Modules." TripleT Blog, January 22. Accessed 2019-02-24.
- Suraj KC. 2017. "CSS Modules with React: The Complete Guide." YoungInnovations' Blog, on Medium, August 01. Accessed 2019-02-24.