Getting StartedInstall and bootstrap headless foundations 4
LayoutHeadless structural primitives for expandable and container patterns 6
OverlayHeadless modal and floating layer behavior 3
FeedbackHeadless notification and status communication patterns 5
FormHeadless input and selection contracts 17
UtilityReusable action, identity, and clipboard behavior 6
NavigationHeadless trails, trees, menus, and command surfaces 7

Styling contract

Headless context menu styling stays on public slot and state hooks. The primitive does not position the panel for you, so owners usually pair those hooks with either anchored CSS or pointer-aware inline styles derived from the exported instance.

CSS contract table

SelectorApplied onPurpose
[data-slot='context-menu-trigger']Context target surfaceRight-click affordance, focus ring, and expanded target styling.
[data-slot='menu']Context menu panelSurface, placement, elevation, and open-state display rules.
[data-slot='menu-item']Action rowSpacing, hover affordance, and keyboard active treatment.
[data-slot='menu-item'][data-active]Active action rowReflects the arrow-key cursor managed by tngMenu.
[data-slot='menu-group-label'], [data-slot='menu-separator']Menu sub-elementsSection labeling and separation for larger contextual action groups.

Context-menu contract starter

css
.context-shell {
  position: relative;
}

[data-slot="context-menu-trigger"] {
  cursor: context-menu;
}

.context-shell [data-slot="menu"] {
  position: absolute;
  left: 0;
  top: calc(100% + 0.42rem);
}

.context-shell [data-slot="menu"][hidden] {
  display: none !important;
}

.context-shell [data-slot="menu-item"][data-active],
.context-shell [data-slot="menu-item"][aria-expanded="true"] {
  background: color-mix(in srgb, var(--tng-semantic-accent-brand) 15%, transparent);
}

Pointer placement

For true cursor-relative placement, read getPointerAnchor() from the exported context-menu instance after open. Keyboard opens return null, so owners usually fall back to an element-relative placement rule.

Pointer-aware placement sketch

ts
readonly panelStyle = signal<Record<string, string>>({ position: 'absolute' });

syncPlacement(menu: TngContextMenu): void {
  const pointerAnchor = menu.getPointerAnchor();

  if (pointerAnchor !== null) {
    this.panelStyle.set({
      position: 'fixed',
      left: `${pointerAnchor.x}px`,
      top: `${pointerAnchor.y}px`,
    });
    return;
  }

  this.panelStyle.set({
    position: 'absolute',
    left: '0',
    top: 'calc(100% + 0.42rem)',
  });
}

State selectors

Style the trigger through aria-expanded, active rows through data-active, and submenu owners through aria-expanded on the item that owns a nested menu.

State selector starter

css
[data-slot="context-menu-trigger"][aria-expanded="true"] {
  border-color: color-mix(in srgb, var(--tng-semantic-accent-brand) 45%, transparent);
}

[data-slot="menu-item"][data-active],
[data-slot="menu-item"][aria-expanded="true"] {
  color: color-mix(
    in srgb,
    var(--tng-semantic-accent-brand) 72%,
    var(--tng-semantic-foreground-primary)
  );
}

[data-slot="menu-item"][aria-disabled="true"] {
  cursor: default;
  opacity: 0.55;
}