You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/src/uix/eidos/components/context-menu/context-menu.css

184 lines
7.0 KiB

/*
* ContextMenu recipe.
*
* Functionally identical to DropdownMenu — same panel chrome, same
* item row treatment. Only differences:
*
* - Trigger is a `<span>` wrapper (NOT a button) that intercepts
* `contextmenu`. The recipe gives it no chrome by default; the
* wrapper is meant to feel inert until invoked.
* - Anchoring is to the cursor position rather than the trigger's
* bounding box — soma's floating layer handles this. The visual
* panel reads identical to DropdownMenu's.
*
* To avoid recipe duplication we lean on attribute selectors that
* cover both `data-dropdown-menu-*` and `data-context-menu-*` for the
* shared parts (Content, Item, Group, Separator, etc.). The Trigger
* gets its own rule since its semantics differ.
*/
/* ── Panel (Content / SubContent) ──────────────────────────────────── */
[data-context-menu-content],
[data-context-menu-sub-content] {
min-inline-size: var(--context-menu-content-min-width, 12rem);
max-block-size: var(--context-menu-content-max-height, min(60vh, 22rem));
overflow-y: auto;
padding: var(--context-menu-content-padding, var(--space-1));
border: var(--context-menu-content-border-width, 1px) solid
var(--context-menu-content-border-color, var(--color-border-default));
border-radius: var(--context-menu-content-radius, var(--radius-md));
background: var(--context-menu-content-bg, var(--color-surface-overlay));
box-shadow: var(--context-menu-content-shadow, var(--shadow-overlay));
font-family: var(--style-label-font-family, var(--font-ui));
font-size: var(--context-menu-content-font-size, var(--font-size-sm));
color: var(--color-content-primary);
}
[data-context-menu-content]:focus-visible,
[data-context-menu-sub-content]:focus-visible {
outline: none;
}
feat(motion): event-driven cascade rollout to the menu family (ContextMenu + Menubar) Extends the just-landed DropdownMenu event-driven item cascade to its siblings so the framework is coherent — every menu in the family fades its items in staggered on open, same mechanism, zero new generator code (rides the container-driven preset branch shipped with DropdownMenu). - ContextMenu (own recipe): Content marks the panel `[data-stagger]` + `--motion-stagger-each: var(--motion-stagger)`; item / checkbox-item / radio-item carry `data-animation-style="fade"`. - Menubar (ONE line): soma `Menubar.Content` delegates to `DropdownMenuContent`, so its panel already carries `data-dropdown-menu-content` (→ the dropdown recipe rhythm applies) and re-uses `DropdownMenu.Item` (→ already `data-animation-style="fade"`). It only adds the `[data-stagger]` marker. Verified at runtime: ContextMenu (scrubbed opacity gradient 0.96/0.84/0.00), Menubar (panel carries `data-stagger` + `data-dropdown-menu-content`, items `name:fade-in`, structural index from `:nth-child` 0/1/2/6/7). `check` clean for the touched components. (Preview tab runs backgrounded → rAF paused; verified by scrubbing `animation.currentTime` via WAAPI rather than waiting for playback.) Deliberately NOT rolled out: select / combobox / command — live-filtered or long-scroll lists where an open cascade is questionable UX. Docs: ContextMenu README (Motion parity note), Menubar index doc, DropdownMenu README (family cross-reference), RFC §D.11.4 (rollout). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
/* Event-driven item cascade rhythm — the Content wrapper marks itself
`[data-stagger]`; each direct-child item carries `data-animation-style` and
gets its structural index from `:nth-child`. `var(--motion-stagger)` is the
canonical 20ms (0 = parallel). Same mechanism as DropdownMenu; the appearance
flows from the `open` (emerge) event — no JS writes the index. */
[data-context-menu-content] {
--motion-stagger-each: var(--motion-stagger);
}
/* ── Item rows ─────────────────────────────────────────────────────── */
[data-context-menu-item],
[data-context-menu-checkbox-item],
[data-context-menu-radio-item],
[data-context-menu-sub-trigger] {
display: flex;
align-items: center;
gap: var(--context-menu-item-gap, var(--space-2));
min-block-size: var(--context-menu-item-height, 2rem);
padding-block: var(--context-menu-item-py, var(--space-1));
padding-inline: var(--context-menu-item-px, var(--space-2));
border-radius: var(--context-menu-item-radius, var(--radius-sm));
color: var(--color-content-primary);
line-height: var(--leading-ui);
cursor: pointer;
user-select: none;
transition:
background var(--duration-fast) var(--ease-default),
color var(--duration-fast) var(--ease-default);
}
[data-context-menu-item]:hover:not([data-disabled]),
[data-context-menu-checkbox-item]:hover:not([data-disabled]),
[data-context-menu-radio-item]:hover:not([data-disabled]),
[data-context-menu-sub-trigger]:hover:not([data-disabled]),
[data-context-menu-item]:focus-visible,
[data-context-menu-checkbox-item]:focus-visible,
[data-context-menu-radio-item]:focus-visible,
[data-context-menu-sub-trigger]:focus-visible,
[data-context-menu-sub-trigger][data-state='open'] {
outline: none;
background: var(--context-menu-item-bg-hover, var(--color-primary-element));
color: var(--context-menu-item-fg-hover, var(--color-content-primary));
}
[data-context-menu-item][data-disabled],
[data-context-menu-checkbox-item][data-disabled],
[data-context-menu-radio-item][data-disabled],
[data-context-menu-sub-trigger][data-disabled] {
cursor: default;
opacity: var(--context-menu-item-disabled-opacity, 0.55);
}
/* Indicator slot + ::before painting for checkbox-item / radio-item shared
with dropdown-menu via `lib/menu-indicator.css` (imported by eidos
entrypoint before this recipe). Override the private vars below if a
per-component tweak is needed. */
[data-context-menu-sub-trigger]::after {
content: '';
margin-inline-start: auto;
inline-size: 0.625rem;
block-size: 0.625rem;
border-inline-end: var(--border-width-medium) solid currentColor;
border-block-start: var(--border-width-medium) solid currentColor;
transform: rotate(45deg);
opacity: var(--opacity-muted);
}
/* ── Group / GroupHeading / Separator ─────────────────────────────── */
[data-context-menu-group] {
display: contents;
}
[data-context-menu-group-heading] {
padding-block: var(--context-menu-heading-py, var(--space-1));
padding-inline: var(--context-menu-heading-px, var(--space-2));
color: var(--color-content-muted);
font-size: var(--context-menu-heading-font-size, var(--font-size-xs));
font-weight: var(--style-label-font-weight, 500);
text-transform: uppercase;
letter-spacing: var(--tracking-caps);
}
[data-context-menu-separator] {
margin-block: var(--context-menu-separator-my, var(--space-1));
block-size: 1px;
background: var(--context-menu-separator-bg, var(--color-border-subtle));
}
/* ── Trigger ──────────────────────────────────────────────────────── */
/*
* Default: no visual treatment. The Trigger is meant to feel inert
* until invoked. Consumers wrap whatever content they want
* right-clickable; the recipe stays out of the way.
*
* `data-state='open'` (set by soma when the menu is open) gets a
* subtle outline as a courtesy affordance — the user often forgets
* which area summoned the menu.
*/
[data-context-menu-trigger] {
display: inline-block;
}
[data-context-menu-trigger][data-state='open'] {
outline: 1px dashed var(--color-border-default);
outline-offset: 2px;
border-radius: var(--radius-sm);
}
/* ── Arrow ────────────────────────────────────────────────────────── */
[data-context-menu-arrow] {
fill: var(--context-menu-content-bg, var(--color-surface-overlay));
}
/* ── State transitions ────────────────────────────────────────────── */
[data-context-menu-content][data-state='closed'],
[data-context-menu-sub-content][data-state='closed'] {
opacity: 0;
pointer-events: none;
}
[data-context-menu-content][data-state='open'],
[data-context-menu-sub-content][data-state='open'] {
animation: context-menu-enter var(--duration-fast) var(--ease-out);
}
@keyframes context-menu-enter {
from {
opacity: 0;
transform: scale(0.96);
}
to {
opacity: 1;
transform: none;
}
}

Powered by TurnKey Linux.