Components / Overlays & Dialogs / Command
Command beta
The ⌘K palette: one input that searches actions and pages, driven from the keyboard.
Uses: Button, Kbd — include its CSS alongside this one.
<button class="btn btn-sec btn-md" data-command-open><svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.5-4.5"/></svg><span style="margin:0 26px 0 2px">Search…</span><span class="k-kbd">⌘</span> <span class="k-kbd">K</span></button>
<dialog class="kcmd" data-command aria-label="Command palette">
<div class="kcmd-input-row">
<svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.5-4.5"/></svg>
<input class="kcmd-input" data-command-input type="text" placeholder="Type a command or search…" aria-label="Search commands">
</div>
<ul class="kcmd-list">
<li><button class="kcmd-item active" data-cmd-item><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3.5" y="4.5" width="17" height="15" rx="2"/><path d="M3.5 9.5h17M9.5 9.5v10"/></svg><span>Go to Dashboard</span><span class="kcmd-hint">Page</span></button></li>
<li><button class="kcmd-item" data-cmd-item><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8l-5-5Z"/><path d="M14 3v5h5M12 12v6M9 15h6"/></svg><span>New file</span><span class="kcmd-hint">Action</span></button></li>
<li><button class="kcmd-item" data-cmd-item><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="3.6"/><path d="M12 2.5v2.4M12 19.1v2.4M2.5 12h2.4M19.1 12h2.4M5 5l1.7 1.7M17.3 17.3 19 19M19 5l-1.7 1.7M6.7 17.3 5 19"/></svg><span>Toggle theme</span><span class="kcmd-hint">Action</span></button></li>
<li><button class="kcmd-item" data-cmd-item><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 6h10M17 6h4M3 12h4M11 12h10M3 18h12M19 18h2"/><circle cx="15" cy="6" r="2"/><circle cx="9" cy="12" r="2"/><circle cx="17" cy="18" r="2"/></svg><span>Open settings</span><span class="kcmd-hint">Page</span></button></li>
<li><button class="kcmd-item" data-cmd-item><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 14a4 4 0 0 0 6 0l3-3a4 4 0 1 0-6-6l-1.5 1.5"/><path d="M14 10a4 4 0 0 0-6 0l-3 3a4 4 0 1 0 6 6l1.5-1.5"/></svg><span>Copy page link</span><span class="kcmd-hint">Action</span></button></li>
<li><button class="kcmd-item" data-cmd-item><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3v12m-5-5 5 5 5-5"/><path d="M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2"/></svg><span>Download report</span><span class="kcmd-hint">Action</span></button></li>
</ul>
<div class="kcmd-empty" data-command-empty style="display:none">No results.</div>
</dialog>.kcmd{border:0;padding:0;border-radius:14px;width:min(560px,calc(100% - 40px));margin:12vh auto auto;background:var(--k-bg);color:var(--k-ink);font-family:var(--k-font);box-shadow:0 30px 80px -20px rgba(0,0,0,.45)}
.kcmd::backdrop{background:rgba(24,24,27,.45);backdrop-filter:blur(3px)}
.kcmd-input-row{display:flex;align-items:center;gap:10px;padding:14px 16px;border-bottom:1px solid var(--k-line);color:var(--k-ink-3)}
.kcmd-input{flex:1;border:0;outline:0;font:inherit;font-size:15px;background:transparent;color:var(--k-ink)}
.kcmd-input::placeholder{color:var(--k-ink-3)}
.kcmd-list{list-style:none;margin:0;padding:6px;max-height:300px;overflow:auto}
.kcmd-item{display:flex;align-items:center;gap:11px;width:100%;padding:9px 10px;border:0;border-radius:8px;background:transparent;font:inherit;font-size:13.5px;color:var(--k-ink);cursor:pointer;text-align:left}
.kcmd-item svg{color:var(--k-ink-3);flex:none}
.kcmd-item.active{background:var(--k-bg-soft)}
.kcmd-hint{margin-left:auto;font-size:11px;color:var(--k-ink-3)}
.kcmd-empty{padding:22px 10px;text-align:center;font-size:13px;color:var(--k-ink-3)}
const cmdDlg = document.querySelector('[data-command]');
const cmdInput = cmdDlg.querySelector('[data-command-input]');
const cmdItems = [...cmdDlg.querySelectorAll('[data-cmd-item]')];
const cmdEmpty = cmdDlg.querySelector('[data-command-empty]');
let cmdActive = 0;
function openCommand() { cmdDlg.showModal(); cmdInput.value = ''; filterCmd(''); cmdInput.focus(); }
function setActiveCmd(i) {
cmdActive = Math.max(0, Math.min(i, cmdItems.filter(it => it.offsetParent !== null).length - 1));
let n = -1;
cmdItems.forEach(it => { const vis = it.offsetParent !== null; if (vis) n++; it.classList.toggle('active', vis && n === cmdActive); });
}
function filterCmd(q) {
let any = false;
cmdItems.forEach(it => { const hit = it.textContent.toLowerCase().includes(q.toLowerCase()); it.parentElement.style.display = hit ? '' : 'none'; if (hit) any = true; });
cmdEmpty.style.display = any ? 'none' : '';
setActiveCmd(0);
}
document.querySelector('[data-command-open]').addEventListener('click', openCommand);
document.addEventListener('keydown', e => {
if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') { e.preventDefault(); if (!cmdDlg.open) openCommand(); }
});
cmdInput.addEventListener('input', () => filterCmd(cmdInput.value));
cmdInput.addEventListener('keydown', e => {
if (e.key === 'ArrowDown') { e.preventDefault(); setActiveCmd(cmdActive + 1); }
if (e.key === 'ArrowUp') { e.preventDefault(); setActiveCmd(cmdActive - 1); }
if (e.key === 'Enter') { const vis = cmdItems.filter(it => it.offsetParent !== null); if (vis[cmdActive]) cmdDlg.close(); }
});
cmdItems.forEach(it => it.addEventListener('click', () => cmdDlg.close()));
Usage
For apps with many actions or pages. Trigger it with a button and with Cmd/Ctrl+K; selecting an item is the app’s job.
Accessibility
Arrow keys move, Enter selects, Escape closes. The list is a listbox with aria-activedescendant left to the app; the input keeps focus throughout.