Build-Time CSS-in-JS Libraries: Here’s Why They Are Trending

Why zero-runtime CSS-in-JS tools have become the preferred choice for web developersTwo major revolutions have reshaped the way we write and manage CSS: runtime CSS-in-JS and the more recent transition to build-time CSS-in-JS. Both approaches align wit…


This content originally appeared on Bits and Pieces - Medium and was authored by Eden Ella

Why zero-runtime CSS-in-JS tools have become the preferred choice for web developers

Two major revolutions have reshaped the way we write and manage CSS: runtime CSS-in-JS and the more recent transition to build-time CSS-in-JS. Both approaches align with composable UIs, integrate seamlessly with modern UI frameworks, and enable patterns that lead to more maintainable code.

To understand the need for the latter, let’s first explore the advantages brought by the former.

Runtime CSS-in-JS (no build-time CSS extraction)

CSS-in-JS is a styling technique where CSS is written using JavaScript, often within the same file as the component it styles. For example:

import { css } from '@emotion/css'

const primaryColor = '#4287f5'

const myClass = css`
padding: 32px;
color: ${primaryColor}`

const StyledDiv = () => <div className={myClass} />

This technique and the tools that support it have become incredibly popular for two main reasons: how well they work with composable applications and the improved developer experience they can provide.

See an example in this Material UI Bit scope or read this blog about creating custom Material UI components:

MUI cusotm components shared on Bit platform

Built for components: scoped, context-agnostic styling that supports component reuse

Plain CSS is global in nature. It works well in legacy monolithic applications where different parts of the app’s UI are tightly coupled and “hyper-aware” of their context. In these apps, we create global CSS files that apply styles to different elements, often using selectors that assume a specific HTML structure.

.main-content article h2 {
font-size: 28px;
color: navy;
text-align: center;
}

In contrast, modern composable UIs often reuse components across different parts of the application or even across different applications.

Components are mounted into different places in the DOM, which means their styling needs to be agnostic to their context. Styling rules should not be defined globally, using selectors that expect to find elements in a particular place in the DOM tree (i.e., they should not select using a path that would not apply when the components are mounted elsewhere).

In addition, components are often authored and delivered by different developers. If styling rules are not scoped to their components, name conflicts will most certainly happen.

The DevX benefits of using JS/TS

Javascript and, to a larger extent, Typescript provide a developer experience far better than CSS. Type checking, code completion, and better linting support empower developers to create highly standardized and maintainable code that produces more robust UIs.

Standardization based on types also plays a vital role in composability. For example, a theme type can be used as the contract for teams in an organization to extend and modify the base theme or default theme while adhering to the same rules and style tokens.

import { createTheme, defaultTheme, type ThemeOptions } from '@my-org.design/theme';

