Component catalog
Browse the public controls by interaction purpose. Every result links to generated API facts, accessibility behavior, source, and exhaustive stories.
Browse the public controls by interaction purpose. Every result links to generated API facts, accessibility behavior, source, and exhaustive stories.
A visible, sizable trigger that, on a plain click/tap, presents a Liquid Glass popover of command rows — with labelled and unlabeled sections, leading icons, a destructive role, and drill-in submenus.
import { Menu } from "@liquidify/react/menu"| Prop | Type | Default | Description |
|---|---|---|---|
| aria-label | string | undefined | — | Accessible name of the panel (`role="menu"`). Defaults to the trigger's accessible name. |
| className | string | undefined | — | Optional additional CSS class merged into the glass surface class. The supported per-instance customisation seam: override any `--lq-*` custom property from this class's CSS — the token cascade the engine reads (ADR-0004). No raw inline style is involved, so this stays ADR-0003 clean. |
| defaultOpen | boolean | undefined | false | Uncontrolled seed for the open state. |
| disabled | boolean | undefined | — | Whole-control disable: desaturates the trigger, `aria-disabled` + `data-disabled`, removed from tab order, cannot open. |
| icon | ReactNode | — | Optional leading icon in the trigger's reserved gutter, `aria-hidden`. |
| items | readonly MenuNode[] | undefined | — | The declarative command tree; each node carries a required stable `id` (§16.6). |
| label required | ReactNode | — | Fixed trigger text/content (the SwiftUI `Menu(_ title:, content:)` analogue) — NOT a current-value display. |
| labelProps | LabelProps | undefined | — | Per-instance typography override spread last into every row's label (ADR-0028). |
| onOpenChange | ((open: boolean) => void) | undefined | — | Fires with the next open state on every open/close path. |
| open | boolean | undefined | — | Controlled open state. Passing it switches Menu to controlled mode. |
| placement | LogicalSide | undefined | — | Which side of the trigger the panel sits on. Forwarded to Popup. |
| ref | Ref<HTMLButtonElement> | undefined | — | Forwarded ref to the rendered element — the component's native root (or the merged child element on a component that opts into {@link SlottableProps}). Generic over `E` (charter `docs/api-conventions.md` §4 rule 1, default `HTMLElement`) so a component with a polymorphic root (Link's navigation / action mode union, Text's `as`) can instantiate the concrete element per variant instead of leaving every consumer with a widened `Ref<HTMLElement>`. A component that does not narrow simply inherits the `HTMLElement` default — source-compatible with every pre-existing non-generic `extends ControlProps`. |
| size | MenuSize | undefined | regular | Size of the `style="button"` trigger capsule (the SwiftUI `.controlSize(_:)` analogue). `mini` / `large` / `xlarge` are deferred — no authentic reference frame (spec `## Out of scope`). |
| style | "menu" | "button" | undefined | menu | Visual style of the trigger. `"menu"` (default) is a plain accent-text trigger with no chrome at rest (`.menuStyle(.automatic)` analogue); `"button"` is a `size`-scaled glass-capsule trigger (`.menuStyle(.button)` analogue). |
Menu supports the documented keyboard interactions below.
| Name | Value |
|---|---|
| --lq-color-accent | #007aff |
| --lq-color-action-destructive | #ff3b30 |
| --lq-color-label-primary- | #000000#ffffff |
| --lq-color-label-secondary | Component-defined value |
| --lq-color-separator | rgba(60, 60, 67, 0.14) |
| --lq-duration- | 0ms80ms120ms150ms250ms350ms500ms |
| --lq-duration-quick | 150ms |
| --lq-easing- | cubic-bezier(0.25, 0.1, 0.25, 1)cubic-bezier(0, 0, 0.2, 1)cubic-bezier(0.4, 0, 1, 1)cubic-bezier(0.175, 0.885, 0.32, 1.275)cubic-bezier(0.25, 0.46, 0.45, 0.94) |
| --lq-easing-standard | cubic-bezier(0.25, 0.1, 0.25, 1) |
| --lq-font-size-body | 0.8125rem |
| --lq-glass- | 440.71.15var(--lq-segmented-control-indicator-blur)calc(var(--lq-glass-material-blur) * 8)var(--lq-shadow-presentational) |
| --lq-glass-elevation | |
| --lq-hit-target-touch | 2.75rem |
| --lq-menu-min-width | calc(var(--lq-space-48) * 3) |
| --lq-menu-pad | var(--lq-space-8)var(--lq-space-4)var(--lq-space-12) |
| --lq-opacity-muted | 0.4 |
| --lq-popup- | 1 0.90.9 1 |
| --lq-radius-14 | 0.875rem |
| --lq-radius-8 | 0.5rem |
| --lq-radius-pill | 9999rem |
| --lq-shadow-interactive-idle | |
| --lq-shadow-presentational | var(--lq-shadow-ambient-3) |
| --lq-space-12 | 0.75rem |
| --lq-space-2 | 0.125rem |
| --lq-space-20 | 1.25rem |
| --lq-space-8 | 0.5rem |