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/dropdown-menu/dropdown-menu.css

190 lines
7.0 KiB

feat(dropdown-menu): port from soma to eidos (Tier 1 sprint, 3/5) Full 15-part compound — pass-through wrappers over soma with the canonical visual recipe. Parts: Provider · Trigger · Content · Arrow · Item · Group · GroupHeading · Separator · CheckboxItem · CheckboxGroup · RadioGroup · RadioItem · Sub · SubTrigger · SubContent. API parity with Bits UI (richest reference): CheckboxGroup + RadioGroup binding arrays / scalars, nested Sub-menus, Arrow pointer. Radix Primitives + Ark UI lack CheckboxGroup; Chakra v3 + Radix Themes lack Arrow. Recipe (dropdown-menu.css): - Uniform chrome across Content + SubContent (panel shadow, border, padding, max-height) - Items share one set of tokens (hover/focus highlight, disabled opacity, indicator slot via `::before` for Checkbox/Radio data-state='checked') - SubTrigger gets a chevron via `::after` - GroupHeading: muted uppercase, micro-tracked - Separator: 1px subtle divider - Enter animation on `data-state='open'` No `variant` / `color` / `size` knobs at the eidos layer — menus are themselves the visual variant of "list of actions" within an app. Per-item destructive coloring deferred to next pass (cascade via item's `data-color` attr). Morfo scope: ['soma'] → ['soma', 'sema', 'eidos']. Sound comes from the family bases (`emerge` for open/close, `commit` for select) — no per-component cascade needed. Demo: 6-tab template, items hooked to `lastSelected` tracker, all three groupings demoed (Items / CheckboxGroup / RadioGroup / Sub). Checks: svelte-check 0 errors, component:audit 91/91 PASS. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
/*
* DropdownMenu recipe.
*
* Soma anchors the Content via the floating layer (Side / Align /
* collision avoidance). Eidos paints the surface chrome: panel with
* shadow, item hover/focus highlight, indicator slots for
* Checkbox/Radio items, muted Separator and GroupHeading.
*
* [data-dropdown-menu-trigger] → toggle button
* [data-dropdown-menu-content] → floating menu panel
* [data-dropdown-menu-item] → menuitem row
* [data-dropdown-menu-checkbox-item] → menuitemcheckbox row
* [data-dropdown-menu-radio-item] → menuitemradio row
* [data-dropdown-menu-sub-trigger] → menuitem that opens a submenu
* [data-dropdown-menu-sub-content] → nested menu panel
* [data-dropdown-menu-group] → semantic grouping wrapper
* [data-dropdown-menu-group-heading] → muted group label
* [data-dropdown-menu-separator] → 1px divider
* [data-dropdown-menu-arrow] → optional anchor pointer
*
* All highlighted-row visuals share one set of tokens so a fly-out
* SubContent reads identically to the root Content.
*/
/* ── Panel (Content / SubContent) ──────────────────────────────────── */
[data-dropdown-menu-content],
[data-dropdown-menu-sub-content] {
min-inline-size: var(--dropdown-menu-content-min-width, 12rem);
max-block-size: var(--dropdown-menu-content-max-height, min(60vh, 22rem));
overflow-y: auto;
padding: var(--dropdown-menu-content-padding, var(--space-1));
border: var(--dropdown-menu-content-border-width, 1px) solid
var(--dropdown-menu-content-border-color, var(--color-border-default));
border-radius: var(--dropdown-menu-content-radius, var(--radius-md));
background: var(--dropdown-menu-content-bg, var(--color-surface-overlay));
box-shadow: var(--dropdown-menu-content-shadow, var(--shadow-floating));
font-family: var(--style-label-font-family, var(--font-ui));
font-size: var(--dropdown-menu-content-font-size, var(--font-size-sm));
color: var(--color-content-primary);
}
[data-dropdown-menu-content]:focus-visible,
[data-dropdown-menu-sub-content]:focus-visible {
outline: none;
}
/* ── Item rows ─────────────────────────────────────────────────────── */
[data-dropdown-menu-item],
[data-dropdown-menu-checkbox-item],
[data-dropdown-menu-radio-item],
[data-dropdown-menu-sub-trigger] {
display: flex;
align-items: center;
gap: var(--dropdown-menu-item-gap, var(--space-2));
min-block-size: var(--dropdown-menu-item-height, 2rem);
padding-block: var(--dropdown-menu-item-py, var(--space-1));
padding-inline: var(--dropdown-menu-item-px, var(--space-2));
border-radius: var(--dropdown-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-dropdown-menu-item]:hover:not([data-disabled]),
[data-dropdown-menu-checkbox-item]:hover:not([data-disabled]),
[data-dropdown-menu-radio-item]:hover:not([data-disabled]),
[data-dropdown-menu-sub-trigger]:hover:not([data-disabled]),
[data-dropdown-menu-item]:focus-visible,
[data-dropdown-menu-checkbox-item]:focus-visible,
[data-dropdown-menu-radio-item]:focus-visible,
[data-dropdown-menu-sub-trigger]:focus-visible,
[data-dropdown-menu-sub-trigger][data-state='open'] {
outline: none;
background: var(--dropdown-menu-item-bg-hover, var(--color-primary-element));
color: var(--dropdown-menu-item-fg-hover, var(--color-content-primary));
}
[data-dropdown-menu-item][data-disabled],
[data-dropdown-menu-checkbox-item][data-disabled],
[data-dropdown-menu-radio-item][data-disabled],
[data-dropdown-menu-sub-trigger][data-disabled] {
cursor: default;
opacity: var(--dropdown-menu-item-disabled-opacity, 0.55);
}
/* Indicator slot for Checkbox/Radio — soma renders the Indicator child
* inline. The recipe reserves a leading affordance for the glyph. */
[data-dropdown-menu-checkbox-item],
[data-dropdown-menu-radio-item] {
padding-inline-start: var(--dropdown-menu-item-indicator-inset, calc(var(--space-2) + 1rem));
position: relative;
}
[data-dropdown-menu-checkbox-item][data-state='checked']::before,
[data-dropdown-menu-radio-item][data-state='checked']::before {
content: '';
position: absolute;
inset-inline-start: var(--dropdown-menu-item-px, var(--space-2));
inline-size: 0.875rem;
block-size: 0.875rem;
border-radius: 0.2rem;
background: var(--color-primary-solid);
mask-image: var(--dropdown-menu-check-mask, none);
}
[data-dropdown-menu-radio-item][data-state='checked']::before {
border-radius: 50%;
}
/* SubTrigger gets a chevron affordance on the trailing edge. */
[data-dropdown-menu-sub-trigger]::after {
content: '';
margin-inline-start: auto;
inline-size: 0.625rem;
block-size: 0.625rem;
border-inline-end: 2px solid currentColor;
border-block-start: 2px solid currentColor;
transform: rotate(45deg);
opacity: 0.65;
}
/* ── Group / GroupHeading / Separator ─────────────────────────────── */
[data-dropdown-menu-group] {
display: contents;
}
[data-dropdown-menu-group-heading] {
padding-block: var(--dropdown-menu-heading-py, var(--space-1));
padding-inline: var(--dropdown-menu-heading-px, var(--space-2));
color: var(--color-content-muted);
font-size: var(--dropdown-menu-heading-font-size, var(--font-size-xs));
font-weight: var(--style-label-font-weight, 500);
text-transform: uppercase;
letter-spacing: 0.04em; /* literal: micro-tracking for caps */
}
[data-dropdown-menu-separator] {
margin-block: var(--dropdown-menu-separator-my, var(--space-1));
block-size: 1px;
background: var(--dropdown-menu-separator-bg, var(--color-border-subtle));
}
/* ── Trigger (button-ish, but bring-your-own visual) ──────────────── */
[data-dropdown-menu-trigger] {
cursor: pointer;
}
[data-dropdown-menu-trigger]:focus-visible {
outline: none;
box-shadow: var(--focus-ring);
}
/* ── Arrow ────────────────────────────────────────────────────────── */
[data-dropdown-menu-arrow] {
fill: var(--dropdown-menu-content-bg, var(--color-surface-overlay));
}
/* ── State transitions ────────────────────────────────────────────── */
[data-dropdown-menu-content][data-state='closed'],
[data-dropdown-menu-sub-content][data-state='closed'] {
opacity: 0;
pointer-events: none;
}
[data-dropdown-menu-content][data-state='open'],
[data-dropdown-menu-sub-content][data-state='open'] {
animation: dropdown-menu-enter var(--duration-fast) var(--ease-out);
}
@keyframes dropdown-menu-enter {
from {
opacity: 0;
transform: translateY(-4px) scale(0.97);
}
to {
opacity: 1;
transform: none;
}
}

Powered by TurnKey Linux.