Bonus
Command palette
Spotlight-style ⌘K search overlay
Spotlight-style ⌘K search overlay
Build a command palette overlay with Tailwind CSS: a dimmed backdrop with a blur, and a centered max-w-xl panel positioned toward the top of the screen with a heavy shadow and rounded-2xl corners. The panel has a search row with a leading glyph, a borderless input, and an Esc key hint; below it a scrollable results area grouped by small uppercase section labels, where each row shows an icon tile, a label, and a right-aligned keyboard shortcut, with the first row highlighted as the active selection. Close with a footer bar showing navigation hints using kbd elements. Then add the JavaScript a palette actually needs: filter rows as you type and hide any group heading left with no matches, move the highlight with the arrow keys wrapping at both ends and scrolling the active row into view, activate the highlighted row on Enter, follow the mouse so hover and keyboard selection never disagree, close on Escape or a click on the backdrop, and reopen on ⌘K / Ctrl-K with the query reset and the input focused. <div id="palette" class="fixed inset-0 z-50 flex items-start justify-center bg-neutral-900/40 p-4 pt-[15vh] backdrop-blur-sm">
<div id="palettePanel" class="w-full max-w-xl overflow-hidden rounded-2xl bg-white shadow-2xl ring-1 ring-neutral-900/5">
<div class="flex items-center gap-3 border-b border-neutral-200 px-4">
<span class="text-neutral-400"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="inline-block h-[1em] w-[1em] shrink-0 align-[-0.125em] transition-transform"><circle cx="11" cy="11" r="7"/><path d="m16 16 4 4"/></svg></span>
<input id="paletteInput" autofocus placeholder="Search or jump to…" class="flex-1 bg-transparent py-4 text-sm outline-none placeholder:text-neutral-400" />
<kbd class="rounded border border-neutral-200 px-1.5 py-0.5 text-[10px] font-medium text-neutral-400">esc</kbd>
</div>
<div id="paletteList" class="max-h-80 overflow-y-auto p-2">
<p data-group class="px-3 py-2 text-[11px] font-semibold uppercase tracking-wider text-neutral-400">Actions</p>
<button data-item class="flex w-full items-center gap-3 rounded-xl px-3 py-2.5 text-left">
<span class="flex h-7 w-7 items-center justify-center rounded-lg bg-neutral-100 text-sm"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="inline-block h-[1em] w-[1em] shrink-0 align-[-0.125em] transition-transform"><path d="M12 5v14M5 12h14"/></svg></span>
<span class="flex-1 text-sm text-neutral-700">Create new project</span>
<kbd class="rounded border border-neutral-200 px-1.5 py-0.5 text-[10px] text-neutral-400">⌘N</kbd>
</button>
<button data-item class="flex w-full items-center gap-3 rounded-xl px-3 py-2.5 text-left">
<span class="flex h-7 w-7 items-center justify-center rounded-lg bg-neutral-100 text-sm"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="inline-block h-[1em] w-[1em] shrink-0 align-[-0.125em] transition-transform"><path d="M14 4h6v6M20 4l-9 9"/><path d="M18 13v6a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h6"/></svg></span>
<span class="flex-1 text-sm text-neutral-700">Invite a teammate</span>
<kbd class="rounded border border-neutral-200 px-1.5 py-0.5 text-[10px] text-neutral-400">⌘I</kbd>
</button>
<p data-group class="px-3 pb-2 pt-4 text-[11px] font-semibold uppercase tracking-wider text-neutral-400">Recent</p>
<button data-item class="flex w-full items-center gap-3 rounded-xl px-3 py-2.5 text-left">
<span class="flex h-7 w-7 items-center justify-center rounded-lg bg-neutral-100 text-sm"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="inline-block h-[1em] w-[1em] shrink-0 align-[-0.125em] transition-transform"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg></span>
<span class="flex-1 text-sm text-neutral-700">Q3 billing migration</span>
<span class="text-xs text-neutral-400">Project</span>
</button>
<button data-item class="flex w-full items-center gap-3 rounded-xl px-3 py-2.5 text-left">
<span class="flex h-7 w-7 items-center justify-center rounded-lg bg-neutral-100 text-sm"><svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="inline-block h-[1em] w-[1em] shrink-0 align-[-0.125em] transition-transform"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3.5 2"/></svg></span>
<span class="flex-1 text-sm text-neutral-700">Deployment history</span>
<span class="text-xs text-neutral-400">Page</span>
</button>
<p id="paletteEmpty" class="hidden px-3 py-8 text-center text-sm text-neutral-500">No matching commands.</p>
</div>
<div class="flex items-center gap-4 border-t border-neutral-200 bg-neutral-50 px-4 py-2.5 text-[11px] text-neutral-400">
<span class="flex items-center gap-1"><kbd class="rounded border border-neutral-200 bg-white px-1 py-0.5">↑</kbd><kbd class="rounded border border-neutral-200 bg-white px-1 py-0.5">↓</kbd> navigate</span>
<span class="flex items-center gap-1"><kbd class="rounded border border-neutral-200 bg-white px-1 py-0.5">↵</kbd> select</span>
</div>
</div>
</div> var palette = document.getElementById("palette");
var panel = document.getElementById("palettePanel");
var input = document.getElementById("paletteInput");
var list = document.getElementById("paletteList");
var empty = document.getElementById("paletteEmpty");
var items = [].slice.call(list.querySelectorAll("[data-item]"));
var groups = [].slice.call(list.querySelectorAll("[data-group]"));
var HIGHLIGHT = "bg-neutral-100";
var active = 0;
function visible() {
return items.filter(function (item) { return !item.classList.contains("hidden"); });
}
function paint() {
var shown = visible();
if (shown.length === 0) return;
// Wrap at both ends.
if (active >= shown.length) active = 0;
if (active < 0) active = shown.length - 1;
// Toggle only the highlight class — rewriting className here would clobber
// the "hidden" class the filter just applied.
items.forEach(function (item) { item.classList.remove(HIGHLIGHT); });
var current = shown[active];
current.classList.add(HIGHLIGHT);
current.scrollIntoView({ block: "nearest" });
}
function filter() {
var query = input.value.trim().toLowerCase();
items.forEach(function (item) {
item.classList.toggle("hidden", Boolean(query) && item.textContent.toLowerCase().indexOf(query) === -1);
});
// A heading with nothing under it is noise — hide it until its group returns.
groups.forEach(function (group) {
var any = false;
var node = group.nextElementSibling;
while (node && !node.hasAttribute("data-group")) {
if (node.hasAttribute("data-item") && !node.classList.contains("hidden")) any = true;
node = node.nextElementSibling;
}
group.classList.toggle("hidden", !any);
});
var shown = visible();
empty.classList.toggle("hidden", shown.length > 0);
active = 0;
paint();
}
function close() {
palette.classList.add("hidden");
}
function open() {
palette.classList.remove("hidden");
input.value = "";
filter();
input.focus();
}
input.addEventListener("input", filter);
input.addEventListener("keydown", function (e) {
if (e.key === "ArrowDown") { e.preventDefault(); active += 1; paint(); }
if (e.key === "ArrowUp") { e.preventDefault(); active -= 1; paint(); }
if (e.key === "Enter") {
e.preventDefault();
var current = visible()[active];
if (current) current.click();
}
});
// Hover and keyboard selection must agree on what "active" means.
items.forEach(function (item) {
item.addEventListener("mousemove", function () {
var index = visible().indexOf(item);
if (index !== -1 && index !== active) { active = index; paint(); }
});
item.addEventListener("click", function () {
console.log("Ran:", item.textContent.trim());
close();
});
});
palette.addEventListener("mousedown", function (e) {
if (!panel.contains(e.target)) close();
});
document.addEventListener("keydown", function (e) {
if (e.key === "Escape") close();
if (e.key.toLowerCase() === "k" && (e.metaKey || e.ctrlKey)) {
e.preventDefault();
palette.classList.contains("hidden") ? open() : close();
}
});
filter();