DEV Community

Cover image for EffCSS lazy mode: emit exactly what's used
Marat Sabitov
Marat Sabitov

Posted on

EffCSS lazy mode: emit exactly what's used

Docs
GitHub
NPM

Tailwind taught us that we don't have to pay for the variants we don't use — the tool keeps only what actually appears in markup. EffCSS brings the same idea to utility‑based style composition, but takes it on demand. Switch on lazy mode, and only the CSS that is actually used will be generated. Let's see it in action with a concrete example.

Typical case: you have many style variants

Let's say your colleague (let's call him Mr. Pearman) is an ardent fan of Animate.css. He created a special file with several of these animations, described through the EffCSS utilities:

import { animation } from 'effcss';

export const pulse = animation({
  from: {
    transform: 'scale3d(1, 1, 1)'
  },
  '50%': {
    transform: 'scale3d(1.05, 1.05, 1.05)'
  },
  to: {
    transform: 'scale3d(1, 1, 1)'
  }
});

export const heartBeat = animation({
  '0%': {
    transform: 'scale(1)'
  },
  '14%': {
    transform: 'scale(1.3)'
  },
  '28%': {
    transform: 'scale(1)'
  },
  '42%': {
    transform: 'scale(1.3)'
  },
  '70%': {
    transform: 'scale(1)'
  }
});
Enter fullscreen mode Exit fullscreen mode

Then, one wonderful day, you decided to use heartBeat animation in your CSS classes:

import { className } from 'effcss';
import { heartBeat } from '../animations';
import { duration } from '../themeVars';

const animationTimingFunction = 'ease-in-out';

export const heartBeatCls = className({
  animationName: heartBeat(),
  animationDuration: `calc(${duration('1s')} * 1.3)`,
  animationTimingFunction
});

export const shortHeartBeatCls = className({
  animationName: heartBeat(),
  animationDuration: `calc(${duration('1s')} * 0.5)`,
  animationTimingFunction
});

export const longHeartBeatCls = className({
  animationName: heartBeat(),
  animationDuration: `calc(${duration('1s')} * 2)`,
  animationTimingFunction
});
Enter fullscreen mode Exit fullscreen mode

So far so good — you've taken a ready-made variable and ready-made animation, and created your own class. Then in your component you use the heartBeatCls class:

import { heartBeatCls } from '../styles/heartbeat-variants';

const cls = `another-cls ${heartBeatCls}`;

export const HeartBeatBase = () => {
  return <div className={cls}>💚</div>
};
Enter fullscreen mode Exit fullscreen mode

But after running the build, you suddenly discover that all your classnames and all of Mr. Pearman's animations have been created. And this is a very important aspect — by default, EffCSS assumes that all your imported style files will be used. But the good news is that this behavior can be switched to lazy mode in two ways. Let's start with the first way — lazy utils.

Lazy utils — the first way

These utilities are always lazy, regardless of any global configuration. Each call returns a function-resolver. Nothing is generated until you:

  • call the resolver — fn() (or fn(...args)),
  • coerce it to a string — `${fn}` or String(fn).

A function is evaluated lazily, on the first warm-up.

Utility Returns Generates
lazyClassName(rule) (or className.lazy(rule)) () => string (class name) one anonymous class rule
lazyAttribute(rule) (or attribute.lazy(rule)) () => object ({ 'data-…': '' }) one anonymous attribute rule
lazyClassNames(gen) (or classNames.lazy(gen)) selectors resolver a class-selector stylesheet
lazyAttributes(gen) (or attributes.lazy(gen)) selectors resolver an attribute-selector stylesheet
lazyCustomStyles(gen) (or customStyles.lazy(gen)) selectors resolver a custom stylesheet

After reading the table, you decided to rewrite your heartbeat variants to lazy analogs:

export const heartBeatCls = className.lazy({/* the same */});

export const shortHeartBeatCls = className.lazy({/* the same */});

export const longHeartBeatCls = className.lazy({/* the same */});
Enter fullscreen mode Exit fullscreen mode

Lazy utilities always return functions, so you had to fix the component too:

import { heartBeatCls } from '../styles/heartbeat-variants';

// heartBeatCls is a lazy function now
const cls = `another-cls ${heartBeatCls()}`;

export const HeartBeatBase = () => {
  return <div className={cls}>💚</div>
};
Enter fullscreen mode Exit fullscreen mode

New project build — hooray, the unused class names are gone! But the animations are still in the generated styles. Possibly it's time to discuss solutions with Mr. Pearman.

Lazy configuration — the second way

The colleague looked at your code and sent a short reply:

import { configure } from 'effcss';

configure({
    lazy: true
});
Enter fullscreen mode Exit fullscreen mode

This code changes CSS emit logic — it switches, on the fly, every generating utility that returns a function-resolver to its lazy version:

variable, variables, animation, animations, layer, layers, font, fonts, classNames, attributes, customStyles.

You keep writing the same API — no need to reach for lazy* names. Each of these utilities now defers generation until its result is used.

It should be noted right away that className and attribute are not affected: they return no-callable values (string / object) rather than resolvers, so they keep generating immediately.

Well, it's time to add the magic code to your app:

import { createRoot } from 'react-dom/client';
import { configure } from 'effcss';

// should be called before any stylesheet created
configure({
  lazy: true
});

const domNode = document.getElementById('root');
const root = createRoot(domNode);

root.render(<App />);
Enter fullscreen mode Exit fullscreen mode

Final build — the styles contain exactly what's used. Now it doesn't matter how many variables, animations, or fonts are described through EffCSS utils — only what's used will be emitted.

Final thoughts

Here are some tips for using EffCSS lazy mode:

  • Use controlled lazy* utilities when you want laziness only for specific rules and keep the rest eager, or when you want to guarantee laziness independently of the app-wide setting.
  • Use configure({ lazy: true }) when you want a broad only-what-is-used behavior across the whole library without changing your code, e.g. combined with vite-plugin-effcss to ship only the CSS that is actually rendered.
  • Use configure({ lazy: true }) on both the server and the client side to ensure the generated stylesheets are the same.

Most importantly, lazy mode is the easiest way to import third-party EffCSS styles without cluttering your app with unused CSS. If you're only using your own rules, you probably don't need to enable this mode — just relax.

Enjoy your Frontend Development!

Top comments (0)