Skip to content
uiify

Context menu

Menu that opens on a right-click.

Browsers
Chrome 151+
Why this version?

CSS anchor positioning places this surface beside its trigger using position-anchor, and anchor(). Older Chrome versions may place the surface away from its trigger.

Firefox 151+
Why this version?

CSS anchor positioning places this surface beside its trigger using position-anchor, and anchor(). Older Firefox versions may place the surface away from its trigger.

Safari 27+
Why this version?

CSS anchor positioning places this surface beside its trigger using position-anchor, and anchor(). Older Safari versions may place the surface away from its trigger.

Tier
2
Bundle compressed
4.90 kB
Forced layout budget
0 ms
Axe budget
0
SSR compatible
Yes
Published contract
Test harness

Playground

Context Menu

Closed

Right-click area

Open

Right-click area
import { ContextMenu } from "@gunkin/uiify";

export default function Example() {
  return (
    <ContextMenu.Root>
      <ContextMenu.Trigger className="">Right-click here</ContextMenu.Trigger>
      <ContextMenu.Content className="">
        <ContextMenu.Item className="">Copy</ContextMenu.Item>
        <ContextMenu.Separator className="" />
        <ContextMenu.Item className="">Paste</ContextMenu.Item>
      </ContextMenu.Content>
    </ContextMenu.Root>
  );
}
/* Generic skin for custom core surfaces used by the demo and consumers. */

@layer components {
  [data-uiify-surface] {
    display: grid;
    gap: var(--space-md);
    inline-size: max-content;
    max-inline-size: min(28rem, calc(100vw - 2 * var(--space-lg)));
    padding: var(--space-lg);
    border: var(--border-width) solid var(--border);
    border-radius: var(--radius-xl);
    corner-shape: squircle;
    background-color: var(--card);
    color: var(--card-foreground);
    box-shadow: var(--shadow-popover);
  }

  [data-uiify-surface][popover]:not(:popover-open) {
    display: none;
  }

  @media (prefers-reduced-motion: no-preference) {
    [data-uiify-surface][popover] {
      transition: var(--transition-presence);
    }

    [data-uiify-surface][popover]:not(:popover-open) {
      opacity: 0;
      scale: var(--presence-scale);
    }

    @starting-style {
      [data-uiify-surface][popover]:popover-open {
        opacity: 0;
        scale: var(--presence-scale);
      }
    }
  }
}

/* Dropdown menu — a compact command surface with explicit anatomy hooks. */

