Skip to content
Foundations

Colour

One indigo accent, a six-step ink ladder, and four status hues that carry no planning content.

The accent

Selected5
--gd-accent-ink is measured on TWO grounds: the page, where it is a link, and --gd-accent-wash, where it is the label of a latched control. In the dark it is #A9A4F2 — 7.83:1 on the surface and 4.77:1 on the wash.
Code
<GdButton variant="primary">Primary action</GdButton>
<GdButton variant="ghost">Ghost / link</GdButton>
<GdChip tone="accent">Selected</GdChip>
<GdBadge :value="5" />
accent
--gd-accent#4740DB#5F57E3

filled primary buttons, the selected tab rule, the focus ring AA-nontext on --gd-surface

--gd-accent-hover#362FBF#6D66E8

hover on a filled accent surface

--gd-accent-pressed#2F2AA3#4740DB

pressed

--gd-accent-ink#362FBF#A9A4F2

accent TEXT on a light ground — links, ghost buttons, the active contents item, and the label of a latched control on `accent-wash`. The accent proper is 1.9:1 on charcoal and cannot be a link colour in the dark, which is why this forks from `accent` AA on --gd-surfaceAA on --gd-accent-wash

--gd-accent-wash#EEF0FF#3B3866

the accent surface — selected rows, count badges, accent chips

--gd-accent-wash-soft#F7F7FD#1C1B2E

the softest accent ground, for editorial callouts where the full wash shouts

--gd-accent-tintrgba(71, 64, 219, .25)rgba(140, 134, 240, .3)

the border of an accent-washed thing

--gd-accent-ringrgba(71, 64, 219, .14)rgba(140, 134, 240, .22)

a soft accent halo — the selected parcel's glow on the map, and emphasis behind a chosen thing. NOT a focus indicator: focus is `border-focus-ring` everywhere, because a 14%-alpha wash cannot carry WCAG 1.4.11's 3:1

Ink ladder

ink
--gd-ink#292A2E#F2F2F4

headlines and primary text AA on --gd-surface

--gd-ink-body#3D4047#C9CBD2

body copy — the readable default for prose AA on --gd-surface

--gd-ink-secondary#5C6068#9A9EA8

supporting sentences beneath a title AA on --gd-surface

--gd-ink-muted#6B6E75#8A8E96

eyebrows, captions, hints, units — the smallest type that still carries meaning, so it must still clear AA AA on --gd-surface

--gd-ink-faint#8A8E96#74777F

LARGE type and non-text marks only (≥18.66px, or ≥14px bold). Never prose — see the header note AA-large on --gd-surface

--gd-ink-placeholder#9A9EA8#5C606B

a disabled control's label, or a placeholder that only repeats its visible label. Fails AA by design and may carry nothing a user needs

--gd-ink-inverse#FFFFFF#F2F2F4

text on `surface-inverse` AA on --gd-surface-inverse

--gd-ink-on-solid#FFFFFF#101116

text on `surface-solid` — the neutral CTA's label AA on --gd-surface-solid

--gd-ink-on-accent#FFFFFF#FFFFFF

text on a filled accent surface — white in both themes AA on --gd-accent

--gd-ink-on-accent-body#E9E8FB#ECEBFC

body copy on a filled accent band (white at 88%, composited) AA on --gd-accent

--gd-ink-on-accent-muted#CBCAF5#DCDAF9

LARGE type (≥18.66px, or ≥14px bold) and non-text marks on an accent band — the floor of the ladder (white at 72% / 78%, composited) AA-large on --gd-accent

Surfaces & lines

surface
--gd-surface#FFFFFF#17181E

the default ground for cards, panels and rows

--gd-surface-page#F4F4F2#101116

the page behind everything — warm off-white in the light, the lowest charcoal in the dark

--gd-surface-subtle#FBFBFC#1D1F26

table headers and glyph-tile grounds: a surface that is not quite the card it sits on

--gd-surface-raised#FFFFFF#1D1F26

a surface lifted off its ground — a floating pill, a popover, a menu. In light the shadow carries it and this equals `surface`; in dark the surface itself has to

--gd-surface-sunken#F4F4F7#101116

recessed groups — segmented tracks, icon tiles, layer lists. Sunken is the one people get wrong: a GROUP of things is recessed, not raised

--gd-surface-hover#FAFAFB#24262E

pointer over a row. Hover is a SURFACE change, never a shadow change — a list that lifts under the pointer shimmers

--gd-surface-pressed#F4F4F7#2B2D36

the moment of the click

--gd-surface-selected#EEF0FF#3B3866

the chosen row or option

--gd-surface-disabled#F0F1F4#212329

an inert control's fill

--gd-surface-inverse#14151A#24262E

toasts, code blocks, the ink CTA band — a surface that is dark in BOTH themes

--gd-surface-solid#14151A#F2F2F4

the neutral high-emphasis CTA fill — near-black on a light page, near-white on a dark one. NOT `surface-inverse`, which stays dark in both

--gd-surface-map#F0EEE9#22242B

the basemap ground. Deliberately not black in the dark, so parcel strokes and overlay fills keep the relationship to their ground that they have in daylight

--gd-surface-scrimrgba(20, 21, 26, 0.42)rgba(0, 0, 0, 0.55)

the dimmer behind modal surfaces — dialog, drawer, palette. Deeper in the dark, because 42% charcoal over a charcoal page barely reads as a veil

--gd-surface-on-accentrgba(255, 255, 255, .10)rgba(255, 255, 255, .10)

a card or row sitting ON a filled accent band

--gd-surface-on-accent-strongrgba(255, 255, 255, .16)rgba(255, 255, 255, .16)

