Skip to content

CSS Layers ​

remCSS uses @layer to create a deterministic, predictable cascade — without !important.

The Layer Order ​

Declared once, at the top of src/remcss.css, never changed:

css
@layer base, components, utilities;

Layers listed later win over layers listed earlier. The order means:

LayerWins overLoses to
baseBrowser defaultscomponents, utilities
componentsbaseutilities
utilitiesbase, components—

Why This Matters ​

Without layers, specificity wars force you to write !important or invent elaborate selectors. With layers, you always know who wins — it's the layer order, not specificity.

css
/* Without layers — this .button rule loses to a 3-part selector elsewhere */
.button {
	color: red;
}
nav .site-header .button {
	color: blue;
} /* wins */

/* With layers — utilities always win regardless of selector complexity */
@layer utilities {
	.text-danger {
		color: var(--color-danger);
	} /* always wins over components */
}

What Goes in Each Layer ​

@layer base ​

  • CSS reset (box model, margin/padding reset, reduced motion)
  • Root font-size calibration (html { font-size: 62.5% })
  • All :root custom properties (tokens for scale, color, typography)
  • Font stack declarations
  • Heading size defaults

No component-specific rules belong here.

@layer components ​

  • .button, .button-group
  • .form, .field, .label, .input
  • .table, .table-wrapper
  • .grid, .stack, .cluster, .sidebar
  • Every rule is scoped with @scope (progressive enhancement)

@layer utilities ​

  • .m-*, .p-*, .px-*, .py-*, .mt-* — spacing
  • .flex, .flex-col, .items-center, .gap-* — flexbox
  • .grid-auto, .grid-sidebar — grid helpers

Utilities are intentionally single-purpose and override everything else in the cascade.

@scope Inside Components ​

All component rules are wrapped in @scope for additional isolation:

css
/* Fallback for browsers without @scope */
.button {
	padding-block: var(--step-n1);
	padding-inline: var(--step-1);
}

/* Progressive enhancement */
@supports selector(:scope) {
	@scope (.button) {
		:scope {
			padding-block: var(--step-n1);
			padding-inline: var(--step-1);
		}
	}
}

@scope prevents styles from leaking out of the component root. A .button inside a .card cannot accidentally affect a .button outside of it.

Unlayered Tokens ​

CSS custom properties (tokens) are declared on :root outside of any layer. This is intentional — custom properties must resolve before any layer rule references them. CSSOM processes unlayered rules first, making them always available.

css
/* Tokens — no @layer, always resolve first */
:root {
	--step-1: calc(1rem * 1.618);
	--color-accent: oklch(62% 0.22 27);
}

/* Components can safely reference them */
@layer components {
	.button {
		background: var(--color-accent); /* resolves correctly */
	}
}