@layer components {
  [data-uiify-menu-bar] {
    display: inline-flex;
    align-items: center;
    gap: var(--space-xs);
    padding: var(--space-xs);
    border: var(--border-width) solid var(--border);
    border-radius: var(--radius-lg);
    corner-shape: squircle;
    background-color: var(--background);
  }

  :where(
    [data-uiify-menu] :is([data-part="content"], [data-part="sub-content"]),
    [data-uiify-menu]:is([data-part="content"], [data-part="sub-content"])
  ):where(:not([popover]), :popover-open) {
    display: flex;
  }

  :where(
    [data-uiify-menu] :is([data-part="content"], [data-part="sub-content"]),
    [data-uiify-menu]:is([data-part="content"], [data-part="sub-content"])
  ) {
    flex-direction: column;
    min-inline-size: 12rem;
    max-block-size: min(24rem, calc(100dvb - 2rem));
    overflow-block: auto;
    overscroll-behavior: contain;
    padding: var(--space-xs);
    border: var(--border-width) solid var(--border);
    border-radius: var(--radius-lg);
    corner-shape: squircle;
    background-color: var(--popover);
    color: var(--popover-foreground);
    box-shadow: var(--shadow-popover);
    transform-origin: var(--transform-origin, top);
  }

  :where(
    [data-uiify-menu] :is([data-part="content"], [data-part="sub-content"]),
    [data-uiify-menu]:is([data-part="content"], [data-part="sub-content"])
  )[data-state="closed"] {
    display: none;
  }

  [data-uiify-menu]
    :is(
      [data-part="item"],
      [data-part="checkbox-item"],
      [data-part="radio-item"],
      [data-part="sub-trigger"]
    ) {
    display: flex;
    align-items: center;
    column-gap: var(--space-md);
    padding-block: var(--space-sm);
    padding-inline: var(--space-md);
    border-radius: calc(var(--radius-lg) - var(--space-xs));
    corner-shape: squircle;
    cursor: pointer;
    user-select: none;
    inline-size: 100%;
    border: 0;
    background: transparent;
    color: inherit;
    font: inherit;
    font-size: var(--text-sm) !important;
    line-height: var(--leading-normal);
    text-align: start;
    appearance: none;
  }

  [data-uiify-menu]
    :is(
      [data-part="item"],
      [data-part="checkbox-item"],
      [data-part="radio-item"],
      [data-part="sub-trigger"]
    ):is(:hover, :focus-visible, [data-highlighted]) {
    background-color: var(--accent);
    color: var(--accent-foreground);
    outline: none;
  }

  [data-uiify-menu]
    [data-part="radio-item"]:hover[role="menuitemradio"][data-state="checked"]
    [data-part="item-indicator"],
  [data-uiify-menu]
    [data-part="radio-item"]:focus-visible[role="menuitemradio"][data-state="checked"]
    [data-part="item-indicator"],
  [data-uiify-menu]
    [data-part="radio-item"][data-highlighted][role="menuitemradio"][data-state="checked"]
    [data-part="item-indicator"] {
    border-color: var(--accent-foreground);
  }

  [data-uiify-menu]
    :is(
      [data-part="item"],
      [data-part="checkbox-item"],
      [data-part="radio-item"],
      [data-part="sub-trigger"]
    ):is([aria-disabled="true"], [data-disabled]) {
    opacity: 0.5;
    pointer-events: none;
  }

  [data-uiify-menu]
    :is(
      [data-part="item"],
      [data-part="checkbox-item"],
      [data-part="radio-item"],
      [data-part="sub-trigger"]
    )[data-danger] {
    color: var(--destructive, var(--foreground));
  }

  [data-uiify-menu]
    :is(
      [data-part="item"],
      [data-part="checkbox-item"],
      [data-part="radio-item"],
      [data-part="sub-trigger"]
    )[data-danger]:is(:hover, :focus-visible, [data-highlighted]) {
    background-color: var(--destructive);
    color: var(--destructive-foreground);
  }

  [data-uiify-menu] [data-part="item-icon"],
  [data-uiify-menu] [data-part="item-indicator"] {
    display: inline-grid;
    place-items: center;
    inline-size: 1.25rem;
    block-size: 1.25rem;
    flex: 0 0 1.25rem;
  }

  [data-uiify-menu] [data-part="item-indicator"] {
    border: var(--border-width) solid var(--input);
    border-radius: var(--radius-xs);
    corner-shape: squircle;
    color: transparent;
  }

  [role="menuitemcheckbox"][data-state="checked"] [data-part="item-indicator"],
  [role="menuitemcheckbox"][data-state="indeterminate"] [data-part="item-indicator"] {
    border-color: var(--primary);
    background: var(--primary);
    color: var(--primary-foreground);
  }

  [role="menuitemcheckbox"][data-state="checked"] [data-part="item-indicator"]::before {
    inline-size: 0.65rem;
    block-size: 0.65rem;
    background-color: currentcolor;
    clip-path: polygon(14% 44%, 0 65%, 38% 100%, 100% 22%, 84% 6%, 38% 68%);
    content: "";
  }

  [role="menuitemcheckbox"][data-state="indeterminate"] [data-part="item-indicator"]::before {
    inline-size: 0.65rem;
    block-size: 0.12rem;
    background: currentcolor;
    content: "";
  }

  [role="menuitemradio"] [data-part="item-indicator"] {
    border-radius: 50%;
  }

  [role="menuitemradio"][data-state="checked"] [data-part="item-indicator"] {
    border-width: 5px;
    border-color: var(--primary);
    background: var(--card);
  }

  [role="menuitemradio"][data-state="checked"] [data-part="item-indicator"]::before {
    content: none;
  }

  [data-uiify-menu] [data-part="item-icon"] > svg,
  [data-uiify-menu] [data-part="item-indicator"] > svg {
    display: block;
    inline-size: 100%;
    block-size: 100%;
  }

  [data-uiify-menu] [data-part="item-text"] {
    display: grid;
    flex: 1 1 auto;
    min-inline-size: 0;
  }

  [data-uiify-menu] [data-part="item-description"] {
    color: var(--muted-foreground);
    font-size: var(--text-xs);
    line-height: var(--leading-normal);
  }

  [data-uiify-menu]
    :is(
      [data-part="item"],
      [data-part="checkbox-item"],
      [data-part="radio-item"],
      [data-part="sub-trigger"]
    ):is(:hover, :focus-visible, [data-highlighted])
    [data-part="item-description"] {
    color: inherit;
    opacity: 0.8;
  }

  [data-uiify-menu] [data-part="shortcut"] {
    margin-inline-start: auto;
    color: var(--muted-foreground);
    font-size: var(--text-xs);
    font-family: inherit;
    font-weight: inherit;
    line-height: 1;
    white-space: nowrap;
  }

  [data-uiify-menu]
    :is(
      [data-part="item"],
      [data-part="checkbox-item"],
      [data-part="radio-item"],
      [data-part="sub-trigger"]
    ):is(:hover, :focus-visible, [data-highlighted])
    [data-part="shortcut"] {
    color: inherit;
    opacity: 0.85;
  }

  [data-uiify-menu] [data-part="label"] {
    padding-block: var(--space-xs);
    padding-inline: var(--space-md);
    color: var(--muted-foreground);
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
  }

  [data-uiify-menu] [data-part="separator"] {
    block-size: var(--border-width);
    margin-block: var(--space-xs);
    margin-inline: calc(-1 * var(--space-xs));
    background-color: var(--border);
  }

  [data-uiify-menu] [data-part="sub-content"] {
    min-inline-size: 11rem;
  }

  [data-uiify-menu] [data-part="sub-content"][data-side="right"] {
    --uiify-anchor-offset: 0;

    inset-block-start: calc(anchor(start) - var(--space-xs));
  }

  @media (prefers-reduced-motion: no-preference) {
    :where(
      [data-uiify-menu] :is([data-part="content"], [data-part="sub-content"]),
      [data-uiify-menu]:is([data-part="content"], [data-part="sub-content"])
    ) {
      transition: var(--transition-presence);
    }

    :where(
        [data-uiify-menu] :is([data-part="content"], [data-part="sub-content"]),
        [data-uiify-menu]:is([data-part="content"], [data-part="sub-content"])
      )[popover]:not(:popover-open) {
      opacity: 0;
      scale: var(--presence-scale);
    }

    @starting-style {
      :where(
        [data-uiify-menu] :is([data-part="content"], [data-part="sub-content"]),
        [data-uiify-menu]:is([data-part="content"], [data-part="sub-content"])
      ):popover-open {
        opacity: 0;
        scale: var(--presence-scale);
      }
    }
  }
}

