Guidelines

Anti-patterns

Every item here has cost real time on this codebase. None of them throw an error.

Fallback values

Don't
padding: var(--space-13, 13px);
Do
/* The scale has no 13. Rounded to 12. */
padding: var(--space-12);

A fallback hides a missing token instead of fixing it. An undefined custom property with no fallback drops the whole declaration — visibly broken, quickly found. With a fallback it silently ships the literal forever, while reading like a tokenised value. This is how a renamed token generation survived a rewrite here.

A fallback is for a token that genuinely may not be in scope yet — not a hedge against one that no longer exists. If a fallback is doing real work, the token is missing: declare it.

Raw values in product CSS

Don't
background: #292929;
color: rgba(255, 255, 255, 0.6);
Do
background: var(--color-surface-panel);
color: var(--color-text-secondary);

A raw hex in product CSS is an audit failure. Beyond the rule, it opts the element out of the contextual accent: a rule pinned to a violet literal stays violet on the green CMS surface, which is exactly the bug the token tiers exist to prevent.

Overriding the wrong token

Don't
.my-surface {
  --semantic-primary-default: green;
}
Do
.my-surface {
  /* Rebind what components actually consume. */
  --color-accent-primary: …;
  --color-focus-ring: …;
}

Custom properties inherit as computed values. A --color-* token declared at :root from a semantic source is substituted once, at the root. Overriding the source further down the tree changes nothing — the descendant inherits the already-computed value.

Scoped theming must rebind every token components consume, not just the source. Verify it by measuring getComputedStyle inside the scope.

Recomposing border rules

Don't
border: 1px solid var(--color-border-default);
Do
border: var(--border-default-rule);

The composed rules exist so a hairline is one decision. Recomposing it by hand is how one surface ends up 1px while its neighbour is 1.5px after a density pass.

Type shorthand without tracking

Don't
font: var(--text-overline);
Do
font: var(--text-overline);
letter-spacing: var(--font-tracking-label);

The font: shorthand resets letter-spacing. Any composite type token that needs tracking has to declare it separately, on the next line, every time.

Decoration in the tab order

Don't
<span className="card-preview">
  <Button>Primary</Button>
</span>
Do
<span className="card-preview" inert>
  <Button>Primary</Button>
</span>

A live preview used as decoration — the cards on the catalogue, for instance — puts real buttons and inputs in the tab order and the accessibility tree unless it is marked inert.

Relatedly: checkbox visuals rendered as aria-hidden spans inside another button must not be promoted to real role="checkbox" elements. Decorative is not interactive, and a control inside a control is invalid HTML.

Touching the renderer

src/builder/renderer/ and src/templates/ are off-limits to design-system work. They render the user's site content, not Sitefold's chrome. A sweep that tokenises them leaks Sitefold's CSS into customers' exported sites.

This is the one rule on this page with no “do” column: the correct action is to leave those directories alone.