Responsible JavaScript

We just made a note about this article by Jeremy Wagner in our newsletter but it’s so good that I think it’s worth linking to again as Jeremy writes about how our obsession with JavaScript can lead to accessibility and performance issues:

What we tend to forget is that the environment websites and web apps occupy is one and the same. Both are subject to the same environmental pressures that the large gradient of networks and devices impose. Those constraints don’t suddenly vanish when we decide to call what we build “apps”, nor do our users’ phones gain magical new powers when we do so.

It’s our responsibility to evaluate who uses what we make, and accept that the conditions under which they access the internet can be different than what we’ve assumed. We need to know the purpose we’re trying to serve, and only then can we build something that admirably serves that purpose—even if it isn’t exciting to build.

That last part is especially interesting because it’s in the same vein as what Chris wrote just the other day about embracing simplicity in our work. But it’s also interesting because I’ve overheard a lot of engineers at work asking how we might use CSS-in-JS tools like Emotion or Styled Components, both of which are totally neat in and of themselves. But my worry is about jumping to a cool tool before we understand the problem that we want to tackle first.

Jumping on a bandwagon because a Twitter celebrity told us to do so, or because Netflix uses tool X, Y or Z is not a proper response to complex problems. And this connects to what Jeremy says here:

This is not to say that inaccessible patterns occur only when frameworks are used, but rather that a sole preference for JavaScript will eventually surface gaps in our understanding of HTML and CSS. These knowledge gaps will often result in mistakes we may not even be aware of. Frameworks can be useful tools that increase our productivity, but continuing education in core web technologies is essential to creating usable experiences, no matter what tools we choose to use.

Just – yikes. This makes me very excited for the upcoming articles in the series.

Direct Link to ArticlePermalink

The post Responsible JavaScript appeared first on CSS-Tricks.

Web Standards Meet User-Land: Using CSS-in-JS to Style Custom Elements

The popularity of CSS-in-JS has mostly come from the React community, and indeed many CSS-in-JS libraries are React-specific. However, Emotion, the most popular library in terms of npm downloads, is framework agnostic.

Using the shadow DOM is common when creating custom elements, but there’s no requirement to do so. Not all use cases require that level of encapsulation. While it’s also possible to style custom elements with CSS in a regular stylesheet, we’re going to look at using Emotion.

We start with an install:

npm i emotion

Emotion offers the css function:

import {css} from 'emotion';

css is a tagged template literal. It accepts standard CSS syntax but adds support for Sass-style nesting.

