Open demo

CSS code

HTML
<div><span class="lbl">Mechanical keycaps</span>
<div class="ky-row">
  <button class="ky ky-esc" type="button"><span class="ky-top">Esc</span></button>
  <button class="ky" type="button"><span class="ky-top"><span class="ky-sym" aria-hidden="true">⌘</span><span class="sr-only">Command</span></span></button>
  <button class="ky" type="button"><span class="ky-top">K</span></button>
  <button class="ky ky-wide ky-ent" type="button"><span class="ky-top">Enter ⏎</span></button>
</div>
<p class="note">Press ⌘ K anywhere in Ledgerline to search invoices, clients and reports.</p></div>

<div class="pair">
<div><span class="lbl">Toggle keys that stay down</span>
<div class="ky-bar" role="group" aria-label="Text formatting">
  <label class="ky-tg"><input type="checkbox" name="format" value="bold" checked><span class="ky"><span class="ky-top"><b aria-hidden="true">B</b><span class="sr-only">Bold</span></span><span class="ky-led" aria-hidden="true"></span></span></label>
  <label class="ky-tg"><input type="checkbox" name="format" value="italic"><span class="ky"><span class="ky-top"><i aria-hidden="true">I</i><span class="sr-only">Italic</span></span><span class="ky-led" aria-hidden="true"></span></span></label>
  <label class="ky-tg"><input type="checkbox" name="format" value="underline"><span class="ky"><span class="ky-top"><u aria-hidden="true">U</u><span class="sr-only">Underline</span></span><span class="ky-led" aria-hidden="true"></span></span></label>
  <label class="ky-tg"><input type="checkbox" name="format" value="strikethrough" checked><span class="ky"><span class="ky-top"><s aria-hidden="true">S</s><span class="sr-only">Strikethrough</span></span><span class="ky-led" aria-hidden="true"></span></span></label>
</div></div>

<div><span class="lbl">Arrow key cluster</span>
<div class="ky-arrows" role="group" aria-label="Move the selected shape">
  <button class="ky ky-n" type="button" aria-label="Move up"><span class="ky-top" aria-hidden="true">↑</span></button>
  <button class="ky ky-w" type="button" aria-label="Move left"><span class="ky-top" aria-hidden="true">←</span></button>
  <button class="ky ky-s" type="button" aria-label="Move down"><span class="ky-top" aria-hidden="true">↓</span></button>
  <button class="ky ky-e" type="button" aria-label="Move right"><span class="ky-top" aria-hidden="true">→</span></button>
</div></div>
</div>

<div><span class="lbl">Arcade push buttons</span>
<div class="ky-panel">
  <button class="ky-arc" type="button"><span class="ky-bezel" aria-hidden="true"><span class="ky-cap"></span></span>1P Start</button>
  <button class="ky-arc ky-blue" type="button"><span class="ky-bezel" aria-hidden="true"><span class="ky-cap"></span></span>2P Start</button>
  <button class="ky-arc ky-gold" type="button"><span class="ky-bezel" aria-hidden="true"><span class="ky-cap"></span></span>Insert coin</button>
</div></div>
CSS
/* 3D keycaps and push buttons. Each key is a real <button> (or a checkbox for keys that stay down). The button's background
   is the key's side wall, a solid box-shadow is its lower edge, and an inner span is the dished top. Pressing moves the key
   down onto its edge; a checked toggle key stays down and lights its LED. No JavaScript. */
.ky{--ky-face:#f7f4ee;--ky-face-2:#e9e3d8;--ky-side:#d6cebf;--ky-edge:#a8a091;--ky-ink:#292524;--ky-focus:#1d4ed8;--ky-depth:7px;
  position:relative;display:inline-grid;min-width:64px;height:64px;padding:3px 5px calc(var(--ky-depth) + 2px);border:0;border-radius:12px;
  background:var(--ky-side);color:var(--ky-ink);font:inherit;cursor:pointer;
  box-shadow:0 var(--ky-depth) 0 var(--ky-edge),0 calc(var(--ky-depth) + 6px) 16px -4px rgba(41,37,36,.45);
  transition:transform .09s ease-out,box-shadow .09s ease-out}
