Learning to Use CSS Modules in Gatsby

Note: This note is part of learning Gatsby series and still in development. This note is not complete and will be updated frequently.

In ReactJs and GatsbyJS, styling components can be achieved in many different ways, including CSS Module. In Gatsby site, CSS Module works out of the box. One of the major advantages of using CSS module is it “allows to write traditional, portable CSS with minimal side-effects” because its component-scoped property.

What is CSS Module?

CSS Modules are defined as “A CSS Module is a CSS file in which all class names and animation names are scoped locally by default”. The Gatsby Doc further describes CSS Model:

“CSS Modules let you write styles in CSS files but consume them as JavaScript objects for additional processing and safety. CSS Modules are very popular because they automatically make class and animation names unique so you don’t have to worry about selector name collisions.”

Why to Use CSS Module?

CSS module have modular local scope and they are reusable. The main advantages of using CSS module are (i) no more conflict, (ii) no global scope (but local) and (iii) explicit dependencies.

Use Case Examples

Lets take a few more use case examples which are inspired by the this tutorial examples with some minor modifications:

A Simple Use Case

Lets take a simple class and nested class example for the illustration purposes. In the example, color of the page_title is defined as red, sub_title as blue and sub_title.active (nested) as green.

/* file: about.module.css */
.page_title {
  color: red;
  }
.sub_title {
  color: blue;
  font-size: 1.5rem;
  margin: 2rem 0;
  }

.sub_title.active {
  color: green;
}

Lets use the above CSS rules in a Gatsby page component about.js (shown below). First CSS module is imported (line 6) and then referenced the className as {style.page_title} as shown in line 12. The style in line 12 is arbitrary variable name, a JavaScript object.

//src/pages/about.js
import React from "react"
import { Link } from "gatsby"

import Layout from "../components/layout"
import style from "../styles/about.module.css"
import SEO from "../components/seo"

const About = () => (
  <Layout>
    <SEO title="Page two" />
    <h1 className={style.page_title}>About Page</h1>
    <p>This pages uses CSS module for styling this about.js page component</p>

    <div className={style.sub_title}> I am Sub Title </div>
    <div className={`${style.sub_title} ${style.active}`}>I am Active Sub Title </div>
    <Link to="/">Go back to the homepage</Link>
  </Layout>
)
export default About

If we view the page in a browser, it should look like as shown below. As expected, the page_title rule is applied in line 12, sub_title in line 15, and sub_title.active (nested) rule in line 16.

Screenshot showing a simple use case of CSS module in Gatsby page component.

The following CSS code is after browser compiles CSS modules.

/* after compilation */
.about-module--page_title--3GwIL {
    color: red;
}
.about-module--sub_title--c2rzJ {
    color: blue;
    font-size: 1.5rem;
    margin: 2rem 0;
}
.about-module--sub_title--c2rzJ.about-module--active--DflhK {
    color: green;
}

Please take a note that the browser generates random hash to ensure uniqueness in the case of multiple CSS Modules with the same name.

Other CSS Module Examples

The following use case examples are directly adopted from the TechBoxWeb tutorial:

Example 1: Nested Class Names

A simple nested class example where style rules apply to element with that class name (parent), that are also nested with another element with a different class name. The class names are returned separately.

/* nested class example */
.parent .bold {
    color: blue;
    font-weight: bold;
}

/* after compilation */
.Style__parent___w-O8Y .Style__bold___1vZ-u {
    color: blue;
    font-weight: bold;
}

While using in a component, one class name is parent element and another class name on a child element.

//about.js
<section className={style.parent}>
    <p className={style.bold}>
    Nested Class Name Example
    </p>
</section>

In the example above, the style.parent is parent and style.bold is child.

Example 2: Multiple classes on the Same Element

When two CSS classes are implemented in the same element (eg., red.bold ) red and bold classes. After compilation, if used in the same element, remains in one class even after compilation (line 7).

/* multiple class in a single element */
.red.bold {
    font-style: italic;
}