const buttonStyles = css` color: white; font-size: 16px; background-color: blue; &:hover { background-color: purple; }

Once some styles have been defined, they need to be applied. Working with custom elements can be somewhat cumbersome. Libraries — like Stencil and LitElement — compile to web components, but offer a friendlier API than what we’d get right out of the box.

So, we’re going to define styles with Emotion and take advantage of both Stencil and LitElement to make working with web components a little easier.

Applying styles for Stencil

Stencil makes use of the bleeding-edge JavaScript decorators feature. An @Component decorator is used to provide metadata about the component. By default, Stencil won’t use shadow DOM, but I like to be explicit by setting shadow: false inside the @Component decorator:

@Component({ tag: 'fancy-button', shadow: false

Stencil uses JSX, so the styles are applied with a curly bracket ({}) syntax:

export class Button { render() { return <div><button class={buttonStyles}><slot/></button></div> }

Here’s how a simple example component would look in Stencil:

import { css, injectGlobal } from 'emotion';
import {Component} from '@stencil/core'; const buttonStyles = css` color: white; font-size: 16px; background-color: blue; &:hover { background-color: purple; }
@Component({ tag: 'fancy-button', shadow: false
export class Button { render() { return <div><button class={buttonStyles}><slot/></button></div> }

Applying styles for LitElement

LitElement, on the other hand, use shadow DOM by default. When creating a custom element with LitElement, the LitElement class is extended. LitElement has a createRenderRoot() method, which creates and opens a shadow DOM:

createRenderRoot() { return this.attachShadow({mode: 'open'});

Don’t want to make use of shadow DOM? That requires re-implementing this method inside the component class:

class Button extends LitElement { createRenderRoot() { return this; }

Inside the render function, we can reference the styles we defined using a template literal:

render() { return html`<button class=${buttonStyles}>hello world!</button>`

It’s worth noting that when using LitElement, we can only use a slot element when also using shadow DOM (Stencil does not have this problem).

Put together, we end up with:

import {LitElement, html} from 'lit-element';
import {css, injectGlobal} from 'emotion';
const buttonStyles = css` color: white; font-size: 16px; background-color: blue; &:hover { background-color: purple; }
` class Button extends LitElement { createRenderRoot() { return this; } render() { return html`<button class=${buttonStyles}>hello world!</button>` }
} customElements.define('fancy-button', Button);

Understanding Emotion

We don’t have to stress over naming our button — a random class name will be generated by Emotion.

We could make use of CSS nesting and attach a class only to a parent element. Alternatively, we can define styles as separate tagged template literals:

const styles = { heading: css` font-size: 24px; `, para: css` color: pink; `

And then apply them separately to different HTML elements (this example uses JSX):

render() { return <div> <h2 class={styles.heading}>lorem ipsum</h2> <p class={styles.para}>lorem ipsum</p> </div>

Styling the container

So far, we’ve styled the inner contents of the custom element. To style the container itself, we need another import from Emotion.

import {css, injectGlobal} from 'emotion';

injectGlobal injects styles into the “global scope” (like writing regular CSS in a traditional stylesheet — rather than generating a random class name). Custom elements are display: inline by default (a somewhat odd decision from spec authors). In almost all cases, I change this default with a style applied to all instances of the component. Below are the buttonStyles which is how we can change that up, making use of injectGlobal:

fancy-button { display: block;

Why not just use shadow DOM?

If a component could end up in any codebase, then shadow DOM may well be a good option. It’s ideal for third party widgets — any CSS that’s applied to the page simply won’t break the the component, thanks to the isolated nature of shadow DOM. That’s why it’s used by Twitter embeds, to take one example. However, the vast majority of us make components for for a particular site or app and nowhere else. In that situation, shadow DOM can arguably add complexity with limited benefit.

The post Web Standards Meet User-Land: Using CSS-in-JS to Style Custom Elements appeared first on CSS-Tricks.

Why I Write CSS in JavaScript

I’m never going to tell you that writing your CSS in CSS (or some syntactic preprocessor) is a bad idea. I think you can be perfectly productive and performant without any tooling at all. But, I also think writing CSS in JavaScript is a good idea for component-based styles in codebases that build all their components with JavaScript anyway.

In this article, Max Stoiber focuses on why to write CSS in JavaScript rather than how to do it. There is one reason that resonates strongly with me, and that’s confidence. This is what styling confidence means to me.

  • Anyone on a team can work on styling a component without any fear of unintended side effects.
  • There is no pressure to come up with perfect names that will work now and forever.
  • There is no worry about the styles needing to be extremely re-usable or that they play friendly with anything else. These styles will only be used when needed and not any other time.
  • There is an obvious standard to where styles are placed in the codebase.
  • CSS in JavaScript isn’t the only answer to those things, but as Max connects to other posts on the topic, it can lead to situations where good choices happen naturally.

    There are some reasons why I don’t buy into it. Performance is one of them, like choosing CSS-in-JS is some automatic performance win. Part of the problem (and I’m guilty of doing it right here) is that CSS-in-JS is a wide scope of solutions. I’ve generally found there is no big performance wins in CSS-in-JS (more likely the opposite), but that’s irrelevant if we’re talking about something like CSS modules with the styles extracted and linked up like any other CSS.

    Direct Link to ArticlePermalink

    The post Why I Write CSS in JavaScript appeared first on CSS-Tricks.

    Regarding CSS’s Global Scope

    html { font-family: Roboto, sans-serif;

    With the except of some form elements, you’ve just set a font on every bit of text on a site! Nice! That’s probably what you were trying to do, because of the probably hundreds of elements all over your site, setting that font-family every time would be tedious and error-prone.

    CSS is global by nature. On purpose!

    I like how David Khourshid put it:

    You ever stop and think about why CSS has a global scope? Maybe we want to use consistent typography, colors, sizing, spacing, layout, transitions, etc. and have our websites & apps feel like one cohesive unit?

    Love the cascade, the cascade is your friend.

    And yet. The global nature of CSS is perhaps the most-pointed-at anti-feature of CSS. Some people really don’t like it. We all know it’s very easy to write a single CSS rule that has implications all over a site, breaking things you really didn’t want to break.

    There are whole new categories of testing to assist with these problems.

    Scoped styles aren’t the only reason there is such interest and adoption in the landscape of tools that is CSS-in-JS, but it’s a big one. There are loads of sites that don’t directly author any CSS at all — even preprocessed styles — and go for a JavaScript library instead where styles are authored quite literally in JavaScript. There is a playground demonstrating the syntax of the various options. Here’s how styled-components works:

    import React from 'react';
    import styled from 'styled-components'; const Container = styled.main` display: flex; flex-direction: column; min-height: 100%; width: 100%; background-color: #f6f9fc;
    `; export default function Login() { return ( <Container> ... Some stuff .... </Container> );

    There are literally dozens of options, each doing things a bit differently while offering slightly different syntaxes and features. Vue even offers scoped CSS directly in .vue files:

    <style scoped>
    .example { color: red;
    </style> <template> <div class="example">hi</div>

    Unfortunately, <style scoped> never quite made it as a native web platform feature. There is shadow DOM, though, where a style block can be injected in a template and those styles will be isolated from the rest of the page:

    let myElement = document.querySelector('.my-element'); let shadow = myElement.attachShadow({ mode: 'closed'
    shadow.innerHTML = ` <style> p { color: red; } </style> <p>Element with Shadow DOM</p>

    No styles will leak into or out of that shadow DOM boundary. That’s pretty cool for people seeking this kind of isolation, but it could be tricky. You’d likely have to architect the CSS to have certain global styles that can be imported with the shadow DOM’d web component so it can achieve some styling cohesion in your site. Personally, I wish it was possible to make the shadow DOM one-way permeable: styles can leak in, but styles defined inside can’t leak out.

    CSS-in-JS stuff is only one way to scope styles. There are actually two sides to the spectrum. You could call CSS-in-JS total isolation, whereas you could author CSS directly with total abstraction:

    Total abstraction might come from a project, like Tachyons, that gives you a fixed set of class names to use for styling (Tailwind is like a configurable version of that), or a programmatic tool (like Atomizer) that turns specially named HTML class attributes into a stylesheet with exactly what it needs.

    Even adhering 100% to BEM across your entire site could be considered total CSS isolation, solving the problems that the global scope may bring.

    Personally, I’d like to see us move to this kind of future:

    When we write styles, we will always make a choice. Is this a global style? Am I, on purpose, leaking this style across the entire site? Or, am I writing CSS that is specific to this component? CSS will be split in half between these two. Component-specific styles will be scoped and bundled with the component and used as needed.

    Best of both worlds, that.

    Anyway, it’s tricky.

    Maybe this will be the hottest CSS topic in 2019.

    The post Regarding CSS’s Global Scope appeared first on CSS-Tricks.

    Bridging the Gap Between CSS and JavaScript: CSS Modules, PostCSS and the Future of CSS

    In the previous post in this two-part series, we explored the CSS-in-JS landscape and, we realized not only that CSS-in-JS can produce critical styles, but also that some libraries don’t even have a runtime. We saw that user experience can significantly improve by adding clever optimizations, which is why this series focuses on developer experience (the experience of authoring styles).

    In this part, we’ll explore the tools for “plain ol’ CSS” by refactoring the Photo component from our existing example.

    Controversy and #hotdrama

    One of the most famous CSS debates is whether the language is fine just the way that it is. I think this debate stays alive because there is some truth to both sides. For example, while it’s true that CSS was initially designed to style a document rather than components of an application, it’s also true that upcoming CSS features will dramatically change this, and that many CSS mistakes stem from treating styling as an afterthought instead of taking time to learn it properly or hiring someone who’s good at it.

    I don’t think that CSS tools themselves are the source of the controversy; we’ll probably always use them to some extent at the very least. But approaches like CSS-in-JS are different in that they patch up the shortcomings of CSS with client-side JavaScript. However, CSS-in-JS is not the only approach here; it is merely the newest. Remember when we used to have similar debates about preprocessors, like Sass? Sass has features, like mixins, that aren’t based on any CSS proposal (not to mention the entire indented syntax). However, Sass was born in a much different time and has reached a point where it’s no longer fair to include it in the debate because the debate itself has changed — so we started criticizing CSS-in-JS because it’s an easier target.

    I think we should use tools that let us use proposed syntax today. Let’s use JavaScript Promises as an analogy. This feature isn’t supported by Internet Explorer, so many people include a polyfill for it. The point of polyfills is to enable us to pretend like the feature is supported everywhere by substituting native browser implementations with a patch. Same goes for transpiling new syntax with tools, like Babel. We can use it today because the code will be compiled to an older, well-supported syntax. This is a good approach because it allows us to use future features today while pushing JavaScript forward the way preprocessing tools, like Sass, have pushed CSS forward.

    My take on the CSS controversy is that we should use tools that enable us to use future CSS today.


    We’ve already talked a bit about CSS preprocessors, so it’s worth discussing them in a little more details and how they fit into the CSS-in-JS conversation. We have Sass, Less and PostCSS (among others) that can imbue our CSS code with all kinds of new features.

    For our example, we’re only going to be concerned with nesting, one of the most common and powerful features of preprocessors. I suggest using PostCSS because it gives us fine-grained control over the features we’re adding, which is exactly what we need in this case. The PostCSS plugin that we’re going to use is postcss-nesting because it follows the actual proposal for native CSS nesting.

    The best way to use PostCSS with our compiling tool, webpack, is to add postcss-loader after css-loader in the configuration. When adding loaders after css-loader, it’s important to account for them in the css-loader options by setting importLoaders to the number of succeeding loaders, which in this case is 1:

    { test: /\.css$/, use: [ 'style-loader', { loader: 'css-loader', options: { importLoaders: 1, }, }, 'postcss-loader', ],

    This ensures that CSS files imported from other CSS files will be processed with postcss-loader as well.

    After setting up postcss-loader, we’ll install postcss-nesting and include it in the PostCSS configuration:

    yarn add postcss-nesting

    There are many ways to configure PostCSS. In this case, we’re going to add a postcss.config.js file at the root of our project:

    module.exports = { plugins: { "postcss-nesting": {}, },

    Now, we can write a CSS file for our Photo component. Let’s call it Photo.css:

    .photo { width: 200px; &.rounded { border-radius: 1rem; }
    } @media (min-width: 30rem) { .photo { width: 400px; }

    Let’s also add a file called utils.css that contains a class for visually hiding elements, as we covered in the the first part of this series:

    .visuallyHidden { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; white-space: nowrap;

    Since our component relies on this utility, let’s include utils.css to Photo.css by adding an @import statement to the top:

    @import url('utils.css');

    This will ensure that webpack requires utils.css, thanks to css-loader. We can place utils.css anywhere we want and adjust the @import path. In this particular case, it’s a sibling of Photo.css.

    Next, let’s import Photo.css into our JavaScript file and use the classes to style our component:

    import React from 'react'
    import { getSrc, getSrcSet } from './utils'
    import './Photo.css' const Photo = ({ publicId, alt, rounded }) => ( <figure> <img className={rounded ? 'photo rounded' : 'photo'} src={getSrc({ publicId, width: 200 })} srcSet={getSrcSet({ publicId, widths: [200, 400, 800] })} sizes="(min-width: 30rem) 400px, 200px" /> <figcaption className="visuallyHidden">{alt}</figcaption> </figure>
    ) Photo.defaultProps = { rounded: false,
    } export default Photo

    While this will work, our class names are way too simple and they will most certainly clash with others completely unrelated to our .photo class. One of the ways of working around this is using a naming methodology, like BEM, to rename our classes (e.g. photo_rounded and photo__what-is-this--i-cant-even) to help prevent clashes from happening, but components quickly get complex and class names tend to get long, depending on the overall complexity of the project.

    Meet CSS Modules.

    CSS Modules

    Simply put, CSS Modules are CSS files in which all class names and animations are scoped locally by default. They look a lot like regular CSS. For example, we can use our Photo.css and utils.css files as CSS Modules without modifying them at all, simply by passing modules: true to css-loader’s options:

    { loader: 'css-loader', options: { importLoaders: 1, modules: true, },

    CSS Modules are an evolving feature and could be discussed at even greater length. Robin’s three-part series on it is a good overview and introduction.

    While CSS Modules themselves look very similar to regular CSS, the way we use them is quite different. They are imported into JavaScript as objects where keys correspond to authored class names, and values are unique class names that are auto-generated for us that keep the scope limited to a component:

    import React from 'react'
    import { getSrc, getSrcSet } from './utils'
    import styles from './Photo.css'
    import stylesUtils from './utils.css' const Photo = ({ publicId, alt, rounded }) => ( <figure> <img className={rounded ? `${} ${styles.rounded}` :} src={getSrc({ publicId, width: 200 })} srcSet={getSrcSet({ publicId, widths: [200, 400, 800] })} sizes="(min-width: 30rem) 400px, 200px" /> <figcaption className={stylesUtils.visuallyHidden}>{alt}</figcaption> </figure>
    ) Photo.defaultProps = { rounded: false,
    } export default Photo

    Since we’re using utils.css as a CSS Module, we can remove the @import statement at the top of Photo.css. Also, notice that using camelCase to format class names makes them easier to use in JavaScript. If we had used dashes, we’d have to write things out in full, like stylesUtils['visually-hidden'].

    CSS Modules have additional features, like composition. Right now, we’re importing utils.css into Photo.js to apply our component styles, but let’s say that we want to shift the responsibility of styling the caption to Photo.css instead. That way, as far as our JSX code is concerned, styles.caption is just another class name; it just so happens to visually hide the element, but it might be styled differently in the future. Either way, Photo.css will be making those decisions.

    So let’s add a caption style to Photo.css to extend the properties of the visuallyHidden utility using composes:

    .caption { composes: visuallyHidden from './utils.css';

    We could just as well add more rules to that class, but this is all we need in this case. Now, we no longer need to import utils.css into Photo.js; we can simply use styles.caption instead:

    <figcaption className={styles.caption}>{alt}</figcaption>

    How does this work? Do the styles from visuallyHidden get copied over to caption? Let’s examine the value of styles.caption — whoa, two classes! That’s right: one is from visuallyHidden and the other one will apply any other styles we add to caption. CSS-in-JS makes it too easy to duplicate styles with libraries, like polished, but CSS Modules encourage you to reuse existing styles. No need to create a new VisuallyHidden React component to only apply several CSS rules.

    Let’s take it even further by examining this uncomfortable class composition:

    rounded ? `${} ${styles.rounded}` :

    There are libraries for these situations, like classnames, which are useful for more complex class composition. In our example, though, we can keep on using composes and rename .rounded to .roundedPhoto:

    .photo { width: 200px;
    } .roundedPhoto { composes: photo; border-radius: 1rem;
    } @media (min-width: 30rem) { .photo { width: 400px; }
    } .caption { composes: visuallyHidden from './utils.css';

    Now we can apply the class names to our component in a much more readable fashion:

    rounded ? styles.roundedPhoto :

    But wait, what if we accidentally place the .roundedPhoto ruleset before .photo and some rules from .photo end up overriding rules from .roundedPhoto due to specificity? Don’t worry, CSS Modules prevent us from composing classes defined after the current class by throwing an error like this:

    referenced class name "photo" in composes not found (2:3) 1 | .roundedPhoto {
    > 2 | composes: photo; | ^ 3 | border-radius: 1rem; 4 | }

    Note that it’s generally a good idea to use a file naming convention for CSS Modules, for example using the extension .module.css, because it’s common to want to apply some global styles as well.

    Dynamic styles

    So far, we’ve been conditionally applying predefined sets of styles, which is called conditional styling. What if we also want to be able to fine-tune the border radius of the rounded photos? This is called dynamic styling because we don’t know what the value is going to be in advance; it can change while the application is running.

    There aren’t many use cases for dynamic styling — usually we’re styling conditionally, but in cases when we need this, how would we approach this? While we could get by with inline styles, a native solution for this type of problems is custom properties (a.k.a. CSS variables). A really valuable aspect of this feature is that browsers will update styles using custom properties when JavaScript changes them. We can set a custom property on an element through inline styles, which means that it will be scoped to that element and that element only:

    style={typeof borderRadius !== 'undefined' ? { '--border-radius': borderRadius,
    } : null}

    In Photo.css, we can use this custom property by using var() and passing the default value as the second argument:

    .roundedPhoto { composes: photo; border-radius: var(--border-radius, 1rem);

    As far as JavaScript is concerned, it’s only passing a dynamic parameter to CSS, then when CSS takes over, it can apply the value as-is, calculate a new value from it using calc(), etc.


    At the time of this writing, the browser support for custom properties is… well, you decide for yourself. Not supporting these browsers is (probably) out of the question for a real-world application, but keep in mind that some styles are less important than others. In this case, it’s not a big deal if the border radius on IE is always 1rem. The application doesn’t have to look the same way on every browser.

    The way we can automatically provide fallbacks for all custom properties is to install postcss-custom-properties and add it to our PostCSS configuration:

    yarn add postcss-custom-properties
    module.exports = { plugins: { 'postcss-nesting': {}, 'postcss-custom-properties': {}, },

    This will generate a fallback for our border-radius rule:

    .roundedPhoto { composes: photo; border-radius: 1rem; border-radius: var(--border-radius, 1rem);

    Browsers that don’t understand var() will ignore that rule and use the previous one. Don’t let the name of the plugin fool you; it only partially improves the support for custom properties by providing static fallbacks. The dynamic aspect can’t be polyfilled.

    Exposing values to JavaScript

    In the previous part of this series, we explored how CSS-in-JS allows us to share almost anything between CSS and JavaScript, using media queries as an example. There is no possible way to achieve this here, right?

    Thanks to Jonathan Neal, you can!

    First, meet postcss-preset-env, the successor to cssnext. It’s a PostCSS plugin that acts as a preset similar to @babel/preset-env. It contains plugins like postcss-nesting, postcss-custom-properties, autoprefixer etc. so we can use future CSS today. It splits the plugins across four stages of standardization. Some of the features I’d like to show you aren’t included in the default range (stage 2+), so we’ll explicitly enable the ones we need:

    yarn add postcss-preset-env
    module.exports = { plugins: { 'postcss-preset-env': { features: { 'nesting-rules': true, 'custom-properties': true, // already included in stage 2+ 'custom-media-queries': true, // oooh, what's this? 🙂 }, }, },

    Note that we replaced our existing plugins because this postcss-preset-env configuration includes them, meaning our existing code should work the same as before.

    Using custom properties in media queries is invalid because that’s not what they were designed for. Instead we’ll use custom media queries:

    @custom-media --photo-breakpoint (min-width: 30em); .photo { width: 200px;
    } @media (--photo-breakpoint) { .photo { width: 400px; }

    Even though this feature is in the experimental stage and therefore not supported in any browser, thanks to postcss-preset-env it just works! One catch is that PostCSS operates on a per-file basis, so this way only Photo.css can use --photo-breakpoint. Let’s do something about that.

    Jonathan Neal recently implemented an importFrom option in postcss-preset-env, which is passed to other plugins that support it as well, like postcss-custom-properties and postcss-custom-media. Its value can be many things, but for the purpose of our example, it’s a path to a file that will be imported to the files PostCSS processes. Let’s call this one global.css and move our custom media query there:

    @custom-media --photo-breakpoint (min-width: 30em);

    …and let’s define importFrom, providing the path to global.css:

    module.exports = { plugins: { 'postcss-preset-env': { importFrom: 'src/global.css', features: { 'nesting-rules': true, 'custom-properties': true, 'custom-media-queries': true, }, }, },

    Now we can delete the @custom-media line at the top of Photo.css and our --photo-breakpoint value will still work, because postcss-preset-env will use the one from global.css to compile it. Same goes for custom properties and custom selectors.

    Now, how to expose it to JavaScript? When experimental features like custom media queries get standardized and implemented in major browsers, we will be able to retrieve them natively from CSS. For example, this is how we would access a custom property called --font-family defined on :root:

    const rootStyles = getComputedStyle(document.body)
    const fontFamily = rootStyles.getPropertyValue('--font-family')

    If custom media queries get standardized we will probably be able to access them in a similar way, but in the meantime we have to find an alternative. We could use the exportTo option to generate a JavaScript or JSON file, which we would import into JavaScript. However, the problem is that webpack would try to require it before it’s generated. Even if we generated it before running webpack, every update to global.css would cause webpack to re-compile twice, once to generate the output file, and once more to import it. I wanted a solution that’s unencumbered by its implementation.

    For this series, I’ve created a brand new webpack loader called css-customs-loader just for you! It makes this task easy: all we need to is include it in our webpack configuration before css-loader:

    { test: /\.css$/, use: [ 'style-loader', 'css-customs-loader', { loader: 'css-loader', options: { importLoaders: 1, }, }, 'postcss-loader', ],

    This exposes custom media queries, as well as custom properties, to JavaScript. We can access them simply by importing global.css:

    import React from 'react'
    import { getSrc, getSrcSet } from './utils'
    import styles from './photo.module.css'
    import { customMedia } from './global.css' const Photo = ({ publicId, alt, rounded, borderRadius }) => ( <figure> <img className={rounded ? styles.roundedPhoto :} style={ typeof borderRadius !== 'undefined' ? { ['--border-radius']: borderRadius } : null } src={getSrc({ publicId, width: 200 })} srcSet={getSrcSet({ publicId, widths: [200, 400, 800] })} sizes={`${customMedia['--photo-breakpoint']} 400px, 200px`} /> <figcaption className={styles.caption}>{alt}</figcaption> </figure>
    ) Photo.defaultProps = { rounded: false,
    } export default Photo

    That’s it!

    I created a repository demonstrating all of the concepts discussed in this series. Its readme also contains some advanced tips about the approach described in this post.

    View Repo


    It’s safe to say that tools like CSS Modules and PostCSS and upcoming CSS features are up to the task of dealing with many challenges of CSS. Whichever side of the CSS debate you’re on, this approach is worth exploring.

    I have a strong CSS-in-JS background, but I’m very susceptible to hype, so keeping up with that world is very hard for me. While having styles next to the behavior can be succinct, it’s also mixing two very different languages — CSS is very verbose compared to JavaScript. This incentivized me to write less CSS because I wanted to avoid getting the file too crowded. This may be a matter of personal preference, but I didn’t want that to be an issue. Using a separate file for CSS finally gave my code some air.

    While mastering this approach may not be as straightforward as CSS-in-JS, I believe it’s more rewarding in the long run. It will improve your CSS skills and make you better prepared for its future.

    Article Series:

    1. CSS-in-JS
    2. CSS Modules, PostCSS and the Future of CSS (This post)

    The post Bridging the Gap Between CSS and JavaScript: CSS Modules, PostCSS and the Future of CSS appeared first on CSS-Tricks.