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.
Paste it into Claude, Cursor, v0 or any AI coding tool. Prefer the
finished markup? .
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.
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();