.ky-top{display:grid;place-items:center;padding:0 12px;border-radius:9px;background:linear-gradient(180deg,var(--ky-face),var(--ky-face-2));background-color:var(--ky-face);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.7),inset 0 -2px 4px rgba(41,37,36,.08);font-size:.92rem;font-weight:700;letter-spacing:.02em}
.ky:active{transform:translateY(calc(var(--ky-depth) - 2px));box-shadow:0 2px 0 var(--ky-edge),0 4px 8px -4px rgba(41,37,36,.45)}
.ky:focus-visible{outline:3px solid var(--ky-focus);outline-offset:5px}
.ky-wide{min-width:132px}
.ky-wide .ky-top{justify-content:end;align-items:end;padding:0 12px 8px}
.ky-esc{--ky-face:#c2410c;--ky-face-2:#a8380a;--ky-side:#9a3412;--ky-edge:#6c2410;--ky-ink:#fff}
.ky-ent{--ky-face:#3f3a36;--ky-face-2:#2f2b28;--ky-side:#262321;--ky-edge:#0f0e0d;--ky-ink:#f5f0e8}
.ky-row{display:flex;flex-wrap:wrap;gap:14px 12px;align-items:flex-start}
.ky-sym{font-size:1.25rem;font-weight:500}
.ky .sr-only,.ky-tg .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}

/* toggle keys: a visually hidden checkbox and the key drawn after it */
.ky-bar{display:inline-flex;flex-wrap:wrap;gap:12px;padding:16px 18px 22px;border-radius:18px;background:#d9d2c5;box-shadow:inset 0 2px 6px rgba(41,37,36,.18)}
.ky-tg{position:relative;display:inline-block;cursor:pointer}
.ky-tg input{position:absolute;opacity:0;width:1px;height:1px}
.ky-tg .ky{pointer-events:none;min-width:56px;height:58px}
.ky-tg .ky-top{font-family:Georgia,"Times New Roman",serif;font-size:1.15rem}
.ky-led{position:absolute;top:9px;right:10px;width:7px;height:7px;border-radius:50%;background:#a8a29e;box-shadow:inset 0 1px 1px rgba(0,0,0,.25);transition:background-color .15s,box-shadow .15s}
.ky-tg input:checked + .ky{transform:translateY(calc(var(--ky-depth) - 3px));box-shadow:0 3px 0 var(--ky-edge),0 5px 8px -4px rgba(41,37,36,.45);--ky-face:#ece5d8;--ky-face-2:#e2dacb}
.ky-tg input:checked + .ky .ky-led{background:#16a34a;box-shadow:0 0 6px 1px rgba(22,163,74,.7)}
.ky-tg:active .ky{transform:translateY(calc(var(--ky-depth) - 2px));box-shadow:0 2px 0 var(--ky-edge)}
.ky-tg input:focus-visible + .ky{outline:3px solid var(--ky-focus);outline-offset:5px}

/* arrow cluster: an inverted T on a grid */
.ky-arrows{display:grid;grid-template-columns:repeat(3,58px);grid-template-areas:". n ." "w s e";gap:12px 10px}
.ky-arrows .ky{min-width:58px;height:58px}
.ky-arrows .ky-top{font-size:1.1rem}
.ky-n{grid-area:n}.ky-w{grid-area:w}.ky-s{grid-area:s}.ky-e{grid-area:e}

/* arcade push buttons on a dark panel */
.ky-panel{display:flex;flex-wrap:wrap;width:fit-content;max-width:100%;justify-content:center;gap:26px 34px;padding:30px 26px 24px;border-radius:22px;
  background:radial-gradient(120% 140% at 50% 0%,#2a2724,#141210);box-shadow:0 26px 40px -26px rgba(20,18,16,.8),inset 0 1px 0 rgba(255,255,255,.08)}
.ky-arc{--ky-cap:#dc2626;--ky-cap-2:#991b1b;--ky-rim:#7f1d1d;display:grid;justify-items:center;gap:12px;padding:0;border:0;background:none;color:#f5f0e8;font:inherit;font-size:.8rem;font-weight:700;letter-spacing:.12em;text-transform:uppercase;cursor:pointer}
.ky-bezel{display:grid;place-items:center;width:84px;height:84px;border-radius:50%;background:linear-gradient(180deg,#3a3633,#0c0b0a);box-shadow:inset 0 2px 3px rgba(255,255,255,.12),0 2px 0 #000}
.ky-cap{width:62px;height:62px;border-radius:50%;background:radial-gradient(circle at 38% 30%,rgba(255,255,255,.55),rgba(255,255,255,0) 32%),radial-gradient(circle at 50% 50%,var(--ky-cap) 55%,var(--ky-cap-2));
  box-shadow:0 8px 0 var(--ky-rim),0 10px 8px rgba(0,0,0,.6);transform:translateY(-6px);transition:transform .09s ease-out,box-shadow .09s ease-out}
.ky-arc:active .ky-cap{transform:translateY(0);box-shadow:0 2px 0 var(--ky-rim),0 3px 4px rgba(0,0,0,.6)}
.ky-arc:focus-visible{outline:none}
.ky-arc:focus-visible .ky-bezel{outline:3px solid #fbbf24;outline-offset:4px}
.ky-blue{--ky-cap:#3b82f6;--ky-cap-2:#1d4ed8;--ky-rim:#1e3a8a}
.ky-gold{--ky-cap:#facc15;--ky-cap-2:#ca8a04;--ky-rim:#854d0e}

@media (prefers-reduced-motion:reduce){
  .ky,.ky-cap,.ky-led{transition:none}
}

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<div><span class="lbl">Mechanical keycaps</span>
<div class="d-flex flex-wrap align-items-start column-gap-3 row-gap-3">
  <button class="btn ky ky-esc" type="button"><span class="ky-top">Esc</span></button>
  <button class="btn ky" type="button"><span class="ky-top"><span class="ky-sym" aria-hidden="true">⌘</span><span class="visually-hidden">Command</span></span></button>
  <button class="btn ky" type="button"><span class="ky-top">K</span></button>
  <button class="btn ky ky-wide ky-ent" type="button"><span class="ky-top">Enter ⏎</span></button>
</div>
<p class="note">Press ⌘ K anywhere in Ledgerline to search invoices, clients and reports.</p></div>

<div class="pair">
<div><span class="lbl">Toggle keys that stay down</span>
<div class="ky-bar" role="group" aria-label="Text formatting">
  <input type="checkbox" class="btn-check" id="ky-b" name="format" value="bold" autocomplete="off" checked><label class="btn ky" for="ky-b"><span class="ky-top"><b aria-hidden="true">B</b><span class="visually-hidden">Bold</span></span><span class="ky-led" aria-hidden="true"></span></label>
  <input type="checkbox" class="btn-check" id="ky-i" name="format" value="italic" autocomplete="off"><label class="btn ky" for="ky-i"><span class="ky-top"><i aria-hidden="true">I</i><span class="visually-hidden">Italic</span></span><span class="ky-led" aria-hidden="true"></span></label>
  <input type="checkbox" class="btn-check" id="ky-u" name="format" value="underline" autocomplete="off"><label class="btn ky" for="ky-u"><span class="ky-top"><u aria-hidden="true">U</u><span class="visually-hidden">Underline</span></span><span class="ky-led" aria-hidden="true"></span></label>
  <input type="checkbox" class="btn-check" id="ky-s" name="format" value="strikethrough" autocomplete="off" checked><label class="btn ky" for="ky-s"><span class="ky-top"><s aria-hidden="true">S</s><span class="visually-hidden">Strikethrough</span></span><span class="ky-led" aria-hidden="true"></span></label>
</div></div>

<div><span class="lbl">Arrow key cluster</span>
<div class="ky-arrows" role="group" aria-label="Move the selected shape">
  <button class="btn ky ky-n" type="button" aria-label="Move up"><span class="ky-top" aria-hidden="true">↑</span></button>
  <button class="btn ky ky-w" type="button" aria-label="Move left"><span class="ky-top" aria-hidden="true">←</span></button>
  <button class="btn ky ky-s" type="button" aria-label="Move down"><span class="ky-top" aria-hidden="true">↓</span></button>
  <button class="btn ky ky-e" type="button" aria-label="Move right"><span class="ky-top" aria-hidden="true">→</span></button>
</div></div>
</div>

<div><span class="lbl">Arcade push buttons</span>
<div class="ky-panel">
  <button class="btn ky-arc" type="button"><span class="ky-bezel" aria-hidden="true"><span class="ky-cap"></span></span>1P Start</button>
  <button class="btn ky-arc ky-blue" type="button"><span class="ky-bezel" aria-hidden="true"><span class="ky-cap"></span></span>2P Start</button>
  <button class="btn ky-arc ky-gold" type="button"><span class="ky-bezel" aria-hidden="true"><span class="ky-cap"></span></span>Insert coin</button>
</div></div>
CSS
/* 3D keycaps and push buttons on Bootstrap .btn and .btn-check. The button's background is the key's side wall, a solid
   box-shadow is its lower edge, and an inner span is the dished top. Pressing moves the key down onto its edge; a checked
   .btn-check toggle key stays down and lights its LED. No JavaScript. */
.ky{--ky-face:#f7f4ee;--ky-face-2:#e9e3d8;--ky-side:#d6cebf;--ky-edge:#a8a091;--ky-ink:#292524;--ky-focus:#1d4ed8;--ky-depth:7px;
  --bs-btn-padding-x:5px;--bs-btn-padding-y:3px;--bs-btn-border-width:0;--bs-btn-border-radius:12px;--bs-btn-font-weight:700;
  --bs-btn-bg:var(--ky-side);--bs-btn-color:var(--ky-ink);--bs-btn-hover-bg:var(--ky-side);--bs-btn-hover-color:var(--ky-ink);
  --bs-btn-active-bg:var(--ky-side);--bs-btn-active-color:var(--ky-ink);
  position:relative;display:inline-grid;min-width:64px;height:64px;padding-bottom:calc(var(--ky-depth) + 2px);
  box-shadow:0 var(--ky-depth) 0 var(--ky-edge),0 calc(var(--ky-depth) + 6px) 16px -4px rgba(41,37,36,.45);
  transition:transform .09s ease-out,box-shadow .09s ease-out}
.ky-top{display:grid;place-items:center;padding:0 12px;border-radius:9px;background:linear-gradient(180deg,var(--ky-face),var(--ky-face-2));background-color:var(--ky-face);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.7),inset 0 -2px 4px rgba(41,37,36,.08);font-size:.92rem;font-weight:700;letter-spacing:.02em;line-height:1.2}
.btn.ky:active,.btn.ky:first-child:active{transform:translateY(calc(var(--ky-depth) - 2px));box-shadow:0 2px 0 var(--ky-edge),0 4px 8px -4px rgba(41,37,36,.45)}
.btn.ky:focus-visible{outline:3px solid var(--ky-focus);outline-offset:5px;box-shadow:0 var(--ky-depth) 0 var(--ky-edge),0 calc(var(--ky-depth) + 6px) 16px -4px rgba(41,37,36,.45)}
.ky-wide{min-width:132px}
.ky-wide .ky-top{justify-content:end;align-items:end;padding:0 12px 8px}
.ky-esc{--ky-face:#c2410c;--ky-face-2:#a8380a;--ky-side:#9a3412;--ky-edge:#6c2410;--ky-ink:#fff}
.ky-ent{--ky-face:#3f3a36;--ky-face-2:#2f2b28;--ky-side:#262321;--ky-edge:#0f0e0d;--ky-ink:#f5f0e8}
.ky-sym{font-size:1.25rem;font-weight:500}

/* toggle keys: .btn-check checkboxes with the key as their label */
.ky-bar{display:inline-flex;flex-wrap:wrap;gap:12px;padding:16px 18px 22px;border-radius:18px;background:#d9d2c5;box-shadow:inset 0 2px 6px rgba(41,37,36,.18)}
.ky-bar .ky{min-width:56px;height:58px}
.ky-bar .ky-top{font-family:Georgia,"Times New Roman",serif;font-size:1.15rem}
.ky-led{position:absolute;top:9px;right:10px;width:7px;height:7px;border-radius:50%;background:#a8a29e;box-shadow:inset 0 1px 1px rgba(0,0,0,.25);transition:background-color .15s,box-shadow .15s}
.btn-check:checked + .ky{transform:translateY(calc(var(--ky-depth) - 3px));box-shadow:0 3px 0 var(--ky-edge),0 5px 8px -4px rgba(41,37,36,.45);--ky-face:#ece5d8;--ky-face-2:#e2dacb}
.btn-check:checked + .ky .ky-led{background:#16a34a;box-shadow:0 0 6px 1px rgba(22,163,74,.7)}
.btn-check:focus-visible + .ky{outline:3px solid var(--ky-focus);outline-offset:5px}

/* arrow cluster: an inverted T on a grid */
.ky-arrows{display:grid;grid-template-columns:repeat(3,58px);grid-template-areas:". n ." "w s e";gap:12px 10px}
.ky-arrows .ky{min-width:58px;height:58px}
.ky-arrows .ky-top{font-size:1.1rem}
.ky-n{grid-area:n}.ky-w{grid-area:w}.ky-s{grid-area:s}.ky-e{grid-area:e}

/* arcade push buttons on a dark panel */
.ky-panel{display:flex;flex-wrap:wrap;justify-content:center;width:fit-content;max-width:100%;gap:26px 34px;padding:30px 26px 24px;border-radius:22px;
  background:radial-gradient(120% 140% at 50% 0%,#2a2724,#141210);box-shadow:0 26px 40px -26px rgba(20,18,16,.8),inset 0 1px 0 rgba(255,255,255,.08)}
.ky-arc{--ky-cap:#dc2626;--ky-cap-2:#991b1b;--ky-rim:#7f1d1d;--bs-btn-padding-x:0;--bs-btn-padding-y:0;--bs-btn-border-width:0;--bs-btn-color:#f5f0e8;--bs-btn-hover-color:#f5f0e8;--bs-btn-active-color:#f5f0e8;
  --bs-btn-font-size:.8rem;--bs-btn-font-weight:700;display:grid;justify-items:center;gap:12px;letter-spacing:.12em;text-transform:uppercase}
.ky-bezel{display:grid;place-items:center;width:84px;height:84px;border-radius:50%;background:linear-gradient(180deg,#3a3633,#0c0b0a);box-shadow:inset 0 2px 3px rgba(255,255,255,.12),0 2px 0 #000}
.ky-cap{width:62px;height:62px;border-radius:50%;background:radial-gradient(circle at 38% 30%,rgba(255,255,255,.55),rgba(255,255,255,0) 32%),radial-gradient(circle at 50% 50%,var(--ky-cap) 55%,var(--ky-cap-2));
  box-shadow:0 8px 0 var(--ky-rim),0 10px 8px rgba(0,0,0,.6);transform:translateY(-6px);transition:transform .09s ease-out,box-shadow .09s ease-out}
.ky-arc:active .ky-cap{transform:translateY(0);box-shadow:0 2px 0 var(--ky-rim),0 3px 4px rgba(0,0,0,.6)}
.btn.ky-arc:focus-visible{outline:none;box-shadow:none}
.ky-arc:focus-visible .ky-bezel{outline:3px solid #fbbf24;outline-offset:4px}
.ky-blue{--ky-cap:#3b82f6;--ky-cap-2:#1d4ed8;--ky-rim:#1e3a8a}
.ky-gold{--ky-cap:#facc15;--ky-cap-2:#ca8a04;--ky-rim:#854d0e}

@media (prefers-reduced-motion:reduce){
  .ky,.ky-cap,.ky-led{transition:none}
}

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<div><span class="mb-[18px] block text-[.72rem] tracking-[.12em] text-[#57534e] uppercase">Mechanical keycaps</span>
<div class="flex flex-wrap items-start gap-x-3 gap-y-3.5">
  <button class="ky-up relative inline-grid h-16 min-w-16 cursor-pointer rounded-xl bg-(--ky-side) px-[5px] pt-[3px] pb-[9px] text-(--ky-ink) transition-[translate,box-shadow] duration-[90ms] ease-out [--ky-edge:#6c2410] [--ky-face-2:#a8380a] [--ky-face:#c2410c] [--ky-ink:#fff] [--ky-side:#9a3412] focus-visible:outline-3 focus-visible:outline-offset-[5px] focus-visible:outline-[#1d4ed8] active:translate-y-[5px] active:ky-down motion-reduce:transition-none" type="button"><span class="grid place-items-center rounded-[9px] bg-(--ky-face) bg-[linear-gradient(180deg,var(--ky-face),var(--ky-face-2))] px-3 text-[.92rem] font-bold tracking-[.02em] shadow-[inset_0_1px_0_rgba(255,255,255,.7),inset_0_-2px_4px_rgba(41,37,36,.08)]">Esc</span></button>
  <button class="ky-up relative inline-grid h-16 min-w-16 cursor-pointer rounded-xl bg-(--ky-side) px-[5px] pt-[3px] pb-[9px] text-(--ky-ink) transition-[translate,box-shadow] duration-[90ms] ease-out [--ky-edge:#a8a091] [--ky-face-2:#e9e3d8] [--ky-face:#f7f4ee] [--ky-ink:#292524] [--ky-side:#d6cebf] focus-visible:outline-3 focus-visible:outline-offset-[5px] focus-visible:outline-[#1d4ed8] active:translate-y-[5px] active:ky-down motion-reduce:transition-none" type="button"><span class="grid place-items-center rounded-[9px] bg-(--ky-face) bg-[linear-gradient(180deg,var(--ky-face),var(--ky-face-2))] px-3 text-[.92rem] font-bold tracking-[.02em] shadow-[inset_0_1px_0_rgba(255,255,255,.7),inset_0_-2px_4px_rgba(41,37,36,.08)]"><span class="text-xl font-medium" aria-hidden="true">⌘</span><span class="sr-only">Command</span></span></button>
  <button class="ky-up relative inline-grid h-16 min-w-16 cursor-pointer rounded-xl bg-(--ky-side) px-[5px] pt-[3px] pb-[9px] text-(--ky-ink) transition-[translate,box-shadow] duration-[90ms] ease-out [--ky-edge:#a8a091] [--ky-face-2:#e9e3d8] [--ky-face:#f7f4ee] [--ky-ink:#292524] [--ky-side:#d6cebf] focus-visible:outline-3 focus-visible:outline-offset-[5px] focus-visible:outline-[#1d4ed8] active:translate-y-[5px] active:ky-down motion-reduce:transition-none" type="button"><span class="grid place-items-center rounded-[9px] bg-(--ky-face) bg-[linear-gradient(180deg,var(--ky-face),var(--ky-face-2))] px-3 text-[.92rem] font-bold tracking-[.02em] shadow-[inset_0_1px_0_rgba(255,255,255,.7),inset_0_-2px_4px_rgba(41,37,36,.08)]">K</span></button>
  <button class="ky-up relative inline-grid h-16 min-w-[132px] cursor-pointer rounded-xl bg-(--ky-side) px-[5px] pt-[3px] pb-[9px] text-(--ky-ink) transition-[translate,box-shadow] duration-[90ms] ease-out [--ky-edge:#0f0e0d] [--ky-face-2:#2f2b28] [--ky-face:#3f3a36] [--ky-ink:#f5f0e8] [--ky-side:#262321] focus-visible:outline-3 focus-visible:outline-offset-[5px] focus-visible:outline-[#1d4ed8] active:translate-y-[5px] active:ky-down motion-reduce:transition-none" type="button"><span class="grid items-end justify-end rounded-[9px] bg-(--ky-face) bg-[linear-gradient(180deg,var(--ky-face),var(--ky-face-2))] px-3 pb-2 text-[.92rem] font-bold tracking-[.02em] shadow-[inset_0_1px_0_rgba(255,255,255,.7),inset_0_-2px_4px_rgba(41,37,36,.08)]">Enter ⏎</span></button>
</div>
<p class="mt-[22px] text-[.88rem] text-[#57534e]">Press ⌘ K anywhere in Ledgerline to search invoices, clients and reports.</p></div>

<div class="flex flex-wrap gap-x-[72px] gap-y-14">
<div><span class="mb-[18px] block text-[.72rem] tracking-[.12em] text-[#57534e] uppercase">Toggle keys that stay down</span>
<div class="inline-flex flex-wrap gap-3 rounded-[18px] bg-[#d9d2c5] px-[18px] pt-4 pb-[22px] shadow-[inset_0_2px_6px_rgba(41,37,36,.18)]" role="group" aria-label="Text formatting">
  <label class="group relative inline-block cursor-pointer"><input type="checkbox" name="format" value="bold" class="peer sr-only" checked><span class="ky-up relative inline-grid h-[58px] min-w-14 rounded-xl bg-(--ky-side) px-[5px] pt-[3px] pb-[9px] text-(--ky-ink) transition-[translate,box-shadow] duration-[90ms] ease-out [--ky-edge:#a8a091] [--ky-face-2:#e9e3d8] [--ky-face:#f7f4ee] [--ky-ink:#292524] [--ky-side:#d6cebf] peer-checked:translate-y-1 peer-checked:ky-held peer-checked:[--ky-face-2:#e2dacb] peer-checked:[--ky-face:#ece5d8] peer-focus-visible:outline-3 peer-focus-visible:outline-offset-[5px] peer-focus-visible:outline-[#1d4ed8] group-active:translate-y-[5px] group-active:ky-down motion-reduce:transition-none"><span class="grid place-items-center rounded-[9px] bg-(--ky-face) bg-[linear-gradient(180deg,var(--ky-face),var(--ky-face-2))] px-3 font-[Georgia,'Times_New_Roman',serif] text-[1.15rem] font-bold shadow-[inset_0_1px_0_rgba(255,255,255,.7),inset_0_-2px_4px_rgba(41,37,36,.08)]"><b aria-hidden="true">B</b><span class="sr-only">Bold</span></span><span class="absolute top-[9px] right-2.5 size-[7px] rounded-full bg-[#a8a29e] shadow-[inset_0_1px_1px_rgba(0,0,0,.25)] transition-colors duration-150 group-has-checked:bg-[#16a34a] group-has-checked:shadow-[0_0_6px_1px_rgba(22,163,74,.7)] motion-reduce:transition-none" aria-hidden="true"></span></span></label>
  <label class="group relative inline-block cursor-pointer"><input type="checkbox" name="format" value="italic" class="peer sr-only"><span class="ky-up relative inline-grid h-[58px] min-w-14 rounded-xl bg-(--ky-side) px-[5px] pt-[3px] pb-[9px] text-(--ky-ink) transition-[translate,box-shadow] duration-[90ms] ease-out [--ky-edge:#a8a091] [--ky-face-2:#e9e3d8] [--ky-face:#f7f4ee] [--ky-ink:#292524] [--ky-side:#d6cebf] peer-checked:translate-y-1 peer-checked:ky-held peer-checked:[--ky-face-2:#e2dacb] peer-checked:[--ky-face:#ece5d8] peer-focus-visible:outline-3 peer-focus-visible:outline-offset-[5px] peer-focus-visible:outline-[#1d4ed8] group-active:translate-y-[5px] group-active:ky-down motion-reduce:transition-none"><span class="grid place-items-center rounded-[9px] bg-(--ky-face) bg-[linear-gradient(180deg,var(--ky-face),var(--ky-face-2))] px-3 font-[Georgia,'Times_New_Roman',serif] text-[1.15rem] font-bold shadow-[inset_0_1px_0_rgba(255,255,255,.7),inset_0_-2px_4px_rgba(41,37,36,.08)]"><i aria-hidden="true">I</i><span class="sr-only">Italic</span></span><span class="absolute top-[9px] right-2.5 size-[7px] rounded-full bg-[#a8a29e] shadow-[inset_0_1px_1px_rgba(0,0,0,.25)] transition-colors duration-150 group-has-checked:bg-[#16a34a] group-has-checked:shadow-[0_0_6px_1px_rgba(22,163,74,.7)] motion-reduce:transition-none" aria-hidden="true"></span></span></label>
  <label class="group relative inline-block cursor-pointer"><input type="checkbox" name="format" value="underline" class="peer sr-only"><span class="ky-up relative inline-grid h-[58px] min-w-14 rounded-xl bg-(--ky-side) px-[5px] pt-[3px] pb-[9px] text-(--ky-ink) transition-[translate,box-shadow] duration-[90ms] ease-out [--ky-edge:#a8a091] [--ky-face-2:#e9e3d8] [--ky-face:#f7f4ee] [--ky-ink:#292524] [--ky-side:#d6cebf] peer-checked:translate-y-1 peer-checked:ky-held peer-checked:[--ky-face-2:#e2dacb] peer-checked:[--ky-face:#ece5d8] peer-focus-visible:outline-3 peer-focus-visible:outline-offset-[5px] peer-focus-visible:outline-[#1d4ed8] group-active:translate-y-[5px] group-active:ky-down motion-reduce:transition-none"><span class="grid place-items-center rounded-[9px] bg-(--ky-face) bg-[linear-gradient(180deg,var(--ky-face),var(--ky-face-2))] px-3 font-[Georgia,'Times_New_Roman',serif] text-[1.15rem] font-bold shadow-[inset_0_1px_0_rgba(255,255,255,.7),inset_0_-2px_4px_rgba(41,37,36,.08)]"><u aria-hidden="true">U</u><span class="sr-only">Underline</span></span><span class="absolute top-[9px] right-2.5 size-[7px] rounded-full bg-[#a8a29e] shadow-[inset_0_1px_1px_rgba(0,0,0,.25)] transition-colors duration-150 group-has-checked:bg-[#16a34a] group-has-checked:shadow-[0_0_6px_1px_rgba(22,163,74,.7)] motion-reduce:transition-none" aria-hidden="true"></span></span></label>
  <label class="group relative inline-block cursor-pointer"><input type="checkbox" name="format" value="strikethrough" class="peer sr-only" checked><span class="ky-up relative inline-grid h-[58px] min-w-14 rounded-xl bg-(--ky-side) px-[5px] pt-[3px] pb-[9px] text-(--ky-ink) transition-[translate,box-shadow] duration-[90ms] ease-out [--ky-edge:#a8a091] [--ky-face-2:#e9e3d8] [--ky-face:#f7f4ee] [--ky-ink:#292524] [--ky-side:#d6cebf] peer-checked:translate-y-1 peer-checked:ky-held peer-checked:[--ky-face-2:#e2dacb] peer-checked:[--ky-face:#ece5d8] peer-focus-visible:outline-3 peer-focus-visible:outline-offset-[5px] peer-focus-visible:outline-[#1d4ed8] group-active:translate-y-[5px] group-active:ky-down motion-reduce:transition-none"><span class="grid place-items-center rounded-[9px] bg-(--ky-face) bg-[linear-gradient(180deg,var(--ky-face),var(--ky-face-2))] px-3 font-[Georgia,'Times_New_Roman',serif] text-[1.15rem] font-bold shadow-[inset_0_1px_0_rgba(255,255,255,.7),inset_0_-2px_4px_rgba(41,37,36,.08)]"><s aria-hidden="true">S</s><span class="sr-only">Strikethrough</span></span><span class="absolute top-[9px] right-2.5 size-[7px] rounded-full bg-[#a8a29e] shadow-[inset_0_1px_1px_rgba(0,0,0,.25)] transition-colors duration-150 group-has-checked:bg-[#16a34a] group-has-checked:shadow-[0_0_6px_1px_rgba(22,163,74,.7)] motion-reduce:transition-none" aria-hidden="true"></span></span></label>
</div></div>

<div><span class="mb-[18px] block text-[.72rem] tracking-[.12em] text-[#57534e] uppercase">Arrow key cluster</span>
<div class="grid grid-cols-[repeat(3,58px)] gap-x-2.5 gap-y-3 [grid-template-areas:'._n_.'_'w_s_e']" role="group" aria-label="Move the selected shape">
  <button class="[grid-area:n] ky-up relative inline-grid h-[58px] min-w-[58px] cursor-pointer rounded-xl bg-(--ky-side) px-[5px] pt-[3px] pb-[9px] text-(--ky-ink) transition-[translate,box-shadow] duration-[90ms] ease-out [--ky-edge:#a8a091] [--ky-face-2:#e9e3d8] [--ky-face:#f7f4ee] [--ky-ink:#292524] [--ky-side:#d6cebf] focus-visible:outline-3 focus-visible:outline-offset-[5px] focus-visible:outline-[#1d4ed8] active:translate-y-[5px] active:ky-down motion-reduce:transition-none" type="button" aria-label="Move up"><span class="grid place-items-center rounded-[9px] bg-(--ky-face) bg-[linear-gradient(180deg,var(--ky-face),var(--ky-face-2))] px-3 text-[1.1rem] font-bold shadow-[inset_0_1px_0_rgba(255,255,255,.7),inset_0_-2px_4px_rgba(41,37,36,.08)]" aria-hidden="true">↑</span></button>
  <button class="[grid-area:w] ky-up relative inline-grid h-[58px] min-w-[58px] cursor-pointer rounded-xl bg-(--ky-side) px-[5px] pt-[3px] pb-[9px] text-(--ky-ink) transition-[translate,box-shadow] duration-[90ms] ease-out [--ky-edge:#a8a091] [--ky-face-2:#e9e3d8] [--ky-face:#f7f4ee] [--ky-ink:#292524] [--ky-side:#d6cebf] focus-visible:outline-3 focus-visible:outline-offset-[5px] focus-visible:outline-[#1d4ed8] active:translate-y-[5px] active:ky-down motion-reduce:transition-none" type="button" aria-label="Move left"><span class="grid place-items-center rounded-[9px] bg-(--ky-face) bg-[linear-gradient(180deg,var(--ky-face),var(--ky-face-2))] px-3 text-[1.1rem] font-bold shadow-[inset_0_1px_0_rgba(255,255,255,.7),inset_0_-2px_4px_rgba(41,37,36,.08)]" aria-hidden="true">←</span></button>
  <button class="[grid-area:s] ky-up relative inline-grid h-[58px] min-w-[58px] cursor-pointer rounded-xl bg-(--ky-side) px-[5px] pt-[3px] pb-[9px] text-(--ky-ink) transition-[translate,box-shadow] duration-[90ms] ease-out [--ky-edge:#a8a091] [--ky-face-2:#e9e3d8] [--ky-face:#f7f4ee] [--ky-ink:#292524] [--ky-side:#d6cebf] focus-visible:outline-3 focus-visible:outline-offset-[5px] focus-visible:outline-[#1d4ed8] active:translate-y-[5px] active:ky-down motion-reduce:transition-none" type="button" aria-label="Move down"><span class="grid place-items-center rounded-[9px] bg-(--ky-face) bg-[linear-gradient(180deg,var(--ky-face),var(--ky-face-2))] px-3 text-[1.1rem] font-bold shadow-[inset_0_1px_0_rgba(255,255,255,.7),inset_0_-2px_4px_rgba(41,37,36,.08)]" aria-hidden="true">↓</span></button>
  <button class="[grid-area:e] ky-up relative inline-grid h-[58px] min-w-[58px] cursor-pointer rounded-xl bg-(--ky-side) px-[5px] pt-[3px] pb-[9px] text-(--ky-ink) transition-[translate,box-shadow] duration-[90ms] ease-out [--ky-edge:#a8a091] [--ky-face-2:#e9e3d8] [--ky-face:#f7f4ee] [--ky-ink:#292524] [--ky-side:#d6cebf] focus-visible:outline-3 focus-visible:outline-offset-[5px] focus-visible:outline-[#1d4ed8] active:translate-y-[5px] active:ky-down motion-reduce:transition-none" type="button" aria-label="Move right"><span class="grid place-items-center rounded-[9px] bg-(--ky-face) bg-[linear-gradient(180deg,var(--ky-face),var(--ky-face-2))] px-3 text-[1.1rem] font-bold shadow-[inset_0_1px_0_rgba(255,255,255,.7),inset_0_-2px_4px_rgba(41,37,36,.08)]" aria-hidden="true">→</span></button>
</div></div>
</div>

<div><span class="mb-[18px] block text-[.72rem] tracking-[.12em] text-[#57534e] uppercase">Arcade push buttons</span>
<div class="flex w-fit max-w-full flex-wrap justify-center gap-x-[34px] gap-y-[26px] rounded-[22px] bg-[radial-gradient(120%_140%_at_50%_0%,#2a2724,#141210)] px-[26px] pt-[30px] pb-6 shadow-[0_26px_40px_-26px_rgba(20,18,16,.8),inset_0_1px_0_rgba(255,255,255,.08)]">
  <button class="group grid cursor-pointer justify-items-center gap-3 text-[.8rem] font-bold tracking-[.12em] text-[#f5f0e8] uppercase [--ky-cap-2:#991b1b] [--ky-cap:#dc2626] [--ky-rim:#7f1d1d] focus-visible:outline-none" type="button"><span class="grid size-[84px] place-items-center rounded-full bg-[linear-gradient(180deg,#3a3633,#0c0b0a)] shadow-[inset_0_2px_3px_rgba(255,255,255,.12),0_2px_0_#000] group-focus-visible:outline-3 group-focus-visible:outline-offset-4 group-focus-visible:outline-[#fbbf24]" aria-hidden="true"><span class="ky-cap size-[62px] -translate-y-1.5 rounded-full shadow-[0_8px_0_var(--ky-rim),0_10px_8px_rgba(0,0,0,.6)] transition-[translate,box-shadow] duration-[90ms] ease-out group-active:translate-y-0 group-active:shadow-[0_2px_0_var(--ky-rim),0_3px_4px_rgba(0,0,0,.6)] motion-reduce:transition-none"></span></span>1P Start</button>
  <button class="group grid cursor-pointer justify-items-center gap-3 text-[.8rem] font-bold tracking-[.12em] text-[#f5f0e8] uppercase [--ky-cap-2:#1d4ed8] [--ky-cap:#3b82f6] [--ky-rim:#1e3a8a] focus-visible:outline-none" type="button"><span class="grid size-[84px] place-items-center rounded-full bg-[linear-gradient(180deg,#3a3633,#0c0b0a)] shadow-[inset_0_2px_3px_rgba(255,255,255,.12),0_2px_0_#000] group-focus-visible:outline-3 group-focus-visible:outline-offset-4 group-focus-visible:outline-[#fbbf24]" aria-hidden="true"><span class="ky-cap size-[62px] -translate-y-1.5 rounded-full shadow-[0_8px_0_var(--ky-rim),0_10px_8px_rgba(0,0,0,.6)] transition-[translate,box-shadow] duration-[90ms] ease-out group-active:translate-y-0 group-active:shadow-[0_2px_0_var(--ky-rim),0_3px_4px_rgba(0,0,0,.6)] motion-reduce:transition-none"></span></span>2P Start</button>
  <button class="group grid cursor-pointer justify-items-center gap-3 text-[.8rem] font-bold tracking-[.12em] text-[#f5f0e8] uppercase [--ky-cap-2:#ca8a04] [--ky-cap:#facc15] [--ky-rim:#854d0e] focus-visible:outline-none" type="button"><span class="grid size-[84px] place-items-center rounded-full bg-[linear-gradient(180deg,#3a3633,#0c0b0a)] shadow-[inset_0_2px_3px_rgba(255,255,255,.12),0_2px_0_#000] group-focus-visible:outline-3 group-focus-visible:outline-offset-4 group-focus-visible:outline-[#fbbf24]" aria-hidden="true"><span class="ky-cap size-[62px] -translate-y-1.5 rounded-full shadow-[0_8px_0_var(--ky-rim),0_10px_8px_rgba(0,0,0,.6)] transition-[translate,box-shadow] duration-[90ms] ease-out group-active:translate-y-0 group-active:shadow-[0_2px_0_var(--ky-rim),0_3px_4px_rgba(0,0,0,.6)] motion-reduce:transition-none"></span></span>Insert coin</button>
</div></div>
CSS
/* Key colours are CSS variables on each key (--ky-face, --ky-face-2, --ky-side, --ky-edge, --ky-ink); the two utilities below
   draw the key's lower edge from them, raised and pressed. Everything else is utility classes in the markup. */
@utility ky-up{box-shadow:0 7px 0 var(--ky-edge),0 13px 16px -4px rgba(41,37,36,.45)}
@utility ky-down{box-shadow:0 2px 0 var(--ky-edge),0 4px 8px -4px rgba(41,37,36,.45)}
@utility ky-held{box-shadow:0 3px 0 var(--ky-edge),0 5px 8px -4px rgba(41,37,36,.45)}
@utility ky-cap{background:radial-gradient(circle at 38% 30%,rgba(255,255,255,.55),rgba(255,255,255,0) 32%),radial-gradient(circle at 50% 50%,var(--ky-cap) 55%,var(--ky-cap-2))}

About this set

Buttons that look and behave like physical keys, in four variants. The mechanical keycaps are Esc, Command, K and a wide Enter key, each with a dished top face, a side wall and a lower edge; pressing a key drops it onto its edge. The toggle keys are a formatting bar of Bold, Italic, Underline and Strikethrough keys that stay down while they are on and light a small green LED. The arrow key cluster is an inverted T of four keys laid out on a CSS grid. The arcade push buttons are round caps in dark bezels on a control panel, for games and playful call-to-action areas. Every key is a real button, or a checkbox for the toggle keys, so clicks, Space and Enter work natively and the toggle state submits with a form. The depth comes from the button background as the side wall, a solid box-shadow as the lower edge and an inner span as the top, and the pressed state moves the key down with translateY on :active or :checked. No JavaScript is used. The Bootstrap version builds the keys on .btn and the toggle keys on .btn-check.

What’s included

  • Keycaps with a dished top, side wall and solid lower edge
  • Toggle keys that stay pressed and light an LED when on
  • Inverted-T arrow cluster on a CSS grid
  • Round arcade buttons with a bezel and a sinking cap
  • Pressed state from :active and :checked, with no JavaScript

Accessibility

Keys are native buttons or checkboxes. Symbol keys carry a name, such as Command, Move up or Bold, through aria-label or visually hidden text, and the toggle keys sit in a group named Text formatting. Every key shows a 3px outline on keyboard focus (amber around the arcade bezel), the toggle state is shown by depth and the LED rather than colour alone, and the press transitions are removed under prefers-reduced-motion.

Customise it

Each key reads --ky-face, --ky-face-2, --ky-side, --ky-edge and --ky-ink, and --ky-depth sets how far it stands out; the arcade caps use --ky-cap, --ky-cap-2 and --ky-rim. The same variables drive the Bootstrap .btn keys and the Tailwind arbitrary properties, where the edge shadows are the ky-up, ky-held and ky-down utilities.