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.
213 lines
6.4 KiB
213 lines
6.4 KiB
/*
|
|
* 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);
|
|
}
|