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.csspulls partials together via@import. - Build:
build-css.jscompiles with Lightning CSS and your Browserslist config.build:css,dev:css, andlint:cssregenerate design tokens automatically first. - Enqueue: the
Stylescomponent 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:
| Rule | Limit |
| Nesting depth | ≤ 3 levels |
| Selector specificity | ≤ (0,4,1) |
!important | never |
| Custom properties | must be read via var() — wprig/no-undefined-custom-properties also flags unknown tokens |
| Descending specificity | never |
| Hardcoded colors | wprig/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.
Related reading
Design Tokens v2 for the variable system, CSS in WP Rig for the classic intro, and Theme Paradigms for stylesheet gating.