Six ways to draw a keyboard key, from most understated to most emphatic. Every one rides the same base cap and the same glyph rules — the skin is the only variable. Toggle font and theme above; the native twin lives in KeycapGalleryView.swift.
Telling a user “press Option-Command-L” in prose is slow to parse and easy to get wrong across platforms. A keycap — the little rounded box around ⌘ — turns an instruction into something the eye recognises instantly as “a key you press.” We need this in four places that don’t share a rendering engine:
Two rules are already settled and non-negotiable (Apple Style Guide + Gruber, see design-keyboard-shortcuts.md): modifier order is Fn → Control → Option → Shift → Command, and glyphs concatenate with no separator () while spelled-out words take a + (Ctrl+Shift+S). This gallery is about the skin, not those rules.
The one source of truth for every surface. The Unicode glyph is the cross-platform constant; the SF Symbol is the native upgrade (weight/scale control, optical alignment) with the Unicode glyph as its guaranteed fallback.
| Key | Glyph | Unicode | SF Symbol | Non-Mac word |
|---|---|---|---|---|
| Command | ⌘ | U+2318 | command | — |
| Option / Alt | ⌥ | U+2325 | option | Alt |
| Shift | ⇧ | U+21E7 | shift | Shift |
| Control | ⌃ | U+2303 | control | Ctrl |
| Caps Lock | ⇪ | U+21EA | capslock | Caps Lock |
| Return | ↩ | U+21A9 | return | Enter |
| Enter (numpad) | ⌤ | U+2324 | return | Enter |
| Escape | ⎋ | U+238B | escape | Esc |
| Delete (back) | ⌫ | U+232B | delete.left | Backspace |
| Forward delete | ⌦ | U+2326 | delete.right | Delete |
| Tab | ⇥ | U+21E5 | arrow.right.to.line | Tab |
| Space | ␣ | U+2423 | space | Space |
| Arrows | ↑↓←→ | U+2191… | arrow.up … | ↑↓←→ |
| Fn / Globe | 🌐 | U+1F310 | globe | Fn |
Font gotcha: render the modifier glyphs in --bn-font-mono (SF Mono) or the system font — never Inter. Inter’s coverage of ⌘⌥⇧⌃ is incomplete, so the body font silently falls back per-glyph to a different face, and your caps end up mismatched. Every cap in this gallery uses the mono stack for the glyph.
Subtle fill, hairline border, no shadow. The quietest cap. Best for dense inline use where a raised key would be too loud — table cells, filter chips, sidebar hints.
.cap { display:inline-flex; align-items:center; justify-content:center;
min-width:1.7em; height:1.7em; padding:0 .42em;
font-family:var(--bn-font-mono); font-size:var(--bn-text-label); font-weight:500;
color:var(--bn-colour-text); border-radius:5px; }
.cap--flat { background:var(--bn-colour-badge-bg); border:1px solid var(--bn-colour-border); }A physical key: soft top-to-bottom gradient face, a darker bottom edge, and an inner top highlight. Reads as tactile without shouting. The right choice when the key is the content — the help modal, onboarding, “press this” moments.
.cap--raised {
background:linear-gradient(var(--cap-face), var(--cap-face-lo));
border:1px solid var(--bn-colour-border-hover);
box-shadow:0 1.5px 0 0 var(--cap-edge), /* bottom edge */
inset 0 1px 0 0 var(--cap-highlight); /* top highlight */
}
/* --cap-face / --cap-face-lo / --cap-highlight / --cap-edge:
add as light-dark() tokens; face is a hair lighter than badge-bg
so the top edge catches light. */Transparent face, border only. Vanishes into tinted panels (the inspector, a coloured callout) where a filled cap would fight the background. Also the lightest option that still reads as “a key.”
.cap--outline { background:transparent; border:1px solid var(--bn-colour-border-hover); }Dark pill on light (and light on dark). Highest emphasis — a single hero shortcut in an empty state or command-palette prompt (“press ⌘K”). Use sparingly; one per screen.
.cap--solid { background:var(--chip-bg); color:var(--chip-text);
border:1px solid transparent; font-weight:600; }
/* --chip-bg: light-dark(#1a1a1a, #e5e7eb); --chip-text: light-dark(#f4f4f5, #1a1a1a) */iA Writer’s trick: uniform ch-width caps in a monospace face, so single characters form true columns. When ten shortcuts stack in a help list, the letters line up as a scannable vertical index. This is Phase 4 of the shortcuts doc.
.cap--grid { background:var(--bn-colour-badge-bg); border:1px solid var(--bn-colour-border);
font-family:var(--bn-font-mono); min-width:2.2ch; padding:0 .3em; }
/* right-align the .keys column; monospace makes every single char the same width */No cap at all — just the glyph in muted/secondary colour. This is the native inline-shortcut idiom: menu items and list rows show right-aligned in grey, no box (macOS draws it this way everywhere). Also the CLI’s only option, since terminals can’t box-draw a cap cleanly.
.cap--bare { background:transparent; border:0; padding:0; min-width:0;
color:var(--bn-colour-muted); font-family:var(--bn-font-mono); }
/* native menus/rows: NEVER draw a cap — the OS shows bare grey glyphs, right-aligned */An orthogonal choice you make per surface, independent of skin: do modifiers fuse into the letter, or does each key get its own cap?
Rule of thumb: joined for compact reference (menus, tooltips, dense lists — matches how macOS itself renders shortcuts); split when teaching a combo for the first time, and always for spelled-out non-Mac words (Ctrl + S) where fusing would be unreadable.
To save the current view, press ⌘S. Star a quote with s, or select several and press t to tag them all at once. Press ? any time to see every shortcut.
Flat (A) at ~0.78rem sits on the text baseline without disturbing line-height. This is what the public docs and tooltips should use.
Today’s help-overlay.css is essentially skin B with a flat-bottom shadow. The refinement: gradient face + inner highlight so it reads as a real key in both themes, plus the mono glyph-safe font.
Recommendation. Ship B · Raised as the default keycap (help, teaching, docs headers), A · Flat for inline prose & tooltips, E · Mono grid for the aligned help list, and F · Bare for native menus and the CLI. C and D are situational (tinted panels; hero prompts). All six are one CSS file and one SwiftUI file, driven by the same glyph map in §2. Decisions frozen in docs/design-keycaps.md.