The HTML kbd tag: show keyboard keys, program output and variables

Wrap a key or anything the user types in kbd, what a program prints back in samp, and a placeholder name in var. A few CSS lines turn kbd into a key cap.

The <kbd> tag marks user input: a key to press, a shortcut, or text to type. Write Press <kbd>Ctrl</kbd>+<kbd>C</kbd> and the key names are marked as input.

Two sibling tags cover the rest: <samp> for what a program prints back, and <var> for a name that stands for a value.

Tick the box below to see which words each tag wraps.

Live exampletry it here, then copy the code
Share it as a link
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>kbd, samp and var</title>
<style>
  body { margin: 0; padding: 16px; font-family: system-ui, sans-serif; background: #f4f5f7; color: #1d2330; }
  .box { background: #fff; border-radius: 10px; padding: 12px 14px; margin-bottom: 12px; box-shadow: 0 2px 8px rgba(0, 0, 0, .08); }
  p { margin: 0 0 10px; font-size: 16px; line-height: 1.6; }
  p:last-child { margin-bottom: 0; }
  label { display: flex; gap: 8px; align-items: center; font-size: 14px; cursor: pointer; }
  .legend { font-size: 13px; color: #5b6270; margin-top: 8px; line-height: 1.5; }
  .legend span { padding: 0 4px; border-radius: 4px; }

  /* only when the box is ticked: colour each tag so you can see it */
  .show kbd  { background: #dbeafe; outline: 1px solid #2563eb; }
  .show samp { background: #dcfce7; outline: 1px solid #16a34a; }
  .show var  { background: #fef3c7; outline: 1px solid #d97706; }
</style>
</head>
<body>
<div class="box" id="text">
  <!-- kbd: what the user types or presses -->
  <p>To copy, press <kbd>Ctrl</kbd>+<kbd>C</kbd>. Then type <kbd>npm test</kbd> and press <kbd>Enter</kbd>.</p>
  <!-- samp: what the program prints back -->
  <p>The terminal answers <samp>3 passed, 0 failed</samp>.</p>
  <!-- var: a name that stands for a value -->
  <p>The area is <var>w</var> &times; <var>h</var>. Replace <var>filename</var> with your file.</p>
</div>

<div class="box">
  <label><input type="checkbox" id="show"> Outline each tag</label>
  <div class="legend"><span style="background:#dbeafe">kbd</span> input &nbsp; <span style="background:#dcfce7">samp</span> output &nbsp; <span style="background:#fef3c7">var</span> variable</div>
</div>

<script>
  document.getElementById('show').addEventListener('change', (e) => {
    document.getElementById('text').classList.toggle('show', e.target.checked);
  });
</script>
</body>
</html>
kbd for what you type, samp for what the computer answers, var for a name you fill in.

Browsers draw <kbd> and <samp> in a monospace font and <var> in italics. There is no box, no border and no behaviour. Everything else is CSS.

kbd, samp or var: which one?

Ask which way the text travels. Into the computer is <kbd>. Out of the computer is <samp>. A stand-in for some value is <var>.

Input, output and a placeholder: three different tags.
Input, output and a placeholder: three different tags.
Tag Marks Example
<kbd> Keys pressed, text typed, voice commands Type <kbd>npm test</kbd>
<samp> Output a program printed or showed It prints <samp>3 passed</samp>
<var> A variable or a placeholder Open <var>name</var>.txt
<code> A piece of source code The <code>fetch()</code> function

<code> is the closest neighbour, and it is easy to mix them up. A command the reader types is <kbd>. The same command shown as part of a script is <code>. The code and pre guide covers code blocks and whitespace.

These tags describe what text is, not how it looks. For stress and italics, the em tag guide covers <em>, <i> and <cite>. For short forms, see the abbr tag.

Writing keyboard shortcuts

Wrap each key in its own <kbd> and put the plus sign between them as plain text:

<p>Press <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>Esc</kbd> to open Task Manager.</p>

The HTML spec also allows nesting. An outer <kbd> stands for the whole input, and each inner <kbd> is one key:

<kbd><kbd>Ctrl</kbd>+<kbd>C</kbd></kbd>

Both forms are valid. Nesting helps when you style the keys, because kbd kbd selects only the single keys and leaves the outer wrapper plain.

Write key names the way they are printed on the keyboard: Ctrl, Shift, Enter, Esc. On a Mac the same shortcut often uses Command instead of Ctrl, so a page for both needs two versions. The finished example below switches between them.

Nesting kbd and samp

The spec gives two more combinations. They are useful in manuals and support articles.

Three nesting patterns from the HTML spec, and what each one means.
Three nesting patterns from the HTML spec, and what each one means.
  • <samp> inside <kbd>: input that picks something the screen shows, such as a menu item. <kbd><samp>File</samp> > <samp>Save</samp></kbd> means "choose File, then Save".
  • <kbd> inside <samp>: input as the system echoed it back, such as a command in a terminal transcript.
<pre><samp>$ <kbd>git status</kbd>
On branch main
nothing to commit, working tree clean</samp></pre>

Wrapping the transcript in <pre> keeps its line breaks. The typed command stays marked as input, so CSS can colour it differently from the output.

Styling kbd as a key cap

The default look is only a font change, and the keys blend into the sentence. A border, rounded corners and a thicker bottom edge make them read as keys.

Live exampletry it here, then copy the code
Share it as a link
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>kbd key cap styles</title>
<style>
  body { margin: 0; padding: 16px; font-family: system-ui, sans-serif; background: #f4f5f7; color: #1d2330; }
  .row { background: #fff; border-radius: 10px; padding: 10px 14px; margin-bottom: 10px; box-shadow: 0 2px 8px rgba(0, 0, 0, .08); }
  .row h3 { margin: 0 0 4px; font-size: 13px; color: #5b6270; font-weight: 600; }
  .row p { margin: 0; font-size: 16px; line-height: 1.8; }

  /* 2. Flat: a light box and a border */
  .flat kbd {
    font: 0.85em ui-monospace, Consolas, monospace;
    padding: 1px 6px; border: 1px solid #c9ced6; border-radius: 4px; background: #f6f7f9;
  }

  /* 3. Key cap: a thicker bottom edge makes it look raised */
  .cap kbd {
    display: inline-block; min-width: 1.6em; text-align: center;
    font: 600 0.8em/1.5 system-ui, sans-serif;
    padding: 0 6px; border: 1px solid #b8bec8; border-bottom-width: 3px; border-radius: 6px;
    background: linear-gradient(#fff, #eef0f3);
    box-shadow: inset 0 -1px 0 #fff;
    cursor: pointer; user-select: none;
  }
  /* pressed: the edge shrinks and the cap moves down by the same amount */
  .cap kbd:active, .cap kbd.down { border-bottom-width: 1px; transform: translateY(2px); background: #e6e9ee; }

  /* 4. Dark key cap */
  .dark { background: #1f2430; }
  .dark h3 { color: #aab1bf; }
  .dark p { color: #e8ebf1; }
  .dark kbd {
    display: inline-block; min-width: 1.6em; text-align: center;
    font: 600 0.8em/1.5 system-ui, sans-serif; color: #fff;
    padding: 0 6px; border-radius: 6px; background: #3a4150;
    box-shadow: 0 2px 0 #0d1017, inset 0 1px 0 rgba(255, 255, 255, .15);
  }
</style>
</head>
<body>
<div class="row">
  <h3>1. Browser default</h3>
  <p>Press <kbd>Shift</kbd>+<kbd>Enter</kbd> for a new line, <kbd>Ctrl</kbd>+<kbd>Enter</kbd> to send.</p>
</div>
<div class="row flat">
  <h3>2. Flat box</h3>
  <p>Press <kbd>Shift</kbd>+<kbd>Enter</kbd> for a new line, <kbd>Ctrl</kbd>+<kbd>Enter</kbd> to send.</p>
</div>
<div class="row cap">
  <h3>3. Key cap (click a key, or press it)</h3>
  <p>Press <kbd>Shift</kbd>+<kbd>Enter</kbd> for a new line, <kbd>Ctrl</kbd>+<kbd>Enter</kbd> to send.</p>
</div>
<div class="row dark">
  <h3>4. Dark key cap</h3>
  <p>Press <kbd>Shift</kbd>+<kbd>Enter</kbd> for a new line, <kbd>Ctrl</kbd>+<kbd>Enter</kbd> to send.</p>
</div>

<script>
  // Row 3 also reacts to the real keyboard: holding a key presses its cap.
  const caps = document.querySelectorAll('.cap kbd');
  const names = { control: 'ctrl' };
  function setDown(e, on) {
    const k = e.key.toLowerCase();
    caps.forEach((c) => {
      if (c.textContent.toLowerCase() === (names[k] || k)) c.classList.toggle('down', on);
    });
  }
  addEventListener('keydown', (e) => setDown(e, true));
  addEventListener('keyup', (e) => setDown(e, false));
  // if the window loses focus mid-press, release every cap
  addEventListener('blur', () => caps.forEach((c) => c.classList.remove('down')));
</script>
</body>
</html>
The default, a flat box, a key cap that presses down, and a dark version. Click a cap in row 3, or press Shift, Ctrl or Enter.
kbd {
  display: inline-block;
  min-width: 1.6em;
  text-align: center;
  font: 600 0.8em/1.5 system-ui, sans-serif;
  padding: 0 6px;
  border: 1px solid #b8bec8;
  border-bottom-width: 3px;
  border-radius: 6px;
  background: linear-gradient(#fff, #eef0f3);
}

If you nest keys inside an outer <kbd>, use kbd kbd as the selector instead, so only the single keys get the cap.

A default kbd next to a styled one, with the CSS that does each part.
A default kbd next to a styled one, with the CSS that does each part.
  • border-bottom-width: 3px is the raised edge. It is simpler than a stack of shadows and keeps its shape at any zoom.
  • min-width with text-align: center keeps single letters from looking thinner than Ctrl.
  • display: inline-block lets padding and transform apply cleanly inside a line of text.

For a pressed look, shrink the bottom border and move the key down by the same amount: border-bottom-width: 1px; transform: translateY(2px);. The key sinks in place and the line around it does not move. More shadow ideas are in the box-shadow guide.

Why kbd text looks smaller

The browser style sets font-family: monospace on <kbd>, <samp> and <code>. In Chrome, when the text around it is at the default 16px, such an element computes to 13px.

The fix is to name the fonts yourself. font-family: ui-monospace, Consolas, monospace gives a normal size, and so does setting an explicit font-size on the paragraph or on <kbd> itself. A key cap in a sans-serif font, as above, avoids the question entirely.

A finished example: a shortcut cheat sheet

This cheat sheet lists five editing shortcuts. It picks Windows or Mac keys from the browser, and the buttons switch between them. Click inside it and press the shortcuts: each cap presses down, and a finished shortcut gets a tick.

Live exampletry it here, then copy the code
Share it as a link
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Keyboard shortcut cheat sheet</title>
<style>
  body { margin: 0; padding: 16px; font-family: system-ui, sans-serif; background: #f4f5f7; color: #1d2330; }
  .sheet { background: #fff; border-radius: 12px; padding: 14px; box-shadow: 0 2px 8px rgba(0, 0, 0, .08); max-width: 460px; }
  .top { display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 8px; }
  h2 { margin: 0; font-size: 17px; }
  .os button { font: 600 13px system-ui, sans-serif; padding: 6px 10px; border: 1px solid #c9ced6; background: #fff; cursor: pointer; }
  .os button:first-child { border-radius: 8px 0 0 8px; }
  .os button:last-child { border-radius: 0 8px 8px 0; border-left: 0; }
  .os button[aria-pressed="true"] { background: #1d2330; color: #fff; border-color: #1d2330; }
  ul { list-style: none; margin: 0; padding: 0; }
  li { display: flex; justify-content: space-between; align-items: center; padding: 9px 6px; border-top: 1px solid #eceef2; cursor: pointer; border-radius: 6px; }
  li:hover { background: #f7f8fa; }
  li.tried .name::after { content: " \2713"; color: #16a34a; font-weight: 700; }

  /* the outer kbd is the whole shortcut: keep it plain */
  kbd { font: inherit; white-space: nowrap; }
  /* each inner kbd is one key: draw it as a key cap */
  kbd kbd {
    display: inline-block; min-width: 1.7em; text-align: center;
    font: 600 13px/1.6 system-ui, sans-serif;
    padding: 0 6px; margin: 0 1px; border: 1px solid #b8bec8; border-bottom-width: 3px; border-radius: 6px;
    background: linear-gradient(#fff, #eef0f3);
  }
  kbd kbd.down { border-bottom-width: 1px; transform: translateY(2px); background: #dbeafe; border-color: #2563eb; }
  .out { margin: 12px 0 0; font-size: 14px; color: #5b6270; }
  samp { font: 13px ui-monospace, Consolas, monospace; background: #f1f3f6; padding: 2px 6px; border-radius: 4px; color: #1d2330; }
  .hint { font-size: 12.5px; color: #5b6270; margin: 8px 0 0; line-height: 1.45; }
</style>
</head>
<body>
<div class="sheet">
  <div class="top">
    <h2>Editing shortcuts</h2>
    <div class="os">
      <button type="button" data-os="win">Windows</button><button type="button" data-os="mac">Mac</button>
    </div>
  </div>
  <ul id="list"></ul>
  <p class="out">Last shortcut: <samp id="out">none yet</samp></p>
  <p class="hint">Click here, then press a shortcut. On a phone, tap a row to see its keys pressed.</p>
</div>

<script>
  // One list of shortcuts. mod is Ctrl on Windows and Command on a Mac.
  const shortcuts = [
    { name: 'Copy',       win: ['mod', 'c'],        mac: ['mod', 'c'] },
    { name: 'Paste',      win: ['mod', 'v'],        mac: ['mod', 'v'] },
    { name: 'Undo',       win: ['mod', 'z'],        mac: ['mod', 'z'] },
    { name: 'Redo',       win: ['mod', 'y'],        mac: ['mod', 'shift', 'z'] },
    { name: 'Select all', win: ['mod', 'a'],        mac: ['mod', 'a'] },
  ];
  const label = { control: 'Ctrl', meta: '⌘ Cmd', shift: 'Shift' };
  let os = /Mac|iPhone|iPad/.test(navigator.userAgent) ? 'mac' : 'win';
  const list = document.getElementById('list');
  const out = document.getElementById('out');

  // mod becomes the real key name that event.key reports
  const keysFor = (s) => s[os].map((k) => (k === 'mod' ? (os === 'mac' ? 'meta' : 'control') : k));

  function render() {
    list.innerHTML = '';
    shortcuts.forEach((s) => {
      const li = document.createElement('li');
      // nested kbd: the outer one is the shortcut, each inner one is a key
      const caps = keysFor(s).map((k) => `<kbd data-k="${k}">${label[k] || k.toUpperCase()}</kbd>`).join('+');
      li.innerHTML = `<span class="name">${s.name}</span><kbd>${caps}</kbd>`;
      li.addEventListener('click', () => play(li));
      list.appendChild(li);
      s.li = li;
    });
    document.querySelectorAll('.os button').forEach((b) => b.setAttribute('aria-pressed', b.dataset.os === os));
  }

  // tap a row: press its keys one by one, then release them
  function play(li) {
    const caps = [...li.querySelectorAll('kbd kbd')];
    caps.forEach((c, i) => setTimeout(() => c.classList.add('down'), i * 180));
    setTimeout(() => caps.forEach((c) => c.classList.remove('down')), caps.length * 180 + 350);
  }

  function light(key, on) {
    document.querySelectorAll(`kbd kbd[data-k="${key}"]`).forEach((c) => c.classList.toggle('down', on));
  }

  addEventListener('keydown', (e) => {
    const key = e.key.toLowerCase();
    light(key, true);
    // did this key finish one of the shortcuts?
    const mod = os === 'mac' ? e.metaKey : e.ctrlKey;
    shortcuts.forEach((s) => {
      const keys = s[os];
      if (mod && e.shiftKey === keys.includes('shift') && key === keys[keys.length - 1]) {
        s.li.classList.add('tried');
        out.textContent = s.name + ' (' + keysFor(s).map((k) => label[k] || k.toUpperCase()).join('+') + ')';
      }
    });
  });
  addEventListener('keyup', (e) => {
    light(e.key.toLowerCase(), false);
    // a Mac may not send keyup for letters held with Command, so release all with it
    if (e.key === 'Meta') document.querySelectorAll('.down').forEach((c) => c.classList.remove('down'));
  });
  addEventListener('blur', () => document.querySelectorAll('.down').forEach((c) => c.classList.remove('down')));

  document.querySelectorAll('.os button').forEach((b) => b.addEventListener('click', () => { os = b.dataset.os; render(); }));
  render();
</script>
</body>
</html>
Nested kbd for each shortcut, samp for the last result. Tap a row on a phone to see its keys pressed.
  1. One list of shortcuts: each entry has Windows keys and Mac keys, with mod standing for Ctrl or Command.
  2. Nested markup: the script writes <kbd><kbd>Ctrl</kbd>+<kbd>C</kbd></kbd> for each row. CSS draws only kbd kbd as caps.
  3. Live keys: keydown and keyup read event.key and press the matching caps.
  4. Done check: event.ctrlKey or event.metaKey plus the last key marks the row, and <samp> shows the result.

The page does not call preventDefault(), so the shortcuts still do their normal job. Pressing Select all selects the text in the example, as it would anywhere else.

When it does not work

What you see Cause Fix
kbd looks like normal text in monospace That is the browser default Add a border and background, as above
kbd is smaller than the sentence font-family: monospace alone, in Chrome Name a font list or set font-size
A big box around the whole shortcut Cap style on nested <kbd> hits the outer one too Style kbd kbd only, or drop the outer <kbd>
A shortcut breaks across two lines The line wraps at the plus sign white-space: nowrap on the outer <kbd>
Pressing a key does nothing in the demo The frame does not have focus Click inside it first
Mac readers see Ctrl Only one set of keys Show both, or switch with a Windows/Mac toggle

A shortcut sheet or a setup guide is something people keep open next to their work. Sent as an .html file, it may open as plain code on a phone, and a screenshot cannot switch between Windows and Mac.

To send the working version, paste the page into a NOS document and choose Create share link. HTML to link walks through it.

The page renders as written and its scripts run, so the people you send it to can press the keys and flip the toggle themselves. If you add shortcuts later, the same link shows the new version.

Questions people ask

What is the kbd tag in HTML?

It is an inline element for user input: a key such as <kbd>Enter</kbd>, a shortcut, or text the user should type, such as a command. It is usually keyboard input, but the HTML spec also allows other input such as voice commands. Browsers show it in a monospace font and draw no box around it.

How do I write a keyboard shortcut like Ctrl+C?

Nest the elements. The outer kbd is the whole input and each inner kbd is one key: <kbd><kbd>Ctrl</kbd>+<kbd>C</kbd></kbd>. The simpler <kbd>Ctrl</kbd>+<kbd>C</kbd> is also valid and common.

What is the difference between kbd, samp and code?

kbd is what goes into the computer: keys and typed text. samp is what comes out: messages and output a program printed. code is a piece of source code, such as a function name or a line of HTML. All three use a monospace font by default.

Why is my kbd text smaller than the text around it?

In Chrome, an element whose font-family is only the generic monospace, like the default kbd, drops to 13px when the text around it is at the browser default of 16px. Give kbd your own font list, such as ui-monospace, Consolas, monospace, or set its font-size.

When should I use the var tag?

For a name that stands for a value: a variable in a formula, a function parameter, or a placeholder the reader replaces, such as <var>filename</var>. Browsers show it in italics. It does not do anything else.

Keep reading