@layer components {
  :is([data-uiify-dropdown-menu], [data-uiify-context-menu])
    [role="menuitemradio"]
    [data-part="item-indicator"] {
    border-radius: 50%;
    corner-shape: round;
  }
}
import { ContextMenu } from "@gunkin/uiify";

export default function Example() {
  return (
    <ContextMenu.Root>
      <ContextMenu.Trigger
        className="
          grid gap-md w-max max-w-[min(28rem,calc(100vw_-_2_*_var(--space-lg)))] p-6 border border-border rounded-xl
          [corner-shape:squircle] bg-card text-card-foreground shadow-popover [&[popover]:not(:popover-open)]:hidden
          motion-safe:[&[popover]]:[transition:var(--transition-presence)]
          motion-safe:[&[popover]:not(:popover-open)]:opacity-0
          motion-safe:[&[popover]:not(:popover-open)]:[scale:var(--presence-scale)]
          motion-safe:starting:[&[popover]:popover-open]:opacity-0
          motion-safe:starting:[&[popover]:popover-open]:[scale:var(--presence-scale)]
        "
      >
        Right-click here
      </ContextMenu.Trigger>
      <ContextMenu.Content
        className="
          [&[data-positioning=native]]:[--uiify-anchor-offset:var(--space-sm)]
          [&_[data-positioning=native]]:[--uiify-anchor-offset:var(--space-sm)]
          [&[popover]:popover-open:not([data-positioning=native])]:[margin:revert]
          [&_[popover]:popover-open:not([data-positioning=native])]:[margin:revert] flex-col [min-inline-size:12rem]
          [max-block-size:min(24rem,calc(100dvb_-_2rem))] overflow-y-auto overscroll-contain p-xs
          border-[length:var(--border-width)] border-solid border-border rounded-lg [corner-shape:squircle] bg-popover
          text-popover-foreground shadow-popover [transform-origin:var(--transform-origin,top)]
          data-[state=closed]:hidden [&:where(:not([popover]),:popover-open)]:flex
          [&[data-part=sub-content]]:[min-inline-size:11rem]
          [&[data-part=sub-content][data-side=right]]:[--uiify-anchor-offset:0]
          [&[data-part=sub-content][data-side=right]]:[inset-block-start:calc(anchor(start)_-_var(--space-xs))]
          motion-safe:[transition:var(--transition-presence)] motion-safe:[&:not(:popover-open)]:opacity-0
          motion-safe:[&:not(:popover-open)]:[scale:var(--presence-scale)]
          motion-safe:starting:[&:popover-open]:opacity-0
          motion-safe:starting:[&:popover-open]:[scale:var(--presence-scale)]
        "
      >
        <ContextMenu.Item
          className={
            "flex items-center gap-md w-full py-sm px-md border-0 rounded-[calc(var(--radius-lg)_-_var(--space-xs))]" +
            " [corner-shape:squircle] bg-transparent text-inherit [font-family:inherit] [font-weight:inherit] text-s" +
            "m [line-height:var(--leading-normal)] text-start cursor-pointer select-none appearance-none hover:bg-ac" +
            "cent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground focus-vi" +
            "sible:outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground aria-[disable" +
            "d=true]:opacity-50 aria-[disabled=true]:pointer-events-none data-[disabled]:opacity-50 data-[disabled]:" +
            "pointer-events-none data-[danger]:text-destructive data-[danger]:hover:bg-destructive data-[danger]:hov" +
            "er:text-destructive-foreground data-[danger]:focus-visible:bg-destructive data-[danger]:focus-visible:t" +
            "ext-destructive-foreground data-[danger]:data-[highlighted]:bg-destructive data-[danger]:data-[highligh" +
            "ted]:text-destructive-foreground [&_[data-part=item-icon]]:inline-grid [&_[data-part=item-icon]]:place-" +
            "items-center [&_[data-part=item-icon]]:size-5 [&_[data-part=item-icon]]:flex-[0_0_1.25rem] [&_[data-par" +
            "t=item-indicator]]:inline-grid [&_[data-part=item-indicator]]:place-items-center [&_[data-part=item-ind" +
            "icator]]:size-5 [&_[data-part=item-indicator]]:flex-[0_0_1.25rem] [&_[data-part=item-indicator]>svg]:bl" +
            "ock [&_[data-part=item-indicator]>svg]:size-full [&[role=menuitemcheckbox]_[data-part=item-indicator]]:" +
            "border-[length:var(--border-width)] [&[role=menuitemcheckbox]_[data-part=item-indicator]]:border-input " +
            "[&[role=menuitemcheckbox]_[data-part=item-indicator]]:rounded-xs [&[role=menuitemcheckbox][data-state=c" +
            "hecked]_[data-part=item-indicator]]:border-primary [&[role=menuitemcheckbox][data-state=checked]_[data-" +
            "part=item-indicator]]:bg-primary [&[role=menuitemcheckbox][data-state=checked]_[data-part=item-indicato" +
            "r]]:text-primary-foreground [&[role=menuitemcheckbox][data-state=checked]_[data-part=item-indicator]::b" +
            "efore]:content-[''] [&[role=menuitemcheckbox][data-state=checked]_[data-part=item-indicator]::before]:s" +
            "ize-[0.65rem] [&[role=menuitemcheckbox][data-state=checked]_[data-part=item-indicator]::before]:bg-curr" +
            "ent [&[role=menuitemcheckbox][data-state=checked]_[data-part=item-indicator]::before]:[clip-path:polygo" +
            "n(14%_44%,0_65%,38%_100%,100%_22%,84%_6%,38%_68%)] [&[role=menuitemcheckbox][data-state=indeterminate]_" +
            "[data-part=item-indicator]]:border-primary [&[role=menuitemcheckbox][data-state=indeterminate]_[data-pa" +
            "rt=item-indicator]]:bg-primary [&[role=menuitemcheckbox][data-state=indeterminate]_[data-part=item-indi" +
            "cator]]:text-primary-foreground [&[role=menuitemcheckbox][data-state=indeterminate]_[data-part=item-ind" +
            "icator]::before]:content-[''] [&[role=menuitemcheckbox][data-state=indeterminate]_[data-part=item-indic" +
            "ator]::before]:w-[0.65rem] [&[role=menuitemcheckbox][data-state=indeterminate]_[data-part=item-indicato" +
            "r]::before]:h-[0.12rem] [&[role=menuitemcheckbox][data-state=indeterminate]_[data-part=item-indicator]:" +
            ":before]:bg-current [&[role=menuitemradio]_[data-part=item-indicator]]:rounded-full [&[role=menuitemrad" +
            "io][data-state=checked]_[data-part=item-indicator]]:border-[5px] [&[role=menuitemradio][data-state=chec" +
            "ked]_[data-part=item-indicator]]:border-primary [&[role=menuitemradio][data-state=checked]_[data-part=i" +
            "tem-indicator]]:bg-card [&_[data-part=item-text]]:grid [&_[data-part=item-text]]:flex-auto [&_[data-par" +
            "t=item-text]]:[min-inline-size:0] [&_[data-part=item-description]]:text-muted-foreground [&_[data-part=" +
            "item-description]]:text-xs [&_[data-part=item-description]]:[line-height:var(--leading-normal)] [&_[dat" +
            "a-part=shortcut]]:ms-auto [&_[data-part=shortcut]]:text-muted-foreground [&_[data-part=shortcut]]:text-" +
            "xs [&_[data-part=shortcut]]:leading-none [&_[data-part=shortcut]]:whitespace-nowrap"
          }
        >
          Copy
        </ContextMenu.Item>
        <ContextMenu.Separator
          className="
            [block-size:var(--border-width)] [margin-block:var(--space-xs)] [margin-inline:calc(-1_*var(--space-xs))]
            bg-border
          "
        />
        <ContextMenu.Item
          className={
            "flex items-center gap-md w-full py-sm px-md border-0 rounded-[calc(var(--radius-lg)_-_var(--space-xs))]" +
            " [corner-shape:squircle] bg-transparent text-inherit [font-family:inherit] [font-weight:inherit] text-s" +
            "m [line-height:var(--leading-normal)] text-start cursor-pointer select-none appearance-none hover:bg-ac" +
            "cent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground focus-vi" +
            "sible:outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground aria-[disable" +
            "d=true]:opacity-50 aria-[disabled=true]:pointer-events-none data-[disabled]:opacity-50 data-[disabled]:" +
            "pointer-events-none data-[danger]:text-destructive data-[danger]:hover:bg-destructive data-[danger]:hov" +
            "er:text-destructive-foreground data-[danger]:focus-visible:bg-destructive data-[danger]:focus-visible:t" +
            "ext-destructive-foreground data-[danger]:data-[highlighted]:bg-destructive data-[danger]:data-[highligh" +
            "ted]:text-destructive-foreground [&_[data-part=item-icon]]:inline-grid [&_[data-part=item-icon]]:place-" +
            "items-center [&_[data-part=item-icon]]:size-5 [&_[data-part=item-icon]]:flex-[0_0_1.25rem] [&_[data-par" +
            "t=item-indicator]]:inline-grid [&_[data-part=item-indicator]]:place-items-center [&_[data-part=item-ind" +
            "icator]]:size-5 [&_[data-part=item-indicator]]:flex-[0_0_1.25rem] [&_[data-part=item-indicator]>svg]:bl" +
            "ock [&_[data-part=item-indicator]>svg]:size-full [&[role=menuitemcheckbox]_[data-part=item-indicator]]:" +
            "border-[length:var(--border-width)] [&[role=menuitemcheckbox]_[data-part=item-indicator]]:border-input " +
            "[&[role=menuitemcheckbox]_[data-part=item-indicator]]:rounded-xs [&[role=menuitemcheckbox][data-state=c" +
            "hecked]_[data-part=item-indicator]]:border-primary [&[role=menuitemcheckbox][data-state=checked]_[data-" +
            "part=item-indicator]]:bg-primary [&[role=menuitemcheckbox][data-state=checked]_[data-part=item-indicato" +
            "r]]:text-primary-foreground [&[role=menuitemcheckbox][data-state=checked]_[data-part=item-indicator]::b" +
            "efore]:content-[''] [&[role=menuitemcheckbox][data-state=checked]_[data-part=item-indicator]::before]:s" +
            "ize-[0.65rem] [&[role=menuitemcheckbox][data-state=checked]_[data-part=item-indicator]::before]:bg-curr" +
            "ent [&[role=menuitemcheckbox][data-state=checked]_[data-part=item-indicator]::before]:[clip-path:polygo" +
            "n(14%_44%,0_65%,38%_100%,100%_22%,84%_6%,38%_68%)] [&[role=menuitemcheckbox][data-state=indeterminate]_" +
            "[data-part=item-indicator]]:border-primary [&[role=menuitemcheckbox][data-state=indeterminate]_[data-pa" +
            "rt=item-indicator]]:bg-primary [&[role=menuitemcheckbox][data-state=indeterminate]_[data-part=item-indi" +
            "cator]]:text-primary-foreground [&[role=menuitemcheckbox][data-state=indeterminate]_[data-part=item-ind" +
            "icator]::before]:content-[''] [&[role=menuitemcheckbox][data-state=indeterminate]_[data-part=item-indic" +
            "ator]::before]:w-[0.65rem] [&[role=menuitemcheckbox][data-state=indeterminate]_[data-part=item-indicato" +
            "r]::before]:h-[0.12rem] [&[role=menuitemcheckbox][data-state=indeterminate]_[data-part=item-indicator]:" +
            ":before]:bg-current [&[role=menuitemradio]_[data-part=item-indicator]]:rounded-full [&[role=menuitemrad" +
            "io][data-state=checked]_[data-part=item-indicator]]:border-[5px] [&[role=menuitemradio][data-state=chec" +
            "ked]_[data-part=item-indicator]]:border-primary [&[role=menuitemradio][data-state=checked]_[data-part=i" +
            "tem-indicator]]:bg-card [&_[data-part=item-text]]:grid [&_[data-part=item-text]]:flex-auto [&_[data-par" +
            "t=item-text]]:[min-inline-size:0] [&_[data-part=item-description]]:text-muted-foreground [&_[data-part=" +
            "item-description]]:text-xs [&_[data-part=item-description]]:[line-height:var(--leading-normal)] [&_[dat" +
            "a-part=shortcut]]:ms-auto [&_[data-part=shortcut]]:text-muted-foreground [&_[data-part=shortcut]]:text-" +
            "xs [&_[data-part=shortcut]]:leading-none [&_[data-part=shortcut]]:whitespace-nowrap"
          }
        >
          Paste
        </ContextMenu.Item>
      </ContextMenu.Content>
    </ContextMenu.Root>
  );
}
/* app.css */

