Modern CSS with Lightning CSS

WP Rig’s CSS pipeline is built on Lightning CSS — no PostCSS, no Sass. You write modern CSS (native nesting, custom media, @layer, container queries, :has()) and Lightning CSS compiles it down to your Browserslist targets. This post covers the workflow, the enforced style budget, and the playbook conventions that keep a theme’s CSS fast and maintainable.

The style workflow

  • Edit source files in assets/css/src/ (e.g. _header.css) — never compiled artifacts.
  • Import: global.css pulls partials together via @import.
  • Build: build-css.js compiles with Lightning CSS and your Browserslist config. build:css, dev:css, and lint:css regenerate design tokens automatically first.
  • Enqueue: the Styles component enqueues compiled CSS; some sheets load conditionally per page.

Design tokens are generated into _tokens.generated.css (gitignored) from config/tokens.json — see Design Tokens v2. Hand-authored overrides live in _tokens.custom.css.

Custom media from settings.viewport

Breakpoints are not hardcoded. All seven @custom-media aliases derive from settings.viewport (mobile 480px / tablet 782px — the WordPress-native scale) in config/tokens.json.breakpoints, so your CSS, theme.json, core’s block-visibility classes, and the editor preview all agree on one scale:

/* _custom-media.css — generated from settings.viewport */
@custom-media --screen-mobile (min-width: 481px);
@custom-media --screen-tablet (min-width: 782px);

.sidebar {
  display: none;

  @media (--screen-tablet) {
    display: block;
  }
}

Native features you can use today

  • Native nesting — Lightning CSS expands it; keep it ≤ 3 levels (budget, below).
  • @layer — cascade layers for reset → base → component ordering.
  • :has() — parent selection (compiled or passed through per Browserslist).
  • Container queries — component-responsive layouts.
  • Relative color syntax & color-mix() — the color-math foundation of the token system (derive shades/tints instead of naming them).
  • oklch()/oklab() — perceptually uniform color manipulation.

The enforced style budget

npm run lint:css fails on any of these — the budget is the design contract, not a suggestion:

RuleLimit
Nesting depth≤ 3 levels
Selector specificity≤ (0,4,1)
!importantnever
Custom propertiesmust be read via var() — wprig/no-undefined-custom-properties also flags unknown tokens
Descending specificitynever
Hardcoded colorswprig/no-hardcoded-colors flags literals that match a token value

Auto-fix what you can with npm run fix:css.

Paradigm-gated stylesheets

Some modules ship conditionally: dev.styles.preload lists partials injected into every bundle, and dev.styles.preloadBlockBased appends _blocks-based.css (FSE core-block baselines) only when the active theme type is universal or block-based. It never ships in a classic theme. The full module inventory and droppable scenarios are documented in the repo at docs/css-architecture.md.

Block CSS organization

  • _blocks.css — all block-level CSS (quote, cover, galleries, has-* utilities, custom block styles). Shared classic + block content styles.
  • _blocks-based.css — FSE-only baseline (theme parts, Navigation block, Query Loop), paradigm-gated.
  • editor/editor-styles.css — editor-canvas-only styles, isolated and enqueued only in the editor.
  • Classic template partials (_header.css, _navigation.css, _footer.css, …) stay separate; universal themes can drop them.

Critical CSS & performance

Above-the-fold bundles (layout.css, header-navigation.critical.css) feed the cookie-based critical-inlining strategy: first visit inlines critical CSS for the fastest First Contentful Paint; once assets are cached (a wprig_critical_cached cookie), subsequent visits load standard external files. Component-managed fonts are preloaded and inlined through the same system — see Font Management.

Design Tokens v2 for the variable system, CSS in WP Rig for the classic intro, and Theme Paradigms for stylesheet gating.