Command Palette
Overview
An inline command palette: typing filters, arrow keys move the highlight, Enter runs it. The highlight moves with transform, so the list never reflows.
Plain HTML, CSS and a little vanilla JS, with no framework dependency. Copy the prompt into Claude Code, Cursor, Trae or Codex and the model adds Command Palette to your project in your colors.
Where to use it
Search boxes, AI chat inputs, form fields, file uploads, verification codes and anywhere else people type.
Install
With Builder, install it with the 8pt CLI or call it directly from an editor connected to 8pt MCP.
$ npm i -g @8pt-dev/cli && 8pt add command-palette
mcp › Use 8pt MCP to call get_component("command-palette")
Code
Source for Command Palette. Motion uses only transform and opacity and respects the Reduce motion setting.
<div class="x-cmd" data-own-k>
<label class="x-cmd__in"><span aria-hidden="true"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7"/><path d="M20 20l-3.5-3.5"/></svg></span><input placeholder="Type a command…" aria-label="Command"><kbd>K</kbd></label>
<div class="x-cmd__list" role="listbox">
<span class="x-cmd__hl" aria-hidden="true"></span>
<button type="button" role="option" data-k="new New project"><i><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M5 12h14"/></svg></i>New project<em>N</em></button>
<button type="button" role="option" data-k="theme Toggle theme dark"><i><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M20 14.5A8 8 0 1 1 9.5 4a6.5 6.5 0 0 0 10.5 10.5z"/></svg></i>Toggle dark theme<em>T</em></button>
<button type="button" role="option" data-k="invite Invite members"><i><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="8" r="3.5"/><path d="M2.5 20a6.5 6.5 0 0 1 13 0M18 8v6M15 11h6"/></svg></i>Invite members<em>I</em></button>
<button type="button" role="option" data-k="deploy Deploy Release"><i><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M7 17L17 7M9 7h8v8"/></svg></i>Deploy to production<em>D</em></button>
<button type="button" role="option" data-k="settings Settings"><i><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1.1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.7 1.7 0 0 0 1.5-1.1 1.7 1.7 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.8.3H9a1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8V9a1.7 1.7 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1z"/></svg></i>Open Settings<em>,</em></button>
</div>
<div class="x-cmd__toast" aria-live="polite"></div>
</div>.x-cmd{position:relative;width:min(320px,100%);border-radius:14px;background:var(--d-card,#14141c);border:1px solid var(--d-line,rgba(255,255,255,.12));box-shadow:0 24px 50px -28px rgba(0,0,0,.7);color:var(--d-fg,#fff);overflow:hidden}
.x-cmd__in{display:flex;align-items:center;gap:8px;height:42px;padding:0 12px;border-bottom:1px solid var(--d-line,rgba(255,255,255,.1))}
.x-cmd__in span{color:var(--d-muted,#9b9ba8);font-size:13px}
.x-cmd__in input{flex:1;min-width:0;border:0;outline:0;background:transparent;color:inherit;font:14px/1 var(--d-font,system-ui,sans-serif)}
.x-cmd__in input::placeholder{color:var(--d-muted,#9b9ba8)}
.x-cmd__in kbd{padding:3px 6px;border-radius:5px;border:1px solid var(--d-line,rgba(255,255,255,.14));font:600 10px/1 ui-monospace,monospace;color:var(--d-muted,#9b9ba8)}
.x-cmd__list{position:relative;display:grid;padding:6px}
.x-cmd__list button{position:relative;z-index:1;display:flex;align-items:center;gap:10px;height:34px;padding:0 10px;border:0;background:transparent;color:inherit;font:13px/1 var(--d-font,system-ui,sans-serif);text-align:left;cursor:pointer}
.x-cmd__list button[hidden]{display:none}
.x-cmd__list i{display:grid;place-items:center;width:18px;font-style:normal;color:var(--d-muted,#9b9ba8)}
.x-cmd__list em{margin-left:auto;font:600 10px/1 ui-monospace,monospace;font-style:normal;color:var(--d-muted,#9b9ba8)}
.x-cmd__hl{position:absolute;left:6px;right:6px;top:6px;height:34px;border-radius:8px;background:color-mix(in srgb,var(--d-accent,#4D74FF) 18%,transparent);transition:transform .22s cubic-bezier(.2,.8,.2,1),opacity .2s}
…const init = (root) => {
const input = root.querySelector('input'), hl = root.querySelector('.x-cmd__hl'), toast = root.querySelector('.x-cmd__toast');
const opts = [...root.querySelectorAll('[role=option]')];
let sel = 0, t, demo, di = 0;
const vis = () => opts.filter(o => !o.hidden);
const draw = () => { const v = vis(); hl.style.opacity = v.length ? 1 : 0; sel = Math.min(sel, Math.max(0, v.length - 1)); hl.style.transform = `translateY(${sel * 34}px)`; opts.forEach(o => o.setAttribute('aria-selected', String(o === v[sel]))); };
const run = (o) => { if (!o) return; toast.textContent = '✓ ' + o.childNodes[1].textContent.trim(); toast.classList.add('on'); clearTimeout(t); t = setTimeout(() => toast.classList.remove('on'), 1200); };
input.addEventListener('input', () => { const q = input.value.trim().toLowerCase(); opts.forEach(o => o.hidden = !!q && !(o.dataset.k + o.textContent).toLowerCase().includes(q)); sel = 0; draw(); });
input.addEventListener('keydown', e => { const v = vis(); if (!v.length) return;
if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { e.preventDefault(); sel = (sel + (e.key === 'ArrowDown' ? 1 : v.length - 1)) % v.length; draw(); }
if (e.key === 'Enter') { e.preventDefault(); run(v[sel]); } });
opts.forEach(o => { o.addEventListener('pointerenter', () => { sel = vis().indexOf(o); draw(); }); o.addEventListener('click', () => run(o)); });
…Subscribe to Builder to see the full source.See pricing