@layer reset, ui, app, tokens, base, demo, components, utilities, theme;

@import "@gunkin/uiify/styles/reset";

@import "@gunkin/uiify/components/behavior.css";

@import "@gunkin/uiify/styles/tokens";

@import "@gunkin/uiify/styles/tailwind";

@import "tailwindcss/utilities.css";

@theme { --spacing: 0.25rem; --font-weight-normal: 400; --font-weight-medium: 500; --font-weight-semibold: 600; --font-weight-bold: 700; }

@source inline('grid gap-md w-max max-w-[min(28rem,calc(100vw_-_2_*_var(--space-lg)))] p-6 border border-border rounded-xl [corner-shape:squircle] bg-card text-card-foreground shadow-popover [&[popover]:not(:popover-open)]:hidden motion-safe:[&[popover]]:[transition:var(--transition-presence)] motion-safe:[&[popover]:not(:popover-open)]:opacity-0 motion-safe:[&[popover]:not(:popover-open)]:[scale:var(--presence-scale)] motion-safe:starting:[&[popover]:popover-open]:opacity-0 motion-safe:starting:[&[popover]:popover-open]:[scale:var(--presence-scale)] [&[data-positioning=native]]:[--uiify-anchor-offset:var(--space-sm)] [&_[data-positioning=native]]:[--uiify-anchor-offset:var(--space-sm)] [&[popover]:popover-open:not([data-positioning=native])]:[margin:revert] [&_[popover]:popover-open:not([data-positioning=native])]:[margin:revert] flex-col [min-inline-size:12rem] [max-block-size:min(24rem,calc(100dvb_-_2rem))] overflow-y-auto overscroll-contain p-xs border-[length:var(--border-width)] border-solid rounded-lg bg-popover text-popover-foreground [transform-origin:var(--transform-origin,top)] data-[state=closed]:hidden [&:where(:not([popover]),:popover-open)]:flex [&[data-part=sub-content]]:[min-inline-size:11rem] [&[data-part=sub-content][data-side=right]]:[--uiify-anchor-offset:0] [&[data-part=sub-content][data-side=right]]:[inset-block-start:calc(anchor(start)_-_var(--space-xs))] motion-safe:[transition:var(--transition-presence)] motion-safe:[&:not(:popover-open)]:opacity-0 motion-safe:[&:not(:popover-open)]:[scale:var(--presence-scale)] motion-safe:starting:[&:popover-open]:opacity-0 motion-safe:starting:[&:popover-open]:[scale:var(--presence-scale)] flex items-center w-full py-sm px-md border-0 rounded-[calc(var(--radius-lg)_-_var(--space-xs))] bg-transparent text-inherit [font-family:inherit] [font-weight:inherit] text-sm [line-height:var(--leading-normal)] text-start cursor-pointer select-none appearance-none hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground focus-visible:outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground aria-[disabled=true]:opacity-50 aria-[disabled=true]:pointer-events-none data-[disabled]:opacity-50 data-[disabled]:pointer-events-none data-[danger]:text-destructive data-[danger]:hover:bg-destructive data-[danger]:hover:text-destructive-foreground data-[danger]:focus-visible:bg-destructive data-[danger]:focus-visible:text-destructive-foreground data-[danger]:data-[highlighted]:bg-destructive data-[danger]:data-[highlighted]:text-destructive-foreground [&_[data-part=item-icon]]:inline-grid [&_[data-part=item-icon]]:place-items-center [&_[data-part=item-icon]]:size-5 [&_[data-part=item-icon]]:flex-[0_0_1.25rem] [&_[data-part=item-indicator]]:inline-grid [&_[data-part=item-indicator]]:place-items-center [&_[data-part=item-indicator]]:size-5 [&_[data-part=item-indicator]]:flex-[0_0_1.25rem] [&_[data-part=item-indicator]>svg]:block [&_[data-part=item-indicator]>svg]:size-full [&[role=menuitemcheckbox]_[data-part=item-indicator]]:border-[length:var(--border-width)] [&[role=menuitemcheckbox]_[data-part=item-indicator]]:border-input [&[role=menuitemcheckbox]_[data-part=item-indicator]]:rounded-xs [&[role=menuitemcheckbox][data-state=checked]_[data-part=item-indicator]]:border-primary [&[role=menuitemcheckbox][data-state=checked]_[data-part=item-indicator]]:bg-primary [&[role=menuitemcheckbox][data-state=checked]_[data-part=item-indicator]]:text-primary-foreground [&[role=menuitemcheckbox][data-state=checked]_[data-part=item-indicator]::before]:size-[0.65rem] [&[role=menuitemcheckbox][data-state=checked]_[data-part=item-indicator]::before]:bg-current [&[role=menuitemcheckbox][data-state=checked]_[data-part=item-indicator]::before]:[clip-path:polygon(14%_44%,0_65%,38%_100%,100%_22%,84%_6%,38%_68%)] [&[role=menuitemcheckbox][data-state=indeterminate]_[data-part=item-indicator]]:border-primary [&[role=menuitemcheckbox][data-state=indeterminate]_[data-part=item-indicator]]:bg-primary [&[role=menuitemcheckbox][data-state=indeterminate]_[data-part=item-indicator]]:text-primary-foreground [&[role=menuitemcheckbox][data-state=indeterminate]_[data-part=item-indicator]::before]:w-[0.65rem] [&[role=menuitemcheckbox][data-state=indeterminate]_[data-part=item-indicator]::before]:h-[0.12rem] [&[role=menuitemcheckbox][data-state=indeterminate]_[data-part=item-indicator]::before]:bg-current [&[role=menuitemradio]_[data-part=item-indicator]]:rounded-full [&[role=menuitemradio][data-state=checked]_[data-part=item-indicator]]:border-[5px] [&[role=menuitemradio][data-state=checked]_[data-part=item-indicator]]:border-primary [&[role=menuitemradio][data-state=checked]_[data-part=item-indicator]]:bg-card [&_[data-part=item-text]]:grid [&_[data-part=item-text]]:flex-auto [&_[data-part=item-text]]:[min-inline-size:0] [&_[data-part=item-description]]:text-muted-foreground [&_[data-part=item-description]]:text-xs [&_[data-part=item-description]]:[line-height:var(--leading-normal)] [&_[data-part=shortcut]]:ms-auto [&_[data-part=shortcut]]:text-muted-foreground [&_[data-part=shortcut]]:text-xs [&_[data-part=shortcut]]:leading-none [&_[data-part=shortcut]]:whitespace-nowrap [block-size:var(--border-width)] [margin-block:var(--space-xs)] [margin-inline:calc(-1_*var(--space-xs))] bg-border');

