Atom

Key

A picture of a keyboard key, 307 instances on 51 screens since the second form was folded in on 2026-08-26. It is the only component in this system that reads no colour role at all: it takes the colour of whatever it stands in and draws its border out of that. That is the component, not an omission, and it is why the same element works on a filled amber button and inside a quiet list row without a single variant.

Written in round 0 beside btn rather than in its own round. The button's dominant content form is label plus key, 83 of 157, and a reference component that cannot show its own dominant form is not a reference.

Anatomy

Enter a Esc

Shown above at 20px so the drawing is legible. Its real size is 11px, --size-xs, which is the floor of the type scale, and it never renders larger anywhere in the product.

Variants

None, and that is the finding rather than an empty section. The axis a reader expects here is the host, and the host is precisely what the component refuses to encode: it inherits.

Amend m Accept a Cancel Esc Not a threat 1 Benign, expected 2

Five hosts, one declaration, five different renderings. The key on the accent button is amber ink on amber ground; the key on a quiet button recedes with the button; the key in a list row takes the row's ink. Nothing in key.css knows any of that.

Axis a reader looks forValuesWhy the cell is empty
host–Prohibited. A .key--on-primary would be the host's name written into the guest, and it would need a new value every time a new host appears. currentColor covers every host that will ever exist, including the ones not drawn yet
size–Prohibited. 11px is the floor of the scale and the key is always at it. Something that needs to be bigger is not a key, it is the control the key is on
emphasis–Prohibited. The dimming is the host's: .btn .key sits at .75 and .btn--primary .key at .85, and both lines live in btn.css because they are the button's decision about its guest, not the key's about itself

One consolidation happened here and it is the only appearance change step 2 decided. kit.css declared the key twice: currentColor inside .btn and a fixed rule colour inside .opt. So on a filled button the key belonged to the button, and inside an option it carried a line that belonged to nothing. 39 places on the coloured screens and 45 across the whole product take the host's colour now. The row is in tokens-audit.md under consolidated drift.

When to use it

Wherever an action has a keyboard shortcut, and nowhere else. Design principle 3 says the override is one key and it teaches, so the letter is the real interface and the pointer is the fallback: the key on a button is how the analyst learns to stop using the mouse. 88 of the 307 stand on buttons for exactly that reason, 147 stand three at a time under every queue, 45 stand in the reject dialog's option list, and 22 stand on the keyboard map, which is the one screen where a key is the subject rather than a hint.

The empty slot collapses by itself. The shell's foot() always emits the span, so before .key:empty a control with no shortcut wore a bordered blank that reads as a key nobody can press. Found at stage 07 and carried here as part of the component rather than as a patch on the button.

Rule and anti-rule

Do

Inside the control the shortcut operates. The key sits last, after the label, and the label still says what happens: reading the button without the key must lose nothing.

Do not

e

Never alone as something to press. A key is a picture, not a control: it has no hover, no focus and nothing happens when it is clicked. Something pressable is btn. Something that reports a condition is state.

States

It has none, and it is not interactive. No hover, no active, no focus, no out of reach. Inventing one would be the same defect as inventing a role: a hover on a key would say it can be pointed at, and it cannot.

What looks like a state on the screens is the host changing under it. In the reject dialog a chosen option inverts, and the key inverts with it, because the key reads currentColor and the option repainted its own ink. That rule belongs to opt and lives in its file, not here. Shown below rather than described, so the mechanism is visible:

Not a threat 1 Benign, expected 2

What it reads, and where it stands

No colour role, and this is the only row in the system that says that. Geometry only: --font-mono, --size-xs, --radius-ui at zero, --space-1. The border is currentColor and the ink is inherited, so the component needs no pair in the light theme and gets one for free.

Copy this

<kbd class="key">a</kbd> <a class="btn" href="...">Amend <kbd class="key">m</kbd></a> <span class="key key--none">nowhere yet</span>

The file is design/system/components/key.css, six declarations and one of them is :empty.

The open row is closed, and a count closed it

.qfoot kbd did the same job in a different form, and it stayed open for five stages because each form looked right on its own screen. It took secondary ink rather than the host's and drew its border from --line-control rather than currentColor. What settled it was counting the two: 147 against 160. The drawing is this component's now, and the collapse ran both ways, because the element the foot was using is the right one: kbd everywhere, and the only thing that changed on a screen is the border in that foot, from the fixed rule to the ink the strip already carries. The answer had been sitting on the frozen page the whole time: design/kit/kit.html, the stage 07 smoke test nothing may edit, writes kbd class="key" and always has.

Three places it stands