/* * 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] { --_listbox-item-height: var(--control-height-md); --_listbox-item-px: var(--space-3); --_listbox-item-font-size: var(--font-size-sm); --_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: 1px 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; } [data-listbox][data-block] { display: flex; inline-size: 100%; } [data-listbox][data-orientation='horizontal'] { flex-direction: row; flex-wrap: wrap; align-items: center; } /* ── 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 ─ */ [data-listbox][data-size='xs'] { --_listbox-item-height: var(--control-height-xs); --_listbox-item-px: var(--space-2); --_listbox-item-font-size: var(--font-size-xs); } [data-listbox][data-size='sm'] { --_listbox-item-height: var(--control-height-sm); --_listbox-item-px: var(--space-2); --_listbox-item-font-size: var(--font-size-sm); } [data-listbox][data-size='lg'] { --_listbox-item-height: var(--control-height-lg); --_listbox-item-px: var(--space-4); --_listbox-item-font-size: var(--font-size-md); } [data-listbox][data-size='xl'] { --_listbox-item-height: var(--control-height-xl); --_listbox-item-px: var(--space-5); --_listbox-item-font-size: var(--font-size-lg); } /* ── 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: 0.55; 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(--space-2); min-block-size: var(--_listbox-item-height); padding-block: var(--space-1); 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: 0.6; } /* ── 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: 0.04em; }