Class names & the cascade
Deterministic class names, rule ordering, specificity, conflicts and the preflight reset.
Deterministic names#
A class name is <prefix><hash> of the normalised rule — nib- plus an FNV-1a hash in base 36.
The same rule always gives the same class, so:
- server and client HTML match (no hydration warnings),
- output is cacheable and diffs are stable,
- identical rules are emitted once, however many elements use them.
Change the prefix with prefix: "x-" in the config.
Specificity#
Every generated selector has one class of specificity — or one class plus .dark for dark rules,
plus a pseudo for state rules. That is intentionally low, so:
- your own stylesheets can override NibCSS with a single class,
- NibCSS rules on the same element never need
!importantto beat each other.
Order#
@layer nib-preflight— the reset- base rules, in first-seen order
@mediablocks, in first-seen order
So a responsive rule always overrides its base rule.
Conflicts on one element#
Two rules for the same property on one element (p=8px; p=16px) produce two classes with equal
specificity; the one emitted later in the stylesheet wins. Avoid relying on that — write the property
once, or override a recipe property after the recipe, which is the documented pattern.
The preflight reset#
The engine emits a small base reset — box-sizing: border-box, zeroed margins and padding, sensible media
and form defaults — inside @layer nib-preflight. A cascade layer loses to unlayered CSS, so your
stylesheet always wins over it, and utilities always win over both.
export default defineConfig({
preflight: false, // no reset
// preflight: "*, *::before { box-sizing: border-box }", // your own
});