-
Notifications
You must be signed in to change notification settings - Fork 2
CSS Bundling and Layering Strategy
Our project leverages Vite and PostCSS to process and bundle CSS. A core architectural decision in our styling approach is the heavy reliance on native CSS Layers (@layer) combined with CSS Modules. This guarantees that our component styles are safely encapsulated, establishing a predictable specificity hierarchy without fighting the global cascade.
Our styling pipeline is orchestrated through the css configuration block inside vite.config.ts.
-
CSS Modules: We utilize CSS modules and enforce a
camelCaseconvention for local class names (localsConvention: 'camelCase'). -
PostCSS Preset Env: We use the
postcss-preset-envplugin (stage 1) to enable modern CSS features. Crucially, we explicitly disable polyfilling and flattening for specific features:-
'cascade-layers': false- This ensures that our native@layerrules remain intact in the final bundle rather than being flattened by the preset. -
'all-property': false- This preserves the nativeallproperty (e.g., used for CSS resets).
-
To enforce our layer hierarchy across all isolated component styles, we created a custom PostCSS plugin called wrapInLayerPlugin.
This plugin acts on the Abstract Syntax Tree (AST) of our CSS during the build process to automatically inject CSS layers. Here is how it functions:
-
Targeting Modules: The plugin specifically looks for files that match the
/\.module\.css/pattern, ensuring it only affects our scoped component stylesheets. -
Safety Check: Before making any AST modifications, the plugin verifies if the root nodes are already wrapped in an
@layerrule. If it detects an existing layer, it aborts to prevent redundant wrapping. -
Layer Wrapping: All existing CSS nodes within the
.module.cssfile are extracted, removed from the root, and then appended inside a new@layer koval-componentsblock. -
Hierarchy Declaration: To ensure the browser knows the intended specificity order as soon as the file is parsed, the plugin prepends a layer declaration rule:
@layer koval-reset, koval-components;.
By automating this via wrapInLayerPlugin.ts and vite.config.ts, we ensure that our developers can simply write standard CSS modules, while the build pipeline guarantees those styles are cleanly assigned to the lowest-priority component layers, making overrides by end-users (who write unlayered CSS) effortless.