Skip to content
Components

Button

Every clickable commitment renders through here. It picks the right element for the job and makes loading safe.

Variants

Primary is reserved for the single most important next step per VIEW — not per card, not per section. Two primaries on a screen means neither is.

The variant carries colour ONLY. Geometry lives on the base, so a variant can never drift in size — link is the one exception, and the exception is the point.

Solid

The filled neutral-ink CTA, for a view whose accent is already spoken for. The canonical pair is a nav: link on "Sign in", solid on "Open the map". An accent primary in that slot fights whatever else on the page is already accent-coloured.

It paints itself with --gd-surface-solid and --gd-ink-on-solid — the one neutral pair that genuinely flips with the theme, near-black on a light page and near-white on a dark one. Not --gd-surface-inverse, which is deliberately dark in both themes so that a toast, a code block and the footer band stay dark at night. Borrowing inverse here is what shipped a 1.3:1 pill on a charcoal bar — a control with no visible boundary, which is a WCAG 1.4.11 failure and, more plainly, a call to action nobody could see.

There is no --gd-surface-solid-hover to hunt for: solid mixes the ink that already sits ON the fill back over it, which moves the pill toward its own label — lighter on a light page, darker on a dark one. One color-mix rule lifts the hover in each theme without a second token.

Link

A real button that reads as a link, for actions that live in a sentence. An <a href="#"> that performs an action misreports itself to everyone navigating by links; this variant exists so authors stop reaching for that anchor.

A six-digit code is on its way. , or .

The only variant that changes GEOMETRY rather than colour — the box comes off, because a link wearing a padding box is a button in a costume. The size ladder still sets the type size, so it can be told to match the sentence it sits in.

Sizes

Icon buttons

34px is under the 44px touch minimum, so an icon button gets the target back with a pseudo-element rather than by growing. The visual size is a design decision and the tap size is an accessibility one — they are allowed to differ.

States

Loading keeps the variant's own colour — a button mid-action has not become a different button, and repainting it grey reads as failure.

Block

Which element it renders

The part hand-rolled buttons keep getting wrong.

<GdButton to="/library"> → NuxtLink <GdButton href="https://…"> → <a> <GdButton> → <button type="button">

A div with a click handler is not on the table: it answers Enter, silently ignores Space — which every native button activates on — and is unreachable by keyboard without a tabindex nobody remembers to add. Real navigation also means middle-click and open-in-new-tab work, which a click handler cannot give you.

Navigate

Esc