Apply

Layer architecture

Layers decide who wins before selectors start arguing. LSCSS uses explicit cascade layers so override order is architecture, not luck, or repeated emergency !important declarations. The exact layer names and how many you declare are a project choice; the premise is a deliberate stack where lower layers lose to higher ones.

Layer order

CSS code example
@layer legacy, settings, base, utilities,
layout, components, theme, hacks;

Lower layers lose to higher layers. Source order inside a layer matters less than the declared layer order itself. This means architecture controls overrides before specificity becomes a problem.

The list above is the stack this site and the docs use as a reference. You are not obliged to declare or populate every slot on every project.

Unlayered CSS wins

Styles that are not assigned to a layer sit in an implicit unlayered bucket. That bucket beats every declared layer — always, regardless of how carefully you orderedlegacy, settings, base, ..., hacks. A single unlayered file can override your whole stack without higher specificity or!important.

Many teams miss this. They add an @layer list at the top ofsite.css and assume architecture is done. The list only defines precedence between layers. Each stylesheet still has to land inside a layer — through @import ... layer(name), a named@layer name { ... } block, or equivalent. CSS left outside any layer is unlayered, and unlayered wins.

That is why messy layer setups hurt so fast:

  • A “quick fix” pasted into a global file with nolayer() import.
  • Vendor CSS bundled withoutlayer(thirdparty) or another agreed low slot.
  • Component CSS imported as a plain stylesheet instead oflayer(components).
  • New features added beside the layer map “just for now”, then left there.

Override order quietly reverts to luck. Specificity climbs. Emergency!important follows. LSCSS treatseverything authored belongs in a declared layer as a hard rule — not a nice-to-have. The stack only protects you when nothing important is left outside it.

!important and layers

Layers exist so you choose the winner with layer order, not arms races. !important makes that harder, not easier: for important declarations, cascade layers invert the usual order. Lower layers beat higher layers when both use!important.

Without !important, a rule in hacks wins over the same property in components becausehacks is higher in the stack. With!important on both, the result flips — important rules incomponents can beat important rules inhacks. A “fix” at the top of the stack is not a reliable escape hatch.

CSS code example
@layer components, hacks;

@layer components {
    .banner {
        margin-block-end: 2rem !important;
    }
}

@layer hacks {
    /* Higher layer — still loses to components for !important */
    .banner {
        margin-block-end: 0 !important;
    }
}

Unlayered !important still beats layered!important, the same way unlayered normal declarations beat layered ones (see unlayered CSS). Mixing layered and unlayered stylesheets makes debugging worse.

LSCSS stance: fix which layer owns the rule and whether the layer order is correct before reaching for !important. With layers in play, it is even less predictable than in pre-layer CSS.

Optional layers

A brand-new codebase might never ship a legacy folder at all. That is fine: omit the layer from the @layer declaration and do not import anything into it. What you should not do is drop vendor or third-party CSS into an arbitrary mid-stack layer because the namelegacy feels wrong. Give foreign styles their ownlowest intentional slot — call it thirdparty,vendor, or keep legacy for muscle memory — so your authored layers still win without selector arms races.

CSS code example
@layer thirdparty, settings, base, utilities,
    layout, components, theme, hacks;

@import './vendor/maps.css' layer(thirdparty);
@import './components/product-card.css' layer(components);

When you have both inherited first-party CSS and unavoidable vendor CSS, declare two low layers. List thirdparty beforelegacy so vendor styles stay lower in the cascade than your own old CSS you are still retiring.

CSS code example
@layer thirdparty, legacy, settings, base, utilities,
    layout, components, theme, hacks;

@import './vendor/slider.css' layer(thirdparty);
@import './legacy/old-site.css' layer(legacy);
@import './components/product-card.css' layer(components);

What each layer does

The bullets below describe the roles in the canonical LSCSS example stack (the same names appear in site.css on this site). Map them to your own layer names when you branch from the default.

  • legacy holds inherited first-party CSS you are retiring. Optional thirdparty sits below it when vendor CSS needs its own bucket (see legacy and third-party).
  • settings holds tokens, fonts, variables, and custom media.
  • base contains resets, typography, and true global defaults.
  • utilities holds rare helper classes like accessibility helpers.
  • layout handles structural wrappers and global layout patterns.
  • components contains normal component styling.
  • theme changes presentation, not component structure. This site declares the layer in site.css but maps palette and light/dark behaviour through semantic tokens in settings(see theme layer guidance andDESIGN.md in the repository).
  • hacks isolates urgent temporary fixes.

Names and alternatives

If you practise Atomic Design (or any other component taxonomy), you might replace layout, components, andtheme with something like atoms,molecules, organisms, andpages (or templates, or whatever your team agrees on). You might merge or split roles further. That is still LSCSS-shaped thinking as long as the order reflects who may override whom — not as long as the spellings match this page verbatim.

CSS code example
@layer settings, base, utilities,
    atoms, molecules, organisms, pages, hacks;

The same applies to utilities, base, andsettings: keep the ideas (tokens, globals, rare helpers, UI ownership, presentation-only overrides, quarantined hacks), rename the buckets to fit your design system language, and document the map once.

Import into the correct layer

CSS code example
@import './legacy/old-site.css' layer(legacy);
@import './settings/tokens.css' layer(settings);
@import './base/reset.css' layer(base);
@import './layout/page-shell.css' layer(layout);
@import './components/product-card.css' layer(components);
@import './theme/brand.css' layer(theme);
@import './hacks/temporary-fixes.css' layer(hacks);

The goal is not only naming layers. Files must be imported into the correct place or the architecture becomes decorative paperwork. Vendor CSS can live in layer(thirdparty) or, if you use one combined bucket, layer(legacy) — seelegacy and third-party.

Legacy and third-party layers

Inherited first-party CSS belongs in a dedicated low layer. In the canonical stack that layer is namedlegacy and declared first so it stays lowest among the default slots.

Third-party CSS — npm packages, widgets, CMS plugins — also belongs in a dedicated low layer, not in components orbase. How you name and split that depends on the project:

  • Legacy only — one low bucket for old first-party CSS (and optionally vendor CSS too). Use the canonical@layer legacy, settings, … declaration.
  • Third-party only — greenfield with required vendor CSS but no inherited first-party CSS. Omit legacy; declarethirdparty before settings.
  • Both — migration with old CSS and unavoidable vendor imports. Declare thirdparty before legacy so vendor styles stay lower than your own retired CSS.

Structured layers can then win without heavier selectors. Not every site needs every slot, but foreign CSS should be contained — not allowed to quietly become the new architecture through neglect.

Why the hacks layer exists

Sometimes production needs a fast correction before a full refactor is realistic. The hacks layer gives those fixes a visible quarantine zone. Like any other slot, you can omit it until you truly need it — just do not scatter “temporary” overrides through normal component files instead.

Hacks should be obvious, isolated, and temporary. If a hack has been living comfortably for nine months, it is no longer a hack. It should be reviewed as architecture.

Working rules

  • Do not ship authored or vendor CSS outside a declared layer — unlayered styles beat the entire stack.
  • Check layer order before increasing specificity.
  • Do not use !important to fix layer mistakes — check order and ownership first (see !important and layers).
  • Do not put component styles in base or theme files.
  • Do not hide hacks inside normal component partials.
  • Do not leave third-party CSS outside a dedicated low-priority layer (thirdparty, legacy, or both withthirdparty listed before legacy).
  • Theme changes presentation, not structure.
  • Layer order should be decided once, not negotiated weekly.