/* after compilation */
.Style__red___1dIEw.Style__bold___1vZ-u {
    font-style: italic;
}

While using in the component file, the .red.bold rule becomes 2 separate variables of the style, {style.red} and {style.bold} and take effect if used in the same element (see below)

// about.js
<section >
   <p className={`${style.red} ${style.bold}`}>
      Multiple class Name Class Name Example
    </p>
 </section>

In the example above, class red and bold are sibling which got applied on the same element.

Example 3: Composition in CSS Module

CSS class name from other CSS selector can be composed in CSS modules. An example using in the same module:

/* composition */
.site {
  width: 780px;
  margin:1rem auto;
  box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.795);
  padding: 1rem;
}

.bg-color {
  background-color: WhiteSmoke;
}

.title {
  composes: bg-color ;  color: black;
  font-weight: 600;
}

In the example above, the bg-color selector is used inside title selector using composes property.Its use case example in a component:

// about.js
<section >
   <p className={style.title}>
      Composition Example
   </p>
</section>
Example 4: Composition from Other CSS Module

CSS selector can be composed from other CSS modules which mean, styles can be shared between components.

/* about.module.css */
.title {
  font-size: 2rem;
  font-weight: 600;
  color:#808080;
}

In this examples, the title rule from the about.module.css is used in another CSS module, resources.module.css (see below)

/* resources.module.css */
.site {
  width: 780px;
  margin:1rem auto;
  box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.795);
  padding: 1rem;
}

.bg-color {
  background-color: WhiteSmoke;
}

.title {
  composes: title from './about.css';
  color: Aquamarine;
}

In the example above, title class was compose from about.css module and its color was modified.

Example 5: CSS Module in Media Queries

CSS module rules can be applied in media queries as well. Here using the same about.module.css file with the following rules:

/* about.module.css */
.body {
    font-size: 1rem;;
  }
  .header {
    font-size: 1.5rem;
  }
  
  @media (min-width: 780px) {
    .body{
        font-size: 1.25rem;
    }
    .header {
        font-size: 3rem;
    }
}

Use example of the above rules in about.js component.

//about.js
<div>
   <div className={styles.body}>
    The body class fonts size applied here 
   </div>
   <div className={styles.header}>
    The header class fonts size applied here
   </div>
</div>

The compiled browser generated rules looks like as below:

/*about.module.css */
.Style__body___NfhsW {
    font-size: 1rem;
}
.Style__header___2yU03 {
    font-size: 1.5rem;
}

@media (min-width: 780px)
  .Style__body___NfhsW {
    font-size: 1.25rem;
  }
  .Style__header___2yU03 {
    font-size: 3rem;
  }
}

The class names are changed but the media query rules stay the same.

Example 6: CSS Modules in Tag Names

This example is inspired by this tutorial post, which writes – CSS Modules rewrite class names, but they don’t touch tag names and selectors like input, div, or h1 will be left alone. Adopting the example from the tutorial:

/* about.module.css */
input.large {
  font-size: 20px;
}
.medium input {
  font-size: 14px;
}
.tiny * {
  font-size: 9px;
}
/* Don't do this! This style will be global */
input {
  padding: 0.5rem;
}

The above CSS rules after browser compilation:

/* about.module.css */
input.Widget6_large_1QsrJ {
  font-size: 20px;
}
.Widget6_medium_FwRDZ input {
  font-size: 14px;
}
.Widget6_tiny_2iJZp * {
  font-size: 9px;
}
/* Don't do this! This style will be global */
input {
  padding: 0.5rem;
}

Its use case in about.js component.

/* about.js */
<div>
   <input
     className={styles.large}
     placeholder="I am large"
    />
   <div className={styles.medium}>
     <input placeholder="I am medium" />
   </div>
   <div className={styles.tiny}>
     <input placeholder="I am so tiny" />
   </div>
</div>

It’s advised NOT to use a selector which is just a tag name instead always use a class name. Or else, the CSS rules will be applied globally!

Useful Resources

While preparing this post, I have referred the following references extensively. Please refer to original posts for more detailed information.