- The loader walks the module graph and gathers style-producing files (
.css,.scss,.sass,.less,.css.ts, etc.). - It walks the module graph with a built-in depth-first resolver so imports are visited in source order.
- Resolution is powered by
oxc-resolver, so tsconfigpaths, packageexportsconditions, and extension aliasing (like.css.js→.css.ts) all map to the same targets you’d get in a bundler. - JSX/TSX is supported: if
es-module-lexercan’t parse a file or the extension is.jsx/.tsx, we fall back tooxc-parserto read imports and defaults without any user configuration. - CSS from those files is concatenated in that discovery order and returned as
knightedCssfor injection (e.g., Litcss``, SSR, SSG). - We do not sort or reorder; first-seen order is kept, so the CSS cascade mirrors the original import sequence.
- Entry module CSS (if any) is first, then each import in the order written, recursing depth-first.
- Files are deduped but keep their first-seen position; later encounters are skipped to avoid reshuffling the cascade.
- We do not apply alphabetical or timestamp sorts.
lightningcssminifies/prefixes but preserves the rule order we provide, so cascade semantics are intact.
The walker performs a depth-first, preorder traversal (parent before children) so imports are resolved exactly as they are written in source. Files are deduped: the first time a file is seen, it’s included; later encounters are skipped to avoid reshuffling the cascade. We never sort the list, and lightningcss preserves the rule order we provide.
Given:
entry.tsimports./athen./baimports./a1then./a2bimports./b1- CSS files:
entry.cssa.css(imported bya)a1.cssa2.cssb.cssb1.css
Concatenation order:
entry.cssa.cssa1.cssa2.cssb.cssb1.css
If a file is imported multiple times, only its first-seen position is kept; later repeats are ignored to keep cascade order stable.
The concatenation order we produce aligns with the "Order of Appearance" part of the CSS cascade. Specificity (e.g. whether a selector is an ID, class or element name) still wins when selectors differ, but when specificity is the same, later rules win. Because we preserve import order in a depth-first, preorder traversal, foundational styles (earlier imports) land before overrides (later imports). We never alter selector specificity; we only guarantee that file order mirrors the import structure so equal-specificity overrides behave as authored.
- Unknown/modern at-rules (e.g.,
@scope) are passed through as written; we do not strip or reorder them. - If you need polyfills for non-supporting browsers, add a follow-on CSS transform step; we leave them intact.
If you need to raise specificity for targeted selectors, you can supply a Lightning CSS visitor via the specificityBoost option (available in both the helper and loader). We compose your visitor with any existing lightningcss.visitor you provide; order of rules stays the same.
- Cascade behavior should match running the same module graph in a bundler that respects import order.
- If you observe ordering differences, look for downstream steps that resort, merge, or extract CSS; the loader itself keeps the discovered order.