skip to content

Lint & CI Guardrails

Lint rules forbid raw color and spacing values and deprecated components, while token-usage checks run in review and CI. Interviewers probe where a guardrail should stop and an escape hatch begin.

part ofDesign systems & UX foundationsoverview, primer and where to startread it →
on this pageshow

questions

4

In a design system, why do teams lint against raw color and spacing values in product code, and what should the rule suggest instead?

level: juniorimportance: should knowfreq 40%

answer

  1. catch it while typing
  2. literals ignore themes
  3. one value, several roles
  4. suggest by role, not value
  5. allowlist the true exceptions

basics

~20 s

Raw values bypass tokens, so themes, modes and system updates skip them. A lint rule catches them as code is written and suggests the token for that role — not auto-applied, since one value can serve several roles.

solid answer

~50 s

A literal color code or spacing number in product code is invisible to the design system: it will not change in dark or high-contrast mode, will not follow a rebrand, and turns every future token change into a manual search. Review catches some of these, but inconsistently and late. A lint rule catches them **at authoring time and in CI**, on every change, the same way for every team. A good rule does more than say *no*: it suggests the **semantic token** for the role — text, border, surface, spacing step — and names the documentation. It should **suggest rather than auto-replace**, because the same value often belongs to several tokens: one gray might be both *subtle text* and *default border*, and picking by value alone silently encodes the wrong role. A small allowlist covers the genuine exceptions, such as zero.

code

pseudocode · 11 lines
pseudocode
rule no-raw-style-values:
  for each style value in the changed files:
    if its property is color, spacing, radius, type size or elevation
       and the value is a literal
       and the value is not on the allowlist:
         role = role implied by the property
         candidates = tokens for that role whose value matches
         report "Use a design token, not a raw value"
           suggest candidates if any, else the tokens for that role
           link the guidance page for that role
         autofix only if exactly one candidate

go deeper

for a junior

Recall why raw values are a problem — themes, modes and system changes skip them — and that the rule points you to the token for the role you are styling.

for a middle

Explain why suggestions should be by role rather than by value, why autofix is risky when one value maps to several tokens, and what belongs on the allowlist.

for a senior

Show how you would write rule messages that teach the system, handle data-driven values such as customer brand colors, and apply the same check on web and native code.

for a principal

Weigh strict enforcement against team autonomy, and decide which style decisions the system should own centrally versus leave to product teams.

## What the rule protects A design system expresses color, spacing, radius and type through **design tokens**: named values such as a subtle-text color or a medium spacing step. Components and product code reference the names; the system decides the values, and themes or modes remap them. A **raw value** — a literal color code or a bare spacing number written straight into product code — bypasses all of that: - **Themes and modes skip it.** A literal is correct in one theme only. Dark mode, a high-contrast mode or a white-label brand remaps every token around it and leaves the literal behind. - **System changes miss it.** When the system adjusts its error color for contrast, a hardcoded copy keeps the old one. - **It spreads.** The next engineer copies the nearby literal, and one exception becomes a pattern. - **It is hard to find later.** Searching for every place a value was used, across dozens of repositories, is slow and unreliable. ## Why a lint rule rather than review alone Code review catches raw values sometimes, depending on who reviews and how busy they are. A lint rule catches them **every time, for every team, at the moment the value is typed** — in the editor, then again in CI. Review attention is freed for questions a rule cannot answer: is this the right component, is this token's role right here? The rule is the floor; review is the judgement above it. The same rule typically runs in three places: - **In the editor**, as the engineer types, where fixing costs seconds. - **In CI**, on every change, so nothing depends on local setup. - **Across repositories**, as a count per team, so the system team can see where literals concentrate and which tokens are missing. ## What the rule should suggest The message matters as much as the check. Compare: | Rule message | Effect | |---|---| | Raw values are not allowed | Engineer guesses a token, or finds a way around the rule | | Use a token; nearest match by value: two candidates | Engineer picks one, possibly the wrong role | | Use a token for this role; for text on a surface consider subtle text or default text; see the color guidance page | Engineer picks by meaning, learns the system | Suggest **by role**, not only by value. The same resolved value frequently belongs to more than one token: a mid gray may be both the subtle-text color and the default-border color in the light theme, and differ between them in dark mode. Replacing by value alone picks one arbitrarily and bakes the wrong role in, which surfaces as a bug only when a mode changes. That is why many teams make this rule **suggest** rather than **autofix**, or autofix only when exactly one token matches and the property implies its role. ## What the rule checks, and what it allows The rule looks at style positions — color, spacing, radius, type size, elevation — and flags literal values there. Its **allowlist** covers values with no token meaning: 1. Zero, which means *none* rather than a spacing step. 2. Values that follow from the platform or content rather than the system, such as sizing to an image's own dimensions. 3. Explicit, reasoned exceptions, handled through a suppression the team can see and review. The same idea applies on native mobile: literal colors or spacing in a screen's layout code bypass the token output generated for that platform, and a platform-appropriate lint rule flags them the same way. ## An example in an e-signature product The signing page highlights each field the signer must complete. An engineer writes a literal yellow for the highlight and a literal spacing number around the field. The rule flags both, suggests the highlight-surface token and the small spacing step, and links to the guidance. In high-contrast mode the highlight now follows the system's high-contrast palette instead of staying a pale yellow that a low-vision signer cannot see. One exception is real: the **sender's brand color**, which the sending company configures in its account and which the signing page shows in its header. That value is **data**, not a design decision, so it is read from configuration and passed through a sanctioned mechanism rather than written as a literal — the rule has nothing to flag.

  • Why not autofix every raw value to the token with the same value?
    Because one value often belongs to several tokens with different roles. A gray that is both the subtle-text and the border color in one theme may diverge in another. An autofix by value picks one arbitrarily and encodes the wrong role, which only shows when a mode or the system changes. Suggest by role, and autofix only when exactly one candidate fits.
  • How should the rule treat a value that comes from customer configuration, such as a sender's brand color?
    It is data, not a design decision, so it should not be a literal at all: read it from configuration and apply it through the mechanism the system provides for runtime brand values. Then there is nothing to flag. If code writes it as a literal, that is a real finding, not a false positive.

saying these in an interview costs you the question

  • Raw values are fine as long as they match the token's current value.
  • Code review alone reliably catches raw values, so lint is redundant.
  • The rule should autofix every literal to whichever token has that value.
  • A lint rule that only says no is enough; engineers will find the token.
  • Every numeric value in styling code, including zero, must be a token.
open as a page

In a design system, where should a guardrail stop and an escape hatch begin, and what makes an escape hatch safe rather than a loophole?

level: middleimportance: should knowfreq 35%

basics

~10 s

Block only what is never legitimate; elsewhere offer an escape hatch, or people route around the rule unseen. A safe hatch is explicit, local and reasoned, visible to the system team, and revisited.

open as a page

How would you roll out a new design-system lint rule, such as banning a deprecated component, across forty product repositories without breaking everyone's builds?

level: seniorimportance: should knowfreq 32%

basics

~10 s

Ship the rule as a warning and measure; then block only new violations against a per-repository baseline; then burn the baseline down and block everything by a date tied to the component's removal.

open as a page

An e-signature product has accumulated 300 suppressions of the design system's raw-color lint rule; how would you review them and stop the count from growing?

level: seniorimportance: should knowfreq 28%

basics

~20 s

Group suppressions by reason, not file: system gaps, an over-broad rule, data-driven values, debt. Fix gaps and the rule first, burn down the debt, then require reasons, route new suppressions for review and fail CI if the count rises.

open as a page