Skip to content

Overlays

All overlays are built on Radix primitives, so focus trap, Esc, scroll lock, ARIA roles, and keyboard navigation are correct by construction. We supply the styling with adaptive tokens — pass surface="dark" to theme any overlay for the dark shell. No backdrop blur (brand rule).

import { Modal } from '@repo/ui/overlay';
const [open, setOpen] = useState(false);
<Modal open={open} onOpenChange={setOpen} title="Submit order?" description=""
footer={<><Button variant="outline" onClick={() => setOpen(false)}>Cancel</Button>
<Button onClick={submit}>Submit</Button></>}>
Body content
</Modal>

title is required (wired to the accessible name). Props: open, onOpenChange, title, description?, size? (sm|md|lg|xl), surface?, trigger?, footer?.

Same API as Modal plus side (right|left) and width. A full-height slide-over.

<Tooltip label="Archive"><button aria-label="Archive"></button></Tooltip>

Always ink (reads the same on any surface). The child must accept a ref (Radix asChild).

<Popover trigger={<Button>Info</Button>} side="bottom" align="center"></Popover>

Floating panel with collision handling; surface-aware.

<DropdownMenu trigger={<Button>Actions ▾</Button>}>
<DropdownMenu.Label>Order</DropdownMenu.Label>
<DropdownMenu.Item onSelect={archive}>Archive</DropdownMenu.Item>
<DropdownMenu.Separator />
<DropdownMenu.Item danger onSelect={remove}>Delete</DropdownMenu.Item>
</DropdownMenu>
import { CommandPalette } from '@repo/ui/overlay';
<CommandPalette open={open} onOpenChange={setOpen} commands={[
{ id: 'inbox', label: 'Go to inbox', hint: 'g i', onSelect: goInbox },
{ id: 'archive', label: 'Archive order', keywords: ['remove'], onSelect: archive },
]} />

A Cmd+K palette on Radix Dialog + fuzzysort (filters across label, hint, group, keywords). Implements the combobox/listbox pattern: arrow keys move the active option (aria-activedescendant), Enter selects, Esc closes. Wire your own ⌘K shortcut to toggle open. Replaces the app’s three ad-hoc Cmd+K variants.

  • Do give icon-only triggers an aria-label (and a Tooltip for sighted users).
  • Do keep modal copy short; one primary action in the footer.
  • Don’t nest a Drawer inside a Modal, or add backdrop blur.
  • Don’t hand-roll focus traps — that’s why these exist.
  • Focus trap + restore, Esc, scroll lock, role/aria-modal/aria-haspopup, arrow-key menu nav — all from Radix. Modal/Drawer verified with jest-axe; color-scheme matches the overlay’s surface so native controls inside render correctly.