/* * Listbox recipe. * * [data-listbox] → root container * [data-listbox-item] → option row * [data-listbox-item-indicator] → ✓ glyph on selected items * [data-listbox-group] → group container * [data-listbox-group-label] → muted group label * * Sizes — xs..xl on the root; cascades to item height + font-size. * Variant `'surface' | 'outline' | 'ghost'` (ControlVariant). * Color cascades the accent for selected-item highlight + focus ring. * * Cross-component reuse: * - The ItemIndicator visual is the canonical check glyph — same as * [data-checkbox-indicator] / menu-item indicators. Painted here as * a filled accent square with white ✓ on selected items. */ [data-listbox] { /* Size rhythm bridged to the shared list-surface scale (1:1 with the control * sizes) — the SINGLE source in `lib/list-surface.css`. The root carries * `data-list-surface` + `data-size` (stamped by the eidos wrapper), so that * block sets `--list-*`; the private knobs below just adopt them. This aligns * md to the list rhythm (font sm→md, px space-3→space-2) — shared with the * menu surfaces. */ --_listbox-item-height: var(--list-item-height); --_listbox-item-px: var(--list-item-px); --_listbox-item-py: var(--list-item-py); --_listbox-item-gap: var(--list-item-gap); --_listbox-item-font-size: var(--list-font-size); --_listbox-max-height: min(60vh, 22rem); --_listbox-accent: var(--color-primary-solid); --_listbox-accent-soft: var(--color-primary-element); --_listbox-accent-text: var(--color-content-on-solid); display: flex; flex-direction: column; gap: var(--space-1); padding: var(--space-1); border: var(--border-width) solid var(--color-border-default); border-radius: var(--radius-md); background: var(--color-surface-default); color: var(--color-content-primary); font-family: var(--font-ui); font-size: var(--_listbox-item-font-size); line-height: var(--leading-ui); outline: none; /* * Vertical scroll for tall lists. Default cap is min(60vh, 22rem) — * generous but bounded. Consumer overrides via inline style * (e.g. `style="--_listbox-max-height: 40rem;"`) or by wrapping in a * fixed-height container. */ max-block-size: var(--_listbox-max-height); overflow-y: auto; overscroll-behavior: contain; } [data-listbox][data-block] { display: flex; inline-size: 100%; } [data-listbox][data-orientation='horizontal'] { flex-direction: row; flex-wrap: wrap; align-items: center; /* Single row across the viewport — scroll inline, not block. */ max-block-size: none; overflow-y: visible; overflow-x: auto; } /* ── Variant — surface (default) ─ */ [data-listbox][data-variant='surface'] { background: var(--color-surface-default); border-color: var(--color-border-default); } [data-listbox][data-variant='outline'] { background: transparent; border-color: var(--color-border-default); } [data-listbox][data-variant='ghost'] { background: transparent; border-color: transparent; } /* Sizes — the xs..xl rhythm is bridged from `lib/list-surface.css` via the * private `--_listbox-*` knobs in the root block above; no per-size block here. */ /* ── Color cascades ─ */ [data-listbox][data-color='secondary'] { --_listbox-accent: var(--color-secondary-solid); --_listbox-accent-soft: var(--color-secondary-element); } [data-listbox][data-color='affirm'] { --_listbox-accent: var(--color-affirm-solid); --_listbox-accent-soft: var(--color-affirm-element); } [data-listbox][data-color='risk'] { --_listbox-accent: var(--color-risk-solid); --_listbox-accent-soft: var(--color-risk-element); } [data-listbox][data-color='threat'] { --_listbox-accent: var(--color-threat-solid); --_listbox-accent-soft: var(--color-threat-element); } [data-listbox][data-color='fulfill'] { --_listbox-accent: var(--color-fulfill-solid); --_listbox-accent-soft: var(--color-fulfill-element); } [data-listbox][data-color='loss'] { --_listbox-accent: var(--color-loss-solid); --_listbox-accent-soft: var(--color-loss-element); } [data-listbox][data-color='neutral'] { --_listbox-accent: var(--color-neutral-solid); --_listbox-accent-soft: var(--color-neutral-element); } [data-listbox][data-disabled] { opacity: var(--opacity-disabled); cursor: not-allowed; } [data-listbox][data-invalid] { border-color: var(--color-threat-element); } [data-listbox]:focus-visible, [data-listbox][data-focused] { outline: var(--focus-ring-width) solid var(--focus-ring-color); outline-offset: var(--focus-ring-offset); } /* ── Item ─ */ [data-listbox-item] { display: flex; align-items: center; gap: var(--_listbox-item-gap); min-block-size: var(--_listbox-item-height); padding-block: var(--_listbox-item-py); padding-inline: var(--_listbox-item-px); border-radius: var(--radius-sm); color: var(--color-content-primary); cursor: pointer; user-select: none; transition: background var(--duration-fast) var(--ease-default), color var(--duration-fast) var(--ease-default); } [data-listbox-item][data-highlighted]:not([data-disabled]) { background: var(--color-surface-overlay); } [data-listbox-item][data-selected]:not([data-disabled]) { background: var(--_listbox-accent-soft); color: var(--color-content-primary); } [data-listbox-item][data-selected][data-highlighted]:not([data-disabled]) { background: var(--_listbox-accent-soft); } [data-listbox-item][data-disabled] { color: var(--color-content-muted); cursor: default; opacity: var(--opacity-disabled); } /* ── ItemIndicator (✓ glyph on selected items) ─ * * Painted as a 0.875rem filled-accent rounded square with white check — * same shape as the menu-indicator partial used by Dropdown/Context * Menu CheckboxItems. The ItemIndicator is rendered by the consumer * (soma emits the data-attr; visibility is gated to selected). */ [data-listbox-item-indicator] { display: none; inline-size: 0.875rem; block-size: 0.875rem; flex: 0 0 auto; color: var(--_listbox-accent); } [data-listbox-item][data-selected] [data-listbox-item-indicator] { display: inline-flex; align-items: center; justify-content: center; } /* ── Group / GroupLabel ─ */ [data-listbox-group] { display: flex; flex-direction: column; gap: var(--space-1); } [data-listbox-group-label] { padding-block: var(--space-1); padding-inline: var(--_listbox-item-px); color: var(--color-content-muted); font-size: var(--font-size-xs); font-weight: 500; text-transform: uppercase; letter-spacing: var(--tracking-caps); }