Components

Command palette

A ⌘K-style command surface: a borderless search input over a scrollable list of grouped, selectable rows. Filter as you type, move the active row with the arrow keys, and run it with Enter. Reuses the shared overlay scrim.

Static surface

The .ds-cmdk surface on its own (the fixed .ds-overlay scrim is omitted here so it renders in flow). A borderless input with a bottom rule, faint uppercase group labels, an active row on .is-active, and a right-aligned hint or .ds-cmdk__kbd.

Surface
Navigation
Actions
<div class="ds-cmdk">
  <input class="ds-cmdk__input" type="text" placeholder="Type a command or search…">
  <div class="ds-cmdk__list" role="listbox">
    <div class="ds-cmdk__group-label">Navigation</div>
    <button class="ds-cmdk__item is-active" role="option" aria-selected="true">
      <span class="ds-cmdk__label">Go to dashboard</span>
      <span class="ds-cmdk__kbd">G D</span>
    </button>
    <button class="ds-cmdk__item" role="option">
      <span class="ds-cmdk__label">Go to settings</span>
      <span class="ds-cmdk__kbd">G S</span>
    </button>
    <div class="ds-cmdk__group-label">Actions</div>
    <button class="ds-cmdk__item" role="option">
      <span class="ds-cmdk__label">Create reference</span>
      <span class="ds-cmdk__hint">⌘ N</span>
    </button>
  </div>
</div>

Empty state

When the query matches nothing, the list shows a single centered .ds-cmdk__empty message.

No results
No results.
<div class="ds-cmdk__list" role="listbox">
  <div class="ds-cmdk__empty">No results.</div>
</div>

Interactive

A trigger opens the live overlay. Type to filter, use / to move the active row, Enter to run it, Escape or a backdrop click to close. In a real app you would also wire a global K listener to flip the open state.

Open palette
<button class="ds-button ds-button--primary" id="cmdkOpen">Open palette</button>
<div class="ds-overlay" id="demoCmdk" style="align-items:flex-start">
  <div class="ds-cmdk" role="dialog" aria-modal="true" aria-label="Command palette">
    <input class="ds-cmdk__input" type="text" id="cmdkInput" placeholder="Type a command or search…">
    <div class="ds-cmdk__list" role="listbox" id="cmdkList"></div>
  </div>
</div>
<script>
  const COMMANDS = [
    { label: "Go to dashboard", group: "Navigation", hint: "G D" },
    { label: "Go to settings",  group: "Navigation", hint: "G S" },
    { label: "Create reference", group: "Actions", hint: "⌘ N" },
    { label: "Export matrix",    group: "Actions" },
  ];
  const ov = document.querySelector("#demoCmdk");
  const input = document.querySelector("#cmdkInput");
  const list = document.querySelector("#cmdkList");
  let active = 0, view = COMMANDS;

  function render() {
    const q = input.value.trim().toLowerCase();
    view = COMMANDS.filter((c) => c.label.toLowerCase().includes(q));
    active = Math.min(active, Math.max(0, view.length - 1));
    list.innerHTML = "";
    if (!view.length) {
      list.innerHTML = '<div class="ds-cmdk__empty">No results.</div>';
      return;
    }
    let lastGroup;
    view.forEach((c, i) => {
      if (c.group && c.group !== lastGroup) {
        lastGroup = c.group;
        const g = document.createElement("div");
        g.className = "ds-cmdk__group-label";
        g.textContent = c.group;
        list.appendChild(g);
      }
      const b = document.createElement("button");
      b.type = "button";
      b.className = "ds-cmdk__item" + (i === active ? " is-active" : "");
      b.innerHTML = '<span class="ds-cmdk__label"></span>' +
        (c.hint ? '<span class="ds-cmdk__hint"></span>' : "");
      b.querySelector(".ds-cmdk__label").textContent = c.label;
      if (c.hint) b.querySelector(".ds-cmdk__hint").textContent = c.hint;
      b.addEventListener("mouseenter", () => { active = i; render(); });
      b.addEventListener("click", () => close());
      list.appendChild(b);
    });
  }
  function open() { input.value = ""; active = 0; render(); ov.classList.add("is-open"); input.focus(); }
  function close() { ov.classList.remove("is-open"); }

  document.querySelector("#cmdkOpen").addEventListener("click", open);
  ov.addEventListener("click", (e) => { if (e.target === ov) close(); });
  input.addEventListener("input", render);
  input.addEventListener("keydown", (e) => {
    if (e.key === "ArrowDown") { e.preventDefault(); active = (active + 1) % view.length; render(); }
    else if (e.key === "ArrowUp") { e.preventDefault(); active = (active - 1 + view.length) % view.length; render(); }
    else if (e.key === "Enter") { e.preventDefault(); close(); }
    else if (e.key === "Escape") { close(); }
  });
  // Global ⌘K / Ctrl-K listener.
  document.addEventListener("keydown", (e) => {
    if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") { e.preventDefault(); open(); }
  });
</script>

React

The CommandPalette component portals to <body> and is controlled via open. Pass commands as { id, label, group?, hint?, onRun }; filtering, arrow navigation, and Enter-to-run are built in. Wire a global K listener in your app to flip open.

<CommandPalette>
import { CommandPalette } from "@diametral/design-system/react";

function Palette() {
  const [open, setOpen] = React.useState(false);

  React.useEffect(() => {
    const onKey = (e) => {
      if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
        e.preventDefault();
        setOpen((o) => !o);
      }
    };
    document.addEventListener("keydown", onKey);
    return () => document.removeEventListener("keydown", onKey);
  }, []);

  const commands = [
    { id: "dash", label: "Go to dashboard", group: "Navigation", hint: "G D", onRun: () => navigate("/") },
    { id: "new",  label: "Create reference", group: "Actions", hint: "⌘ N", onRun: createReference },
  ];

  return (
    <CommandPalette
      open={open}
      onClose={() => setOpen(false)}
      commands={commands}
      placeholder="Type a command or search…"
    />
  );
}