All pages

Components

Popover

Every popover in the product: one plate, one motion, two primitives. A menu is a list of choices and gets Radix's roving focus, typeahead and radio semantics; a panel holds arbitrary content and behaves like a small dialog. One component wrapping both would mean picking which behaviours a caller silently got.

MenuContent wraps Radix's DropdownMenu content — portalled and already dressed, because a menu that renders inside the dialog or scroller that opened it is clipped by it. MenuRadioItem puts the tick on the trailing edge: the mark for “this one” comes after the thing it marks, and alignment on the leading edge was already paid for by the row itself. Its icon slot is for menus whose rows are a shape before they are a word. Rows that cannot be radio items take the exported MENU_ROW class.

import { DropdownMenu } from "radix-ui";
import { MenuContent, MenuRadioItem } from "@/components/base/popover";

<DropdownMenu.Root>
<DropdownMenu.Trigger asChild>…</DropdownMenu.Trigger>
<MenuContent align="start">
  <DropdownMenu.RadioGroup value={sort} onValueChange={setSort}>
    <MenuRadioItem value="updated">Recently updated</MenuRadioItem>
    …
  </DropdownMenu.RadioGroup>
</MenuContent>
</DropdownMenu.Root>

Panel

PanelContent is the same plate and the same motion around content that is not a list of choices — the chat record, the side menu's More panel. A larger corner (1.25rem against the menu's 0.75rem), the same z-[1000] surface.

import { Popover } from "radix-ui";
import { PanelContent } from "@/components/base/popover";

<Popover.Root>
<Popover.Trigger asChild>…</Popover.Trigger>
<PanelContent align="start" className="w-[16rem] p-4">…</PanelContent>
</Popover.Root>

A panel that opens on hover — the download QR, hanging off a row or a pill the pointer is already over — is HoverPanelContent: the same plate and motion on Radix's HoverCard instead of its Popover.

Motion

Both primitives grow from the corner Radix actually placed them against — the transform origin is Radix's own computed variable, written out per primitive because Tailwind finds classes by scanning source text, and an interpolated origin-[var(--radix-…)] would emit nothing and the panels would scale from their own centres. For a popover Radix is not positioning — a panel anchored to its own button — POPOVER_MOTION exports the pair alone, and whatever drives data-state must keep the element mounted for the length of the closed half.