Primitives Layout and display
Keyboard key
The native <kbd> element drawn as a key. A combination nests one <kbd> per key, and the stylesheet draws the keys rather than the group around them. Tier 0.
- Tier
- 0 · Native
- Behavior JS
- 0 B brotli · 0 B gzip ·
fixtures/· methodresults.json (native-only) - Platform features
<kbd>,nested <kbd>,:has()- Shared listeners
- none
- Per-instance listeners
- none
- Lazy state
- none
- Native base
<kbd>
On this page
Example
Press / to filter the sidebar and Esc to clear it.
Open the command palette with Ctrl+K, or Cmd+K on macOS.
Move between tabs with ← and →.
<p>Press <kbd data-slean="kbd">/</kbd> to filter the sidebar and
<kbd data-slean="kbd">Esc</kbd> to clear it.</p>
<p>Open the command palette with
<kbd data-slean="kbd"><kbd>Ctrl</kbd>+<kbd>K</kbd></kbd>, or
<kbd data-slean="kbd"><kbd>Cmd</kbd>+<kbd>K</kbd></kbd> on macOS.</p>
<p>Move between tabs with <kbd data-slean="kbd">←</kbd> and <kbd data-slean="kbd">→</kbd>.</p><script lang="ts">
import '@svelte-lean/styles/kbd.css';
</script>
<p>Press <kbd data-slean="kbd">/</kbd> to filter the sidebar and
<kbd data-slean="kbd">Esc</kbd> to clear it.</p>
<p>Open the command palette with
<kbd data-slean="kbd"><kbd>Ctrl</kbd>+<kbd>K</kbd></kbd>, or
<kbd data-slean="kbd"><kbd>Cmd</kbd>+<kbd>K</kbd></kbd> on macOS.</p>
<p>Move between tabs with <kbd data-slean="kbd">←</kbd> and <kbd data-slean="kbd">→</kbd>.</p>Why this implementation exists
<kbd> is HTML for user input, and nesting it is HTML for a combination: the outer element is the whole input, the inner ones are the keys. Browsers and screen readers read it as text. The primitive is the drawing of a key and the rule that a combination is drawn key by key.
Where a shortcut exists, it belongs to a control. aria-keyshortcuts on that control announces it; the <kbd> only shows it.
The browser owns
- the <kbd> element as user input, and the nested form of a key combination
- the text screen readers read out
Svelte Lean owns
- kbd.css: the key box with a thicker bottom edge, the combination drawn key by key
- the contract: key names as text, aria-keyshortcuts on the control
- documentation
Usage
The markup needs no package. Install @svelte-lean/styles for the stylesheet; @svelte-lean/primitives adds the typed contract and nothing at runtime.
npm install @svelte-lean/stylespnpm add @svelte-lean/stylesyarn add @svelte-lean/stylesbun add @svelte-lean/stylesimport '@svelte-lean/styles/kbd.css';Write key names as text: Ctrl, Shift, Enter. Symbols such as ⌘ and ⇧ are read differently, or not at all, by screen readers; when you show one, add the name as visually hidden text.
For a combination, put one <kbd> per key inside the root and keep the plus signs as text between them.
Anatomy
| Part | Element | data-slean-part | Required | Notes |
|---|---|---|---|---|
| root | <kbd data-slean="kbd"> | – | yes | One key as text, or one <kbd> per key for a combination. |
| key | <kbd> inside the root | – | no | Drawn as keys; the root around them draws nothing. |
Runtime profile
Tier 0: the key has no behavior module, the Vite plugin maps kbd to no module, and the native-only fixture proves the path ships no Svelte Lean JavaScript.
Accessibility contract
<kbd>is text; screen readers read the key names in order, the plus signs included.- The shortcut itself is announced from
aria-keyshortcutson the control it activates, not from the<kbd>. - Keys are not focusable and have no role.
Keyboard
| Key | When | Result |
|---|---|---|
| Tab | anywhere | Passes the key by: it is text, not a control |
Platform features
| Feature | Baseline | Outside the target |
|---|---|---|
<kbd> | Widely available | Not applicable within the support policy |
:has() | Baseline 2023, widely available since June 2026 | A combination is drawn as one box around its keys |
Without JavaScript
Fully functional.
Server rendering
Static HTML.
Before hydration
Nothing is attached.
Styling
kbd.css draws a key as a bordered box with a thicker bottom edge in the page font, and, through :has(), draws the inner keys of a combination instead of the root. The tokens it reads:
| Token | Default (light) | Applies to |
|---|---|---|
--slean-border-strong | var(--slean-neutral-8) | border |
--slean-radius-sm | 0.375rem | border-radius |
--slean-surface | oklch(100% 0 0) | background |
--slean-fg | var(--slean-neutral-12) | color |
--slean-text-xs | 0.75rem | font-size |
--slean-font-weight-medium | 500 | font-weight |
--slean-fg-muted | var(--slean-neutral-11) | color |
Compatibility notes
<kbd> is widely available. :has() is Baseline 2023 and widely available since June 2026; without it a combination is drawn as one box around its keys.
Examples
Shortcut on a control
A key drawn next to a button tells sighted readers the shortcut. For assistive technology the
shortcut is aria-keyshortcuts on the control itself; the handler that makes the keys
work is the application's.
<!-- The shortcut is announced on the control; the <kbd> only shows it. The keydown
handler that makes Ctrl+K open the palette is the application's. -->
<button type="button" data-slean="button" data-variant="outline" aria-keyshortcuts="Control+K">
Search
<kbd data-slean="kbd"><kbd>Ctrl</kbd>+<kbd>K</kbd></kbd>
</button>Testing
apps/playground/tests/primitives/display.spec.tsPlaywright: open state, exclusive groups, stretched links, roles and names, carousel scrolling, axepackages/styles/testsstatic checks of the stylesheet (layers, tokens, specificity, dark parity)
Source
packages/primitives/src/kbd/contract.mdthe contract in prose: semantics, keyboard, focus, ARIA, SSR, JS-disabled behaviorpackages/primitives/src/kbd/contract.tstyped constants: name, tier, base, parts, options, eventspackages/styles/css/kbd.cssthe optional stylesheet