a chip on a filled accent band — one step up from the card it sits in

line
--gd-line#E8E9ED#2B2D36

THE border — cards, inputs, panels

--gd-line-hairline#F0F1F4#212329

a divider INSIDE a container that already has a border

--gd-line-soft#ECEEF1#212329

the second line weight — nested rules, tracks

--gd-line-strong#D7D8DD#3A3D48

a firmer boundary, and every dashed edge (upload zones, clear-space diagrams)

--gd-line-hover#C9CCD2#454955

border under the pointer

--gd-line-on-accentrgba(255, 255, 255, .18)rgba(255, 255, 255, .18)

the border of a card sitting on a filled accent band

--gd-line-on-accent-hairlinergba(255, 255, 255, .05)rgba(255, 255, 255, .05)

the survey grid drawn over a filled accent band — the faintest mark the system has

--gd-line-control#8A8E96#6E7280

a boundary that IS the control — an unchecked box, an off switch track. WCAG 1.4.11 applies and the design's #E0E1E6 is 1.2:1 here: an off switch drawn in it is invisible until it is already on, so you cannot tell the control exists. Honest caveat: iOS and Material ship light tracks too and the knob's shadow is SOME affordance — 3:1 is the defensible reading, not the only one AA-nontext on --gd-surface

Rules

  • Use --gd-accent for fills — filled buttons, the selected tab rule, the focus ring. Never for type.
  • Use --gd-accent-ink for accent type — links, ghost buttons, the active contents item, the label of a latched control on --gd-accent-wash. It is the only accent value that may be set in text.
  • Do not add a second accent. That is a design change, not a styling choice.
  • Change the SURFACE for hover and pressed, never the shadow. A list row darkens; it does not lift.
  • Reach for --gd-surface-raised when a thing genuinely floats — a pill, a popover, a menu. Reaching for --gd-surface instead lands at 1.00 against its own ground in the dark.
  • Use --gd-surface-sunken for a GROUP — a segmented track, a layers list, an icon-tile grid. A group of things is recessed, not raised.
  • Use --gd-surface-solid, not --gd-surface-inverse, for a neutral CTA. solid flips with the theme; inverse is deliberately dark in both.
  • Never set type in a status solid. Each hue's ink is the only member of the quartet that may carry a sentence.
  • Take the whole quartet or none of it — solid, ink, wash, tint travel together.
  • Never use --gd-ink-faint for prose. Large type (≥18.66px, or ≥14px bold) and non-text marks only.
  • If a label needs to recede, reduce its size or weight — never its contrast.

Behavior & Anatomy

One accent

Exactly one. Indigo was chosen because it collides with no status hue and no zone hue on the map. A second accent is a design change, not a styling choice.

The chrome around it is achromatic on purpose: the map already carries a council's zone cartography, so every hue spent on chrome is a hue taken away from something that means something.

Why the accent forks into a fill and an ink

#4740DB on charcoal is 1.9:1 and cannot be a link colour, so the dark theme needs a lifted step for text while the filled button keeps a bolder indigo underneath white. Two tokens rather than one is what that fork costs.

Why hover and pressed are surface changes

Hover and pressed are SURFACE changes, not shadow changes. A list row darkens; it does not lift. That keeps rows quiet in a long list and stops a panel shimmering as the pointer crosses it.

Why --gd-surface-raised exists

Elevation is carried by different things in the two themes, and only one of them is a shadow. On a light ground a black shadow does the whole job, so the raised surface stays white and this token is a no-op. On a dark ground it does not: --gd-shadow-md is rgba(0,0,0,.45), which over near-black is very nearly nothing — elevation in the dark is carried by the SURFACE getting lighter as it rises. This token is what says that. Without it a floating component has only --gd-surface to reach for, which is 1.00 against its own ground.

The step is chosen against the light theme's own precedent: a white pill on the page tint is 1.101, and #1D1F26 on #17181E is 1.076 — the same relationship, not a new one.

Why solid is not inverse

--gd-surface-inverse does not flip and must not: a toast, a code block and the footer band are dark in both themes on purpose, and a footer that turned white at night would be the brightest thing on a dark page. GdButton variant="solid" borrowed that token anyway, and in the dark theme the result was a #24262E pill on a #17181E bar — 1.3:1, which fails WCAG 1.4.11 for a control's boundary and, more plainly, meant the landing page's one neutral call to action had no visible edge. "Inverse of the page" is what --gd-surface-solid actually means, so it is near-black on a light page and near-white on a dark one.

Where contrast decides the value

Three tokens are pinned by measurement rather than by eye, and each one is a place where the pleasing value and the passing value are not the same. The measurement wins; the rule is that a label recedes by size or weight, never by contrast.

Caution: Eyebrows and captions do not go paler than 5:1

--gd-ink-muted is #6B6E75, 5.11:1 on white. The obvious lighter grey #8A8E96 is 3.29:1, which is under AA for text that is not large — and eyebrows are 10–11px. That value exists as --gd-ink-faint, restricted to large text and non-text marks.

Caution: The off switch track is 3.29:1, not a hairline

--gd-line-control carries the OFF state of every switch. A pale #E0E1E6 track is 1.2:1 against the surface: the control is invisible until it is already on, so you cannot tell it is there — which WCAG 1.4.11 does not allow for something that is the control. Honest caveat: iOS and Material ship light tracks too, and the knob's shadow is some affordance — 3:1 is the defensible reading, not the only one.

The third is caution. #C9A21A is 2.41:1 on white, so the caution solid is the darker #A8850F; #C9A21A appears only as a callout's left rule, where the heading ink beside it carries the meaning.

Navigate

Esc