CSS selec­tors all exist with­in the same glob­al scope. Any­one who has worked with CSS long enough has had to come to terms with its aggres­sive­ly glob­al nature — a mod­el clear­ly designed in the age of doc­u­ments, now strug­gling to offer a sane work­ing envi­ron­ment for today’s mod­ern web appli­ca­tions. Every selec­tor has the poten­tial to have unin­tend­ed side effects by tar­get­ing unwant­ed ele­ments or clash­ing with oth­er selec­tors. More sur­pris­ing­ly, our selec­tors may even lose out in the glob­al speci­fici­ty war, ulti­mate­ly hav­ing lit­tle or no effect on the page at all.

Any time we make a change to a CSS file, we need to care­ful­ly con­sid­er the glob­al envi­ron­ment in which our styles will sit. No oth­er front end tech­nol­o­gy requires so much dis­ci­pline just to keep the code at a min­i­mum lev­el of main­tain­abil­i­ty. But it doesn’t have to be this way. It’s time to leave the era of glob­al style sheets behind.

It’s time for local CSS.

In oth­er lan­guages, it’s accept­ed that mod­i­fy­ing the glob­al envi­ron­ment is some­thing to be done rarely, if ever.

In the JavaScript com­mu­ni­ty, thanks to tools like Browser­i­fy, Web­pack and JSPM, it’s now expect­ed that our code will con­sist of small mod­ules, each encap­su­lat­ing their explic­it depen­den­cies, export­ing a min­i­mal API.

Yet, some­how, CSS still seems to be get­ting a free pass.

Many of us — myself includ­ed, until recent­ly — have been work­ing with CSS so long that we don’t see the lack of local scope as a prob­lem that we can solve with­out sig­nif­i­cant help from brows­er ven­dors. Even then, we’d still need to wait for the major­i­ty of our users to be using a brows­er with prop­er Shad­ow DOM sup­port.

We’ve worked around the issues of glob­al scope with a series of nam­ing con­ven­tions like OOCSS, SMACSS, BEM and SUIT, each pro­vid­ing a way for us to avoid nam­ing col­li­sions and emu­late sane scop­ing rules.

We no longer need to add lengthy pre­fix­es to all of our selec­tors to sim­u­late scop­ing. More com­po­nents could define their own foo and bar iden­ti­fiers which — unlike the tra­di­tion­al glob­al selec­tor model—wouldn’t pro­duce any nam­ing col­li­sions.

import styles from './MyComponent.css';
import React, { Component } from 'react';
export default class MyComponent extends Component {
 render() {
    return (
      <div>
        <div className={styles.foo}>Foo</div>
        <div className={styles.bar}>Bar</div>
      </div>
    );
  }

The ben­e­fits of glob­al CSS — style re-use between com­po­nents via util­i­ty class­es, etc. — are still achiev­able with this mod­el. The key dif­fer­ence is that, just like when we work in oth­er tech­nolo­gies, we need to explic­it­ly import the class­es that we depend on. Our code can’t make many, if any, assump­tions about the glob­al envi­ron­ment.

Writ­ing main­tain­able CSS is now encour­aged, not by care­ful adher­ence to a nam­ing con­ven­tion, but by style encap­su­la­tion dur­ing devel­op­ment.

Once you’ve tried work­ing with local CSS, there’s real­ly no going back. Expe­ri­enc­ing true local scope in our style sheets — in a way that works across all browsers— is not some­thing to be eas­i­ly ignored.

Intro­duc­ing local scope has had a sig­nif­i­cant rip­ple effect on how we approach our CSS. Nam­ing con­ven­tions, pat­terns of re-use, and the poten­tial extrac­tion of styles into sep­a­rate pack­ages are all direct­ly affect­ed by this shift, and we’re only at the begin­ning of this new era of local CSS.

process.env.NODE_ENV === 'development' ?
    '[name]__[local]___[hash:base64:5]' :
    '[hash:base64:5]'
)

Under­stand­ing the ram­i­fi­ca­tions of this shift is some­thing that we’re still work­ing through. With your valu­able input and exper­i­men­ta­tion, I’m hop­ing that this is a con­ver­sa­tion we can have togeth­er as a larg­er com­mu­ni­ty.

Note: Auto­mat­i­cal­ly opti­mis­ing style re-use between com­po­nents would be an amaz­ing step for­ward, but it def­i­nite­ly requires help from peo­ple a lot smarter than me.