defuss-shadcn / Primitives / kbd
KbdATM
A keyboard key - for a shortcut in a menu item, a tooltip, a command palette row or a sentence. Built on the native <kbd> element; a combination is a <kbd> holding one <kbd> per key, exactly as the HTML spec prescribes, so the markup already says "these keys together". The thicker bottom edge is what makes a rectangle read as a key. CSS only.
On this page (10)
§Keys
One <kbd class='kbd'> per key - write the symbol you want read: ⌘, ⇧, ⌥, Ctrl, Enter, Esc.
§Combination
A chord is a <kbd class='kbd-group'> holding one <kbd class='kbd'> per key - the nesting the HTML spec prescribes; the outer element is not a key itself.
§With a separator
data-separator on the group draws a + in the gap between keys - outside the key boxes, never inside one.
§Sizes
data-size sm (inside buttons and menu items) · default · lg - or any size through --kbd-size.
§In a control
Inside a button the key drops its own surface and borrows the button's text colour through color-mix - one rule for every variant, legible on the dark primary button and the outline one alike.
§In prose
Keys sit on the text baseline, so they read inside a sentence.
Shortcuts in dropdown items blend in the same way - the key takes the item's colour, including the highlighted and destructive states.
§In a tooltip
On a tooltip's inverted surface the key borrows the tooltip's colours too - hover or focus the button.
§Decorative shortcuts
No ARIA: <kbd> carries the meaning and its text is the label. When the shortcut only decorates a control that already names its action, hide it with aria-hidden.
§CSS view file
/* -- Kbd ---------------------------------------------------------- *//* Keyboard keys. Built on the native <kbd> element; a combination is a *//* <kbd> holding one <kbd> per key, which is what the HTML spec *//* prescribes — so the markup already says "these keys together". */@layer components { .kbd { --kbd-size: 1.375rem; display: inline-grid; place-items: center; min-width: var(--kbd-size); height: var(--kbd-size); padding-inline: 0.375rem; border: 1px solid var(--border); /* the bottom edge is thicker, which is what reads as a physical key */ border-bottom-width: 2px; border-radius: var(--radius-sm); background: var(--muted); color: var(--muted-foreground); font-family: var(--font-mono); font-size: 0.75rem; font-weight: 500; line-height: 1; white-space: nowrap; vertical-align: middle; &[data-size="sm"] { --kbd-size: 1.125rem; padding-inline: 0.25rem; font-size: 0.6875rem; } &[data-size="lg"] { --kbd-size: 1.75rem; padding-inline: 0.5rem; font-size: 0.875rem; } } /* A key combination: an outer <kbd> holding one <kbd> per key. The outer element is not a key itself, so it keeps none of the key styling. */ .kbd-group { display: inline-flex; align-items: center; gap: 0.25rem; vertical-align: middle; font-family: inherit; background: none; border: 0; padding: 0; } /* Optional "+" between keys. Absolutely placed in the gap so it never sits inside a key's box. */ .kbd-group[data-separator] { gap: 0.875rem; & > .kbd + .kbd { position: relative; &::before { content: "+"; position: absolute; inset-inline-start: -0.625rem; color: var(--muted-foreground); font-size: 0.6875rem; line-height: 1; pointer-events: none; } } } /* Inside a control - or on a tooltip's inverted surface - the key should not out-shout its label: it drops its own surface and borrows the text colour around it. */ .btn .kbd, .command-item .kbd, .dropdown-item .kbd, .menubar-trigger .kbd, .tooltip .kbd { background: transparent; border-color: color-mix(in oklch, currentColor 25%, transparent); color: inherit; opacity: 0.75; }}@media (prefers-reduced-motion: reduce) { @layer components { .kbd, .kbd *, .kbd::before, .kbd::after, .kbd *::before, .kbd *::after, .kbd-group, .kbd-group::before, .kbd-group::after { transition: none; animation: none; } }}@media (prefers-contrast: more) { @layer components { .kbd { border-color: var(--foreground); color: var(--foreground); } }}@media (forced-colors: active) { @layer components { .kbd { border-color: ButtonText; color: ButtonText; background: ButtonFace; } .kbd-group[data-separator] > .kbd + .kbd::before { color: ButtonText; } }}Comments, ideas or improvements? Edit this page's source on GitHub