Override Deck without !important

You wrote a rule, it is not being applied, and the framework's rule is winning in dev tools. The normal fix is a longer selector, and then !important, and then !important on the framework's side too. Deck ends that argument before it starts: put your CSS in a layer and it wins, however short your selector is.

The short answer

Wrap your CSS in @layer app.pages. That is it.

@layer app.pages {
  .card { border-radius: 0; }
}

One class, no !important, and it beats .card in deck.components even though the two selectors are identical. If you only take one thing from this page, take that.

Deck declares the order before any rule exists, in src/00-layers.css. Fifteen layers, eleven of them Deck's and four of them yours, and later beats earlier:

@layer
  deck.reset, deck.tokens, deck.type, deck.layout, deck.components,
  deck.mobile, deck.motion, deck.effects, deck.utilities, deck.rtl, deck.print,

  app.base, app.components, app.pages, app.overrides;

Why that works when specificity says it should not

The cascade decides a winner by asking a series of questions in order, and stopping at the first one that separates the candidates. Most people know the list as: importance, then specificity, then source order. Layers insert a step, and it is earlier than specificity:

The order the cascade asks its questions in
AskedQuestion
1stOrigin and importance — is one of them !important?
2ndWhich layer is it in? Later layer wins. Unlayered beats every layer.
3rdSpecificity — how detailed is the selector?
4thSource order — which came last in the file?

Specificity is question three. If question two already separated them, three is never asked. That is the whole mechanism: a single class in app.pages beats .card .card-header .card-title in deck.components, not because it is more specific — it is dramatically less — but because the argument was settled a step earlier.

The counter-intuitive part, and the one that trips people who already know layers: unlayered CSS beats every layer. A plain <style> block with no @layer around it wins against all fifteen. That is by design, so a page template can always have the last word, but it means putting your CSS in a layer makes it weaker against your own unlayered CSS, not stronger. Pick one convention and hold it.

Which of the four slots to use

All four beat all of Deck. They exist to order your own CSS against itself, which is the argument you will actually be having in six months.

The declared layer order, Deck's and yours
LayerForYours?
deck.reset Normalize and element defaults Deck
deck.tokens Custom properties only Deck
deck.type Headings, prose, links Deck
deck.layout Containers, stack, grid Deck
deck.components Buttons, forms, cards, charts Deck
deck.mobile Sheets, tab bar, icons Deck
deck.motion Transitions, entrances, view transitions Deck
deck.effects Gradients, 3D, surface treatments Deck
deck.utilities Single-purpose classes Deck
deck.rtl Direction fixes — must beat the utilities Deck
deck.print Paper Deck
app.base Your resets and element styles yours
app.components Your components, and your Deck overrides yours
app.pages Per-page and per-view tweaks yours
app.overrides The escape hatch, still not !important yours
app.base
Element styles and token overrides. A different body font, a custom --brand, your own <table> defaults. Things that should lose to your components.
app.components
Your own components, and your permanent changes to Deck's. If every card in your product has square corners, that rule lives here, once. This is where most of your CSS belongs.
app.pages
One page or one view. The hero on the marketing home page, a dashboard that needs a wider rail. Rules that would be wrong applied everywhere.
app.overrides
The escape hatch. Something has to win right now and you will come back to it. Keeping it as a named layer rather than an !important means it is greppable — you can find every one of them in a second, which is not true of importance scattered through a codebase.

When your rule still is not winning

Work down this list. It is in order of how often each one turns out to be the answer.

  1. Your stylesheet loads before Deck's. The order is declared by Deck, but a layer only exists once something declares it. If your CSS is the first to mention app.pages, it creates the layer at that point — before deck.* have been declared — and yours ends up first, which means it loses. Load Deck first, always. This is the single most common cause.
  2. You are competing with an unlayered rule. An inline style attribute, or a <style> block with no @layer. Both beat every layer. Dev tools show unlayered rules with no layer badge.
  3. The other rule is !important. Importance is question one, and it inverts the layer order — among !important declarations, the earlier layer wins, so importance in deck.reset beats importance in app.overrides. Deck's source contains 101 of them, and they are not scattered: 84 are inside @media print, 7 inside prefers-reduced-motion, and of the remaining 10, six reset positioning, shadows and clipping on table cells when the data grid restacks into cards on a narrow screen, two implement the .no-motion opt-out, and two style an item being dragged and the gap it leaves. If one of those is beating you, you are printing, the reader has asked for less motion, you are inside a restacked data grid, or you are restyling a drag — and in each case the rule is meant to win.
  4. You are setting a property the element does not have. gap on a non-flex parent, inset on a static element, width on an inline one. Nothing to do with the cascade; the rule is winning and doing nothing.
  5. It is a custom property, and you set it too low. --brand-600 resolves on :root, so overriding --hue-brand on a card changes nothing. See the theming guide.

In Chrome and Firefox dev tools, the Styles pane groups rules by layer and names the layer above each block. If your rule is there and struck through, the layer above it is the one beating you, and that is the whole diagnosis.

Loading your CSS into a layer

If you would rather not wrap every file, assign it at import time:

@import url("components.css") layer(app.components);
@import url("dashboard.css") layer(app.pages);

Or declare the layer once at the top of your entry stylesheet and let the files fall into it. With a bundler, the same thing done in the right order:

/* app.css — loaded after deck.css */
@layer app.base {
  :root { --hue-brand: 265; }
  body { font-family: "Inter var", var(--font-sans); }
}

@layer app.components {
  .card { border-radius: 0; }
  .btn  { text-transform: uppercase; letter-spacing: .04em; }
}

Deck ships each layer as a separate file under dist/layers/ as well as the combined stylesheet. If you are only using the layout primitives and none of the components, you can load fewer of them — but load them in the declared order, or you are back to the problem in point one.

When not to reach for a layer

  • When a token would do it. Changing --r-md restyles every rounded corner in the framework at once; a rule overriding .card { border-radius } fixes cards and leaves the popover, the modal and the input alone. Reach for the token first.
  • When a variant already exists. Before writing .card { box-shadow: none }, check whether that is .card-flush. Around a third of the overrides people write are a class Deck already ships.
  • When it belongs on the element. A one-off width is a style attribute, and that is fine — a single inline declaration is easier to find and delete than a rule in a file three directories away.
  • When you are fighting a component's structure. If the override list is longer than about four declarations, you do not want that component with changes; you want your own, and Deck's tokens make writing one cheap.