Today I Learned - Rocky Kev

TIL about CSS Modules

POSTED ON:

TAGS:

One of the challenges with CSS is that the cascading features of CSS can make it tricky to manage.

One strategy to avoid it is to use CSS Modules, where you define your markup in a JS file instead. It's not a official spec or anything; it's a build strategy.

This approach is designed to fix the problem of the global scope in CSS. With CSS Modules, a build tool changes class names and selectors to be locally scoped (i.e. kinda like namespaced).

Vanilla CSS:

<style>
.title {
background-color: red;
}
</style>

<h1 class="title">An example heading</h1>

CSS Modules:

// the file's name is `index.js`
import styles from "./styles.css";

element.innerHTML =
`<h1 class="${styles.title}">
An example heading
</h1>
`
;

Then, using a compiler like Webpack, the following steps would happen:

  1. The compiler would search through that styles.css file that we’ve imported,

  2. Look through the JavaScript we’ve written and make the .title class accessible via styles.title.

  3. Create new, separate HTML and CSS files, with a new string of characters replacing both the HTML class and the CSS selector class.

// index.js becomes two files:

// index.css
._styles__title_309571057 {
background-color: red;
}

// index.html
<h1 class="_styles__title_309571057">
An example heading
</h1>

The classes are dynamically generated, unique, and mapped to the correct styles.

JS Frameworks

Using CSS Modules in React:
via https://glenmaddern.com/articles/css-modules

/* 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>
}
}

Using CSS Modules in Vue:
via https://vuejs.org/api/sfc-css-features.html#css-modules

<template>
<p :class="$style.red">This should be red</p>
</template>

<style module>
.red {
color: red;
}
</style>

It's not the same as Scoped CSS, but the outcome is pretty similar.
https://vuejs.org/api/sfc-css-features.html#scoped-css

<style scoped>
.example {
color: red;
}
</style>

<template>
<div class="example">hi</div>
</template>

The composes keyword

.serif-font {
font-family: Georgia, serif;
}

.display {
composes: serif-font; // this
font-size: 30px;
line-height: 35px;
}

The CSS Module:

import type from "./type.css";

element.innerHTML =
`<h1 class="${type.display}">
This is a heading
</h1>
`
;

The result:

<h1 class="_type__display_0980340 _type__serif_404840">
  Heading title
</h1>

via CSS Modules


Related TILs

Tagged:

TIL the alternate keyword

If 'alternate' appears along with the stylesheet keyword, the linked file is an alternative stylesheet. It won’t be applied to the document, but it will be ready for when we need it.

TIL Logical Properties

For that sweet sweet Internationalization you want to avoid directional words like 'left', 'right', 'top', and 'bottom'.

TIL Using pseudo-classes in your querySelector!

let notTuna = document.querySelectorAll('.sandwich:not(.tuna)')