0
(0)

Say again? There is a way to write plain CSS in a .css file but instead of using on every page, we import it only in a component and it will affect only that component ? No way !

Yes indeed ! CSS modules is like a “Pimp up my ride” show for your CSS. This is how you import normally your CSS in a sunshiny day:

import './styles/header.css';

To call the “Pimp my CSS” team you just add a name to your import:

import styles from './styles/header.css'

Magic happens, your CSS is mapped and every CSS rule becomes something more than a plain CSS, something that we call ICSS or Interoperable CSS which is a low-level interchange format.

Why was there a need for CSS modules?

First of all as you probably already know, CSS is:

  • global scoped, meaning it is not localized for a section or component or element. If you write div.box { color: red } then any div tag with a box class name will have it’s text in red color and then if you have a .box { color: green } it won’t work because the former selector is more specific. After this a specificity war breaks out and there you go, your inner peace is gone !
  • your CSS does not know :extend(compositions), theming and using props

Does CSS modules fill in the blanks of CSS?

Well, it definitely has some nice additions that you will value.

It will make your imported CSS locally scoped, by generating a hash to your class name that is globally unique on your whole website. The initial CSS selector will become a Javascript property and this will be accessible just like an object’s property:

First let’s take this example in a .css file:

.header {
   position: fixed;
   top: 30px;
}
import styles from './styles/header.css';

<div className={styles.header}></div>

The styles will become the object and the CSS selectors from header.css will become it’s properties. The classes inside your css don’t have to be specific anymore, like .article-header or .main-header because they have those unique hashes that will apply only on that tag. This way you can write short class names in your CSS and without worrying about not being to specific in naming classes. You can also break your CSS files up in multiple modular CSS files, like: header.css, footer.css, navbar.css and so on.

What if you have a dash separated class name, will that still work?

You can keep the format in CSS modules but you need to reference it in the following format:

<div className={styles['nav-bar']}></div>

It is recommended though that you use the camel case format:

<div className={styles.navBar}></div> 

How about having multiple classes on same tag?

Multiple classes can be written with template literals or with a join method:

<div className={`${styles.navBar} ${styles.nav}`></div>
<div className={[styles.navBar, styles.nav].join(' ')}></div>

Now that we discussed about how you can actually call the CSS rules in your React component, let’s see what you can do in your CSS file that you were not able before.

What new CSS methods did we inherit from CSS modules?

Compositions

You can extend a class with another class from your .css file or from another css file:

.bordered {
   border: 1px solid;
}
.dark {
   background-color: #000;
   color: #fff;
}
.button {
   composes: bordered dark;
   font-size: 12px;
}

The origin of the inherited class can be from an external file:

.button {
   composes: link from "./styles/links.css";
}

Of course there is no limitation to only one value, you can put as many as you like, in a granular way:

.button {
   composes:  link hovered focused from "./styles/links.css";
}

Also you can have have multiple composes rules in one CSS rule:

.button {
    composes: link from "./styles/links.css";
    composes: bordered;
    composes: opacity-transition ease-in-ease-out from "./styles.transitions.css";
    font-size: 12px;
}

Variables

Variables in CSS modules are called with the @value directive.

First we define them somewhere(colors.module.css):

@value pure-red: #f00;
@value pure-green: #0f0;

You can notice that the naming of the files is with name.module.css format. The @value directive introduces the variable name and value separated by a colon.

When we decide to use it in our buttons.module.css, we do it like this:

@value pure-red, pure-green from "./styles/colors.module.css";

.button {
   color: pure-green;
   border: 1px solid pure-red;
}

Theming

There is another way to load CSS modules and that is through props:

const Button = ({theme}) => {
  <button className={theme.button}>Submit</button>
}

The Button is called like this:

import light from "./styles/themes/light.css";
import {Button} from "button/button.jsx";

const App = () => {
  <Button theme={light} />
}

Enough of local scoping, how about styling some elements globally?

Well, for that there is a special command:

:global(.ui-list) {
   list-style: none;
}
:global(p) {
   padding-bottom: 10px;
}
:global(body) {
   font: 16px Arial, Verdana, Sans-serif;
}

Grouping is also possible with the global block:

:global {
   body {
      font: 16px Arial, Verdana, Sans-serif;
   }
   .ui-list {
      list-style: none;
   }
   p {
      padding-bottom: 10px;
   }
}

:global can be used also in grouping just like in css if the value is the same:

:global button, :global a {
   font-size: 12px;
}

It can be nested or combined:

:global(.header) .nav-list {
   display: flex;
}
.header :global(.nav-list) .nav-item {
   display: flex;
}

When you nest a local class name in a global class name you need to escape that with the :local method:

:global {
   .header {
      display: flex;
   }
   :local(.nav-list) {
     display: flex;
   }
}

Alright, but why not use some naming conventions like Bem?

BEM(Block-Element-Modifier) is a pretty nice idea of keeeping your CSS scoping in control by using contextual class names.

The format of the class names are : Block__Element–Modifier

Block = is an element block like navigation bar, card, article, section

Element = is always a child of a block like an image or a paragraph

Modifier = this is usually related to the appearance, state or behavior of a block or an element

<div class="card card--open card--blue">
   <img class="card__image" src="ball.png" />
   <button class="card__button card__button--primary">
</div>
  

As you can see BEM offers a pretty expressive class naming convention where just by looking at the HTML you can understand that this is a card module that is blue and is opened. Also it has inside a card image and a card button that has the primary colors and font styles. This way you keep your style compartmentalized and easy to understand the whereabouts and identity. The reason I still use CSS modules is that the class naming is automatic, so I guess it’s more fun to be lazy and write less code, less effort more smile.

Conclusions

CSS is a global scoped style language and because of it’s global nature it provokes certain unpleasant issues like specificity issues with same selectors in different part of the document. Also there can be a lot of unused CSS rules that are loaded on every page. Well, CSS modules takes care of that by creating class names spiced with unique hashes that will turn global scope into local scope for your style rules and also trashes unused CSS. But in case you want to use mixed, half global and half locally scoped classes then you have the :global and :local methods. Compositions and variables are also included.

How useful was this post?

Click on a star to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.