Skip to main content
A Singapore Government Agency Website
COL-1L1 · mandatoryhybridColour

Primary actions and brand moments use the product's own primary brand colour (or its ramp)

Fails when: primary CTA in a colour that is not the product's primary · one product's primary used inside another product's surface · a light-hue product primary (orange/amber/lime/yellow) paired with white label text that fails A11Y-1 (use the scale's dark contrast step)

Requirement

Each product anchors its primary actions and brand moments in its own primary brand colour and that colour's ramp. Do not import another product's primary for emphasis on a different product's surface. The active product is established in Phase 1 of the design-ui loop.

Per-product primary

Each product declares its primary in its own DESIGN.md: the brand colour, the token that carries it, and its value. For example, a product might record a brand blue #0064FF on its --primary token. A primary marked "(proposed)" in its DESIGN.md awaits the design lead's settle.

Foreground pairing (A11Y-1)

A product's primary is a background for its label text, so the primary and its paired foreground must clear A11Y-1 together — this is not automatic, and a light or mid-luminance primary cannot carry white text.

  • Radix orange-9 #F76B15 takes a dark foreground, not white. White on #F76B15 is 2.97:1 — it fails the 4.5:1 body floor (and the 3:1 large-text floor). Pair it with the scale's high-contrast dark step (Radix orange "contrast" / step-12, a near-black brown) → ~5.7:1. This is the Radix convention for the light-hue solids (orange, amber, lime, yellow): step-9 is a dark-text background, not a white-text one.
  • A brand blue such as #0064FF and Radix indigo-9 take white text (both clear AA).

When a product adopts a light-hue primary, the brand variant's --primary-foreground must be the dark contrast colour, and the evaluator re-checks label contrast.

Same-hue collision (A11Y-1)

Applying COL-1 can create an A11Y-1 failure: when the product primary would sit on a same-hue field, the button's fill can lose the 3:1 boundary against its background. The foreseeable case is an orange primary on an orange tint or splash (orange-on-orange — e.g. a tag-*-50 topic tint or the login splash). Before recolouring a primary onto a coloured field:

  • change the container so the button sits on a neutral / near-white surface (the typical fix is moving a login CTA into a white card — orange-on-white clears AA), or
  • keep the action neutral on that surface and record the deviation.

Never recolour a primary onto a same-hue field without re-checking the 3:1 UI-component boundary; a mechanical "make it brand" that ignores this ships an inaccessible control.

Rationale

The portfolio is independent products: one product's primary might be orange, another's indigo. A rule that pointed every product at a single shared blue would mark a correct orange CTA as a violation. Instead, each product's primary is its own brand colour. People recognise a product partly by its colour; forcing a single blue across the portfolio erases that identity and mis-grades correct work.

How to verify

Deterministic half: the active product's primary CTA and brand-moment colours resolve to that product's primary token (not a raw hex bypassing the token layer). The checks/token-audit allowlist already knows the product's --color-* names from its CSS — see plan 011 for the project-token-aware mechanism.

Judgment half (why this control is hybrid, not deterministic): the colour resolving to a valid token passes the mechanical half trivially — the real question is whether the view's single primary action (per CMP-5) carries the product primary, or a legitimately-tokenised neutral is sitting where the brand belongs. The evaluator confirms: (1) the one primary action per view resolves to the product primary; (2) its label/background pairing clears A11Y-1 (see Foreground pairing); (3) no other product's primary appears for emphasis (an orange from one product on a blue product's screen is a finding, and the reverse is too).

Also available as Markdown · catalog.yaml