Styling contract
Menu styling stays public. The primitive emits stable data-slot values on the trigger, panel, items, and optional backdrop so you can theme plain CSS or Tailwind shells without reaching into internal nodes.
CSS contract table
| Selector | Applied on | Purpose |
|---|---|---|
[data-slot='menu-trigger'] | Trigger host | Button or control surface, hover/focus, and open state via aria-expanded. |
[data-slot='menu'] | Panel host | Surface, placement, elevation; pair with data-state='open' | 'closed'. |
[data-slot='menu'][hidden] | Closed panel | Prefer letting hidden win; avoid unconditional display:grid on the host. |
[data-slot='menu-item'] | Row | Row spacing, hover, and data-active for the keyboard cursor. |
[data-slot='menu-backdrop'] | Optional scrim | Full-bleed dismiss layer when you pair a backdrop with the same menu instance. |
Menu contract tokens (excerpt)
css
[data-slot='menu-trigger'],
[data-slot='menu'][aria-labelledby] {
--tng-menu-radius: 0.75rem;
--tng-menu-padding: 0.4rem;
--tng-menu-item-py: 0.5rem;
--tng-menu-item-px: 0.75rem;
--tng-menu-border: var(--tng-semantic-border-subtle, #e5e7eb);
--tng-menu-brand: var(--tng-semantic-accent-brand, #2563eb);
--tng-menu-shadow:
0 12px 24px -12px rgba(15, 23, 42, 0.22),
0 18px 40px -18px rgba(15, 23, 42, 0.18);
}
[data-slot='menu'][data-state='open'] {
border-radius: var(--tng-menu-radius);
box-shadow: var(--tng-menu-shadow);
}
Theme tokens
| Variable | Purpose |
|---|---|
--tng-menu-radius, --tng-menu-padding | Panel rounding and inner padding for item grids. |
--tng-menu-item-px, --tng-menu-item-py | Row gutters inside the open panel. |
--tng-menu-border, --tng-menu-border-strong | Neutral strokes for triggers and panels. |
--tng-menu-brand, --tng-menu-focus-ring | Accent for active rows, focus rings, and open trigger emphasis. |
--tng-menu-bg, --tng-menu-surface | Canvas and elevated surfaces for triggers and panels. |
The shared Tailwind Menu contract in @tailng-ui/theme defines these tokens on [data-slot='menu-trigger'] and labelled panels; reuse or override them in your shell.
State selectors
Style open triggers with aria-expanded, active rows with data-active, and submenu affordances with aria-expanded on items that own nested panels.
State selector starter
css
[data-slot='menu-trigger'][aria-expanded='true'] {
border-color: var(--tng-menu-brand);
}
[data-slot='menu-item'][data-active],
[data-slot='menu-item'][aria-expanded='true'] {
color: #1d4ed8;
background: rgba(37, 99, 235, 0.16);
}
[data-slot='menu-item'][aria-disabled='true'] {
opacity: 0.5;
cursor: not-allowed;
}