Overlay · ContextMenu

ContextMenu

Right-click / long-press / Shift+F10 menu. Same visual recipe as DropdownMenu — the only differences are the trigger surface (contextmenu event on a wrapped region rather than a button) and the anchoring (cursor position rather than trigger box). 14 parts, 3 semantic events, full Items / CheckboxItem / RadioGroup / Sub-menu parity with the dropdown family.

parts{compiled.parts.order.length} events{events.length} apg menu ↗ {#if lastSelected} last{lastSelected} {/if}
{triggerText}
{#if showGroupHeading} Edit Copy Paste Cut {:else} Copy Paste Cut {/if} {#if showSeparators}{/if} {#if showCheckboxes} Show grid Snap to grid {/if} {#if showSeparators && showCheckboxes}{/if} {#if showSub} {subTriggerText} {#if showRadio} PNG JPG WebP {:else} Bold Italic {/if} {/if} {#if showSeparators}{/if} Delete {#if showArrow}{/if}
trace {#if trace.length === 0} right-click the area to summon the menu {:else} {#each trace.slice(0, 3) as entry} {entry.event} · {entry.family}{entry.intent ? ' · ' + entry.intent : ''} {fmtTime(entry.at)} {/each} {/if} open {String(open)} {#if lastSelected} last {lastSelected} {/if}
{#if tab === 'live'}

Controls

ContextMenu is a pure pass-through over soma; the eidos layer adds no evaluative props. Knobs are grouped by the layer that owns the prop, plus a composition group for parts toggled in / out of the markup (per the composition-over-visibility norm). Semantic events live in the { e.preventDefault(); tab = 'sema'; }}>Sema tab.

soma Provider props
soma Content props · floating layer · cursor-anchored so side/align act as a bias hint
soma Item props · applied to the Delete item below
Composition · toggle parts in / out of the markup (no visibility props)
Bound state · mirrors of `bind:checked` / `bind:value` from the live menu items
Demo content · literal labels rendered into the menu
soma headless · cursor-anchored, contextmenu interception svelte
{somaSnippet}
eidos visual · pure pass-through (no extra props at the visual layer) svelte
{eidosSnippet}
{/if} {#if tab === 'api'}

API reference

14 parts, all forwarded verbatim from soma — eidos adds no evaluative props. Item-family parts (Item / CheckboxItem / RadioItem / SubTrigger) share id, disabled, textValue, closeOnSelect; the other props are listed per part below.

ContextMenu (Provider)
PropTypeDefaultDescription
idstringautoStable identifier. Used to wire aria-labelledby / aria-controls.
open bindablebooleanfalseWhether the menu is open.
onOpenChange(open: boolean) => void—Callback on open state change.
onOpenChangeComplete(open: boolean) => void—Fires after the open/close animation settles.
dir'ltr' | 'rtl''ltr'Direction used by keyboard navigation in horizontal sub-menus.
Trigger
PropTypeDefaultDescription
idstringauto—
disabledbooleanfalseDisable opening. contextmenu bubbles to default browser menu.
Content
PropTypeDefaultDescription
forceMountbooleanfalseKeep in DOM when closed (CSS transitions).
side'top' | 'right' | 'bottom' | 'left''bottom'Preferred side relative to the anchor (cursor on open).
sideOffsetnumber0Pixels between content and anchor on the main axis.
align'start' | 'center' | 'end''start'Cross-axis alignment.
alignOffsetnumber0Pixels along the cross axis.
arrowPaddingnumber0Distance from the panel corner allowed for the Arrow.
avoidCollisionsbooleantrueFlip / shift when the preferred placement clips the viewport.
collisionBoundaryElement | Element[]viewportBoundary the floating math respects.
collisionPaddingnumber | Partial<Record<Side, number>>0Inner inset from the boundary edges.
sticky'partial' | 'always''partial'How the panel sticks while the anchor scrolls.
hideWhenDetachedbooleantrueHide if anchor scrolls fully out of view.
updatePositionStrategy'optimized' | 'always''optimized'How often the floating math recomputes.
strategy'fixed' | 'absolute''fixed'CSS positioning strategy.
loopbooleanfalseArrowDown / ArrowUp wrap at boundaries.
trapFocusbooleantrueTab cycles inside Content.
preventScrollbooleanfalseBody scroll lock. Context menus do NOT lock by default.
escapeKeydownBehavior'close' | 'ignore' | 'defer-otherwise-close' | 'defer-otherwise-ignore''close'How Escape interacts with the dismissal layer.
interactOutsideBehaviorsame as above'close'Click / touch outside response.
onOpenAutoFocus / onCloseAutoFocus(e: Event) => void—Imperative focus hooks. Call e.preventDefault() to opt out.
onEscapeKeydown / onInteractOutside / onFocusOutsidehandlers—Cancellable dismissal events.
dir'ltr' | 'rtl'inheritsOverrides Provider's dir for this panel.
Item · CheckboxItem · RadioItem · SubTrigger
PropTypeDefaultApplies toDescription
disabledbooleanfalseallSkip in keyboard nav; aria-disabled='true'.
textValuestring—allOverride the text used for typeahead search.
onSelect() => void—Item, CheckboxItem, RadioItemFires on activation (click / Enter / Space).
closeOnSelectbooleantrueItem, CheckboxItem, RadioItemClose the menu after activation.
checked bindablebooleanfalseCheckboxItemWhether the checkbox is checked.
onCheckedChange(checked: boolean) => void—CheckboxItemCallback on check state change.
valuestring (required)—RadioItemIdentifies the radio inside the parent RadioGroup.
RadioGroup
PropTypeDefaultDescription
value bindablestring—Currently-selected RadioItem value.
onValueChange(value: string) => void—Callback on selection change.
Sub · SubTrigger · SubContent
PropTypeDefaultApplies toDescription
open bindablebooleanfalseSubIndependent open state for the sub-menu.
onOpenChange(open: boolean) => void—Sub—
side'top' | 'right' | 'bottom' | 'left''right'SubContentDefault direction for the fly-out.
align / alignOffset / sideOffsetlike Content—SubContent—
avoidCollisions / collisionBoundary / collisionPaddinglike Content—SubContent—
loopbooleanfalseSubContentWrap keyboard nav inside the fly-out.
Group · GroupHeading · Separator · Arrow
PartPropsNotes
GroupidWraps a logical block. aria-labelledby wires to GroupHeading.
GroupHeadingidNon-interactive caption above grouped items.
Separatoridrole='separator', aria-orientation='horizontal'.
Arrowwidth, heightOptional SVG pointer rendered inside Content.
Reference parity
FeatureRadixbits-uiark-uiEidos (this)
Right-click + long-press✓✓✓✓
Shift+F10 keyboard summon✓✓✓✓
Cursor-anchored positioning✓✓✓✓
CheckboxItem / RadioGroup / RadioItem✓✓✓✓
Nested Sub menus✓✓✓✓
Group / GroupHeading✓✓✓✓
Arrow part✓✓—✓
closeOnSelect per item✓✓—✓
Typeahead search✓✓✓✓
commit-select semantic event———✓
{/if} {#if tab === 'morfo'}

morfo · declarative contract

14 parts + 3 events. open / close emit on Content (family emerge); commit-select emits on Item with intent affirm (semantic sound from the `commit` family base). Source: src/uix/morfo/components/context-menu.ts.

FieldValue
name"{contextMenuMorfo.name}"
kebab"{contextMenuMorfo.kebab}"
scope[{contextMenuMorfo.scope.map((s) => `"${s}"`).join(', ')}]
apg{contextMenuMorfo.apg}
parts.length{contextMenuMorfo.parts.length}
events.length{contextMenuMorfo.events?.length ?? 0}
Parts
{#each partsList as part (part.kebab)} {/each}
PartMarkerElementRoleArchetypeStatesOptional
{part.kebab} [{part.marker}] <{part.defaultElement}> {part.role ?? '—'} {part.archetype ?? '—'} {part.states.length ? part.states.join(' | ') : '—'} {part.optional ? 'yes' : 'no'}
{#each contextMenuMorfo.parts as rawPart (rawPart.kebab)} {@const partAny = rawPart as unknown as { kebab: string; aria?: ReadonlyArray<{ attr: string; value: { kind: string }; condition?: { when: string; prop?: string; part?: string }; severity?: string }>; keyboard?: ReadonlyArray<{ key: string; action: string }> }} {@const dataAttrs = compiled.contracts.dataAttrsByPart.get(partAny.kebab) ?? []} {@const ariaAttrs = partAny.aria ?? []} {@const keyboard = partAny.keyboard ?? []} {#if dataAttrs.length || ariaAttrs.length || keyboard.length}
{partAny.kebab}
{#if dataAttrs.length}
{#each dataAttrs as attr (attr.attr)} {/each}
data-attrValuesSource kind
{attr.attr} {attr.values ? attr.values.join(' | ') : '—'} {'value' in attr && attr.value ? (attr as { value: { kind: string } }).value.kind : '—'}
{/if} {#if ariaAttrs.length}
{#each ariaAttrs as a (a.attr)} {/each}
aria-attrSource kindConditionSeverity
{a.attr} {a.value.kind} {a.condition ? `when ${a.condition.when}${a.condition.prop ? ` (${a.condition.prop})` : ''}${a.condition.part ? ` (${a.condition.part})` : ''}` : 'always'} {a.severity ?? 'required'}
{/if} {#if keyboard.length}
{#each keyboard as kb (kb.key)} {/each}
keyaction
{kb.key === ' ' ? 'Space' : kb.key} {kb.action}
{/if} {/if} {/each}
Events declaration

open / close fire pre the state flip (so sema's perceptual signal precedes the DOM mutation), commit-select fires post activation with static intent='affirm'. Play any signal from the { e.preventDefault(); tab = 'sema'; }}>Sema tab.

{#each events as action (action.name)} {@const sem = action.semantic} {@const intentDecl = 'intent' in sem ? sem.intent : undefined} {@const intentStr = typeof intentDecl === 'string' ? intentDecl : intentDecl ? `fromProp:${(intentDecl as { fromProp?: string }).fromProp ?? '?'}` : '—'} {/each}
namefamilyverbsequenceintenttargetprewritecommit
{action.name} {sem.family} {sem.verb ?? '—'} {sem.sequence ?? 'pre'} {intentStr} {action.target} {action.prewrite.length ? action.prewrite.map((p) => `${p.attr}=${p.value}`).join(', ') : '—'} {action.commit ? `${action.commit.attr}=${action.commit.value}` : '—'}
{/if} {#if tab === 'sema'}

sema · events + perceptual signature

Three events. emerge·open and emerge·close share the floating-overlay perceptual signature with DropdownMenu / Popover / Tooltip; commit·select with intent='affirm' inherits the affirmative-commit signature. Press play to fire the signal on the live menu — open the menu first so the Content / Item targets exist in the DOM.

{#each events as action (action.name)} {@const intentDecl = 'intent' in action.semantic ? action.semantic.intent : undefined} {@const effectiveIntent = typeof intentDecl === 'string' ? intentDecl : undefined} {/each}
NameFamilyVerbSequenceIntentTarget partPlay
{action.name} {action.semantic.family} {action.semantic.verb ?? '—'} {action.semantic.sequence ?? 'pre'} {effectiveIntent ?? '—'} {action.target}
{/if} {#if tab === 'recipe'}

Eidos recipe

Source: src/uix/eidos/components/context-menu/context-menu.css. Visually identical to DropdownMenu — panel chrome, item rows, indicator slots and submenu chevrons share the same vocabulary. The only diverging rule is the Trigger: it renders inert by default and gets a subtle dashed outline when data-state='open' (courtesy affordance — "the menu came from this region"). All selectors target morfo-emitted markers — eidos owns no extra data-attrs.

SelectorSourceConcern
[data-context-menu-content], [data-context-menu-sub-content]morfoPanel chrome (border, radius, surface, shadow).
[data-context-menu-content][data-state='open' | 'closed']morfoEnter / exit transitions via state-driven keyframes.
[data-context-menu-item], [data-context-menu-checkbox-item], [data-context-menu-radio-item], [data-context-menu-sub-trigger]morfoRow layout, padding, gap, hover background.
[data-context-menu-item][data-disabled]morfoDisabled item opacity / cursor.
[data-context-menu-checkbox-item][data-state='checked']::beforemorfoCheck indicator (rounded square).
[data-context-menu-radio-item][data-state='checked']::beforemorfoRadio indicator (circle).
[data-context-menu-sub-trigger]::aftereidosRight-arrow chevron pure CSS — no part required.
[data-context-menu-group-heading]morfoUppercase micro-caption above grouped items.
[data-context-menu-separator]morfo1px subtle divider line.
[data-context-menu-trigger][data-state='open']eidosDashed outline cue while the menu is open.
[data-context-menu-arrow]morfoSVG fill matched to panel surface.
{/if} {#if tab === 'a11y'}

Accessibility

Follows the APG menu pattern. No native ARIA pattern exists for cursor-summoned menus; the keyboard-summoned path uses Shift+F10 per WCAG 2.1 (keyboard parity for pointer-only triggers).

Summon
InputEffect
Right-clickOpen the menu at cursor position.
Ctrl+Click (macOS)Same as right-click.
Long-press (touch)Same after the platform's long-press delay.
Shift+F10Open the menu via keyboard, anchored at the focused trigger.
ContextMenu keySame (Windows / Linux keyboards).
Inside the menu
KeyAction
ArrowDown / ArrowUpMove focus to next / previous item.
Home / EndFirst / last item.
Enter / SpaceActivate the focused item.
EscapeClose the menu; return focus to the trigger.
ArrowRight on SubTriggerOpen the sub-menu (or ArrowLeft in RTL).
ArrowLeft inside SubContentClose sub-menu, return to parent (or ArrowRight in RTL).
TypeaheadLetters jump to the first item starting with that prefix (uses textValue when set).
ARIA contract
PartAttributeValue
triggeraria-haspopup"menu"
triggeraria-expanded{`{open}`}
triggeraria-controls{``}
contentrole"menu"
contentaria-orientation"vertical"
contentaria-labelledby{``}
itemrole"menuitem"
checkbox-itemrole / aria-checked"menuitemcheckbox" / {`{checked}`}
radio-itemrole / aria-checked"menuitemradio" / {`{checked}`}
sub-triggeraria-haspopup / aria-expanded / aria-controls"menu" / {`{open}`} / {``}
sub-contentrole / aria-labelledby"menu" / {``}
separatorrole / aria-orientation"separator" / "horizontal"
grouprole / aria-labelledby"group" / {``}
{/if}