@source inline("[&[role=menuitemcheckbox][data-state=checked]_[data-part=item-indicator]::before]:content-[''] [&[role=menuitemcheckbox][data-state=indeterminate]_[data-part=item-indicator]::before]:content-['']");

@layer base { :root { --uiify-control-text: var(--text-sm); } @media (pointer: coarse) { :root { --uiify-control-text: var(--text-base); } } }

/* Authored animation and registered properties used by the Tailwind skin.
 * Visual rules belong in utility recipes; only at-rule definitions live here. */
@layer components {
  @keyframes uiify-presence-in {
    from {
      opacity: 0;
      scale: var(--presence-scale);
    }
  }
}

@layer components {
  @keyframes uiify-presence-out {
    to {
      opacity: 0;
      scale: var(--presence-scale);
    }
  }
}

@property --uiify-spinner-tail {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

@property --uiify-spinner-length {
  syntax: "<angle>";
  inherits: false;
  initial-value: 36deg;
}

@layer components {
  @keyframes uiify-spinner-rotate {
    to {
      transform: rotate(360deg);
    }
  }
}

@layer components {
  @keyframes uiify-spinner-orbit {
    0% {
      --uiify-spinner-tail: 0deg;
      --uiify-spinner-length: 36deg;
    }

    50% {
      --uiify-spinner-tail: 90deg;
      --uiify-spinner-length: 324deg;
    }

    100% {
      --uiify-spinner-tail: 360deg;
      --uiify-spinner-length: 36deg;
    }
  }
}

@layer components {
  @keyframes uiify-skeleton-pulse {
    50% {
      opacity: 0.45;
    }
  }
}

@layer components {
  @keyframes uiify-skeleton-shimmer {
    to {
      background-position: -200% 0;
    }
  }
}

@layer components {
  @keyframes uiify-skeleton-reveal {
    from {
      opacity: 0;
    }
    to {
      opacity: 1;
    }
  }
}

@property --candle-body-height {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 0%;
}

@property --candle-body-top {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 50%;
}

@property --candle-slot {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 100%;
}

@property --candle-wick-height {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 0%;
}

@property --candle-wick-top {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 50%;
}

@property --candle-x {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 0%;
}

@property --candle-scale {
  syntax: "<number>";
  inherits: true;
  initial-value: 1;
}

@property --line-y {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 50%;
}

@property --line-prev-y {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 50%;
}

@property --line-count {
  syntax: "<integer>";
  inherits: false;
  initial-value: 1;
}

@property --line-scale {
  syntax: "<number>";
  inherits: true;
  initial-value: 1;
}

@layer components {
  @keyframes uiify-otp-input-caret-blink {
    50% {
      opacity: 0;
    }
  }
}

API

ContextMenu.Root

PropTypeDefaultDescription
childrenReactNode—The trigger area and the menu content.
defaultOpenboolean—Open state the menu starts in when it is left uncontrolled.
idstring—Id used for the popover element that holds the menu.
onOpenChangePopoverChangeHandler—Called when the menu opens or closes, with the reason and the originating event.
openboolean—Open state of the menu, making it controlled.

ContextMenu.Trigger

PropTypeDefaultDescription
childrenReactNode—Area that opens the menu on right-click.
classNamestring—Class applied to the rendered element.
renderReactElement | ((props, state) => ReactElement)—Replace the rendered element while keeping behavior and accessibility wiring.

ContextMenu.Content

PropTypeDefaultDescription
childrenReactNode—Items, labels and separators of the menu.
classNamestring—Class applied to the rendered element.
renderReactElement | ((props, state) => ReactElement)—Replace the rendered element while keeping behavior and accessibility wiring.

ContextMenu.Item

PropTypeDefaultDescription
childrenReactNode—Content of the item.
classNamestring—Class applied to the rendered element.
disabledbooleanfalseDisables the item and skips it during keyboard navigation.
onSelect(event: MenuSelectEvent) => void—Called when the item is selected; calling preventDefault on the event keeps the menu open.
textValuestring—Text the typeahead matches against when the item's content is not plain text.
renderReactElement | ((props, state) => ReactElement)—Replace the rendered element while keeping behavior and accessibility wiring.

ContextMenu.CheckboxItem

PropTypeDefaultDescription
checkedboolean | "indeterminate"falseChecked state of the item.
childrenReactNode—Content of the item.
classNamestring—Class applied to the rendered element.
disabledbooleanfalseDisables the item and skips it during keyboard navigation.
onCheckedChange(checked: boolean) => void—Called with the new checked state when the item is selected.
textValuestring—Text the typeahead matches against when the item's content is not plain text.

ContextMenu.RadioItem

PropTypeDefaultDescription
value*string—Value this item selects within the menu.
childrenReactNode—Content of the item.
classNamestring—Class applied to the rendered element.
disabledbooleanfalseDisables the item and skips it during keyboard navigation.
textValuestring—Text the typeahead matches against when the item's content is not plain text.

ContextMenu.Label

PropTypeDefaultDescription
childrenReactNode—Text of the group heading.
classNamestring—Class applied to the rendered element.

ContextMenu.Separator

PropTypeDefaultDescription
classNamestring—Class applied to the rendered element.

ContextMenu.Sub

PropTypeDefaultDescription
childrenReactNode—The submenu trigger and its content.

ContextMenu.Sub.Trigger

PropTypeDefaultDescription
childrenReactNode—Label of the submenu trigger.
classNamestring—Class applied to the rendered element.
disabledbooleanfalseDisables the trigger and skips it during keyboard navigation.
textValuestring—Text the typeahead matches against when the item's content is not plain text.

ContextMenu.Sub.Content

PropTypeDefaultDescription
childrenReactNode—Items of the submenu.
classNamestring—Class applied to the rendered element.

Keyboard

KeyAction
Shift+F10 / Menu keyOpen at the focused element
↑ / ↓Rove between items
EscapeClose and return focus

Accessibility

Menu pattern opened at the pointer or keyboard-invoked position.

Styling hooks

data-statedata-highlighted