export function darkTheme(): ThemeOptions {
return createTheme(defaultTheme(), {
palette: {
type: 'dark',
primary: {
main: '#6580f9',
secondary: '#ab7bb3',
},
background: {
default: '#454546',
paper: '#000000',
},
text: {
primary: '#ffffff',
},
},
});

Integration with modern UI frameworks

In addition, modern UIs are implemented using frameworks that use an abstract Javascript-based layer to perform manipulations in the DOM. The developer rarely interacts directly with the app’s HTML, making a JS-based styling solution a more natural fit.

For example, a component’s state or props that “live in the JS world” can more easily be used to affect the component’s styling, which occupies the same world.

interface ButtonProps {
primary?: boolean;
}

const Button = styled.button<ButtonProps>`
background-color: ${props => props.primary ? 'blue' : 'gray'};
`;

Build-time CSS-in-JS

As mentioned, CSS-in-JS libraries improved styling by allowing developers to write CSS directly within JavaScript. However, as applications grew, so did the drawbacks — namely, increased bundle sizes and runtime performance hits due to client-side style computation.

This is where build time CSS extraction plays an important role. It retains the advantages of CSS-in-JS while eliminating its performance penalties.

Having said that, build-time CSS-in-JS solutions not only solve performance issues but also address recent trends in SSR (server-side rendering). While runtime CSS-in-JS can be used in SSR, they are not optimized for it. In addition, some patterns and technologies used for SSR applications are not supported.

For example, React Server Components (RSC) can’t handle runtime-style injection mechanisms like useContext or useState to manipulate CSS since they are rendered on the server without the React lifecycle mechanisms available in the browser. Static styles that are not dependent on the app’s state are necessary for RSC’s server-only execution model.

Popular CSS-in-JS tools and libraries include Vanilla Extract, Linaria, Panda CSS, StyleX, and recently, Material UI has come out with experimental support for Pigment CSS.

Many tools (although not all) are based on the wyw-in-js, short for “Whatever-you-want-in-JS,” toolkit for zero-runtime CSS.

How build-time CSS extraction works

Different solutions use different techniques and optimization. However, at the core of any build-time CSS extraction are three stages: Parsing out CSS-in-JS declarations, generating a static CSS file, and updating the source files to reference classes in the newly generated static file.

Step 1: Parse the source code using static analysis

The tool scans your codebase to find CSS-in-JS declarations. For example:

import { styled } from 'linaria/react';

const Button = styled.button`
background-color: #327fa8;
color: #ffffff;
`;

Step 2: Generate static CSS

The parsed-out CSS-in-JS declarations are converted into standard CSS rules. Unique class names are generated (often using a hash) to prevent naming collisions.

.styles_button-fhg2jf {
background-color: #327fa8;
color: #ffffff;
}

Optimization steps are often included in build-time CSS-in-JS solutions such as merging duplicate CSS rules, dead code elimination, etc.

Step 3: Update the code to reference the generated static class

const Button = (props) => <button className="styles_button-fhg2jf" {...props} />;

Since this process happens in the build phase, it is achieved by configuring the bundler with the appropriate plugin. For example, the following is the basic Webpack setup for Vanilla Extract:

/**
* @filename: webpack.config.js
*/

const {
VanillaExtractPlugin
} = require('@vanilla-extract/webpack-plugin');

module.exports = {
plugins: [new VanillaExtractPlugin()]
};

Avoiding compatibility issues when reusing build-time CSS-in-JS components

In modern web development, components are often shared across projects as Bit components in an effort to make the codebase more maintainable, speed up development, and improve overall collaboration.

For example, see these custom Shadcn/UI components shared and maintained on Bit Platform:

Cusotm sahdcn/ui components shared and collaborated on Bit platform

However, components, like full applications, require a certain context for them to be functional. This context can be thought of as the “component runtime environment.” We should aim to make this context as flexible as possible so that our shared components are reusable across a wider range of applications.

A UI component that uses (in-memory) states for styling would not be able to function in all contexts. One example I’ve already given is React Server Components. To address that, either refrain from using states in all components or ensure they can use fallback when the context does not allow for states.

For more information, see this blog post:

Understanding Component Runtime Environments.

Learn More


Build-Time CSS-in-JS Libraries: Here’s Why They Are Trending was originally published in Bits and Pieces on Medium, where people are continuing the conversation by highlighting and responding to this story.


This content originally appeared on Bits and Pieces - Medium and was authored by Eden Ella


Print Share Comment Cite Upload Translate Updates
APA

Eden Ella | Sciencx (2024-10-07T15:48:21+00:00) Build-Time CSS-in-JS Libraries: Here’s Why They Are Trending. Retrieved from https://www.scien.cx/2024/10/07/build-time-css-in-js-libraries-heres-why-they-are-trending/

MLA
" » Build-Time CSS-in-JS Libraries: Here’s Why They Are Trending." Eden Ella | Sciencx - Monday October 7, 2024, https://www.scien.cx/2024/10/07/build-time-css-in-js-libraries-heres-why-they-are-trending/
HARVARD
Eden Ella | Sciencx Monday October 7, 2024 » Build-Time CSS-in-JS Libraries: Here’s Why They Are Trending., viewed ,<https://www.scien.cx/2024/10/07/build-time-css-in-js-libraries-heres-why-they-are-trending/>
VANCOUVER
Eden Ella | Sciencx - » Build-Time CSS-in-JS Libraries: Here’s Why They Are Trending. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2024/10/07/build-time-css-in-js-libraries-heres-why-they-are-trending/
CHICAGO
" » Build-Time CSS-in-JS Libraries: Here’s Why They Are Trending." Eden Ella | Sciencx - Accessed . https://www.scien.cx/2024/10/07/build-time-css-in-js-libraries-heres-why-they-are-trending/
IEEE
" » Build-Time CSS-in-JS Libraries: Here’s Why They Are Trending." Eden Ella | Sciencx [Online]. Available: https://www.scien.cx/2024/10/07/build-time-css-in-js-libraries-heres-why-they-are-trending/. [Accessed: ]
rf:citation
» Build-Time CSS-in-JS Libraries: Here’s Why They Are Trending | Eden Ella | Sciencx | https://www.scien.cx/2024/10/07/build-time-css-in-js-libraries-heres-why-they-are-trending/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.