uix(color): abre `color` en listbox · timeline · combobox

Migra los 3 al forward de paleta compartido, cada uno con su matiz:

- listbox: 2 slots (solid = indicador activo, element = fila seleccionada), raíz
  bare `[data-listbox]`. El `accent-text` era código muerto (sin consumidor) →
  eliminado. Recipe nuevo.
- timeline: 5 slots (solid/track/border/contrast/text) que alimentan
  `--_timeline-hue-*` del item; data-intent por item sigue mandando sobre el
  acento del root. Tipo ProgressiveColorRole → ComponentColorProp. Recipe
  existente ampliado.
- combobox: la cascada vivía en CUATRO partes (control/input/trigger/content),
  así que el forward las cubre vía `parts: [...]` (forma declarations[]). 3 slots
  (track/border/text). Los 5 wrappers (root + 4 partes) pasan por
  `resolveComponentColor` para que el custom hex no se estampe crudo. Recipe
  existente (content-z) ampliado.

Verificado en navegador: combobox data-color=risk → palette-border del control =
token risk compartido; custom #00b894 → color-mix. table (lote previo) idem.
check: 76 ajenos, 0 propios. Contract 26/28 (2 = proof-of-human pre-existente).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
alpha-0.1-sec-dom
dev 3 months ago
parent c5699bd019
commit 528ddaa7f9

@ -2,6 +2,7 @@
import { ActiveEidos } from '$uix/eidos';
import * as Combobox from '$soma/components/combobox';
import { composeInlineStyle } from '$uix/eidos/lib/style';
import { resolveComponentColor } from '$uix/eidos/lib/component-color';
import { getComboboxVisualContext } from './context.svelte';
import '../../lib/list-surface.css';
import type { ComboboxContentProps } from './types';
@ -22,6 +23,7 @@
const resolvedSize = $derived(eidos.resolve(size ?? visual?.getSize(), 'md'));
const resolvedVariant = $derived(variant ?? visual?.getVariant() ?? 'surface');
const resolvedColor = $derived(color ?? visual?.getColor() ?? 'primary');
const colorAttrs = $derived(resolveComponentColor(resolvedColor));
const resolvedMatchAnchorWidth = $derived(eidos.resolve(matchAnchorWidth, true));
// Anchor the portaled popup to the Control (the whole field) when present.
const anchorEl = $derived(visual?.getAnchorEl() ?? null);
@ -30,7 +32,8 @@
resolvedMatchAnchorWidth
? '--_combobox-match-anchor-width: var(--floating-anchor-width);'
: undefined,
style
style,
colorAttrs.customStyle
)
);
</script>
@ -44,7 +47,8 @@
data-floating-gap={rest.sideOffset === undefined ? 'panel' : undefined}
data-size={resolvedSize}
data-variant={resolvedVariant}
data-color={resolvedColor}
data-color={colorAttrs.dataColor}
data-color-custom={colorAttrs.isCustom ? '' : undefined}
data-animation-style={motion === 'none' ? undefined : motion}
>
{#snippet children(snippetProps: { open: boolean })}

@ -1,15 +1,19 @@
<script lang="ts">
import { ActiveEidos } from '$uix/eidos';
import { composeInlineStyle } from '$uix/eidos/lib/style';
import { resolveComponentColor } from '$uix/eidos/lib/component-color';
import { getComboboxVisualContext } from './context.svelte';
import type { ComboboxControlProps } from './types';
let { size, variant, color, children, ...rest }: ComboboxControlProps = $props();
let { size, variant, color, style, children, ...rest }: ComboboxControlProps = $props();
const eidos = ActiveEidos.require();
const visual = getComboboxVisualContext();
const resolvedSize = $derived(eidos.resolve(size ?? visual?.getSize(), 'md'));
const resolvedVariant = $derived(variant ?? visual?.getVariant() ?? 'surface');
const resolvedColor = $derived(color ?? visual?.getColor() ?? 'primary');
const colorAttrs = $derived(resolveComponentColor(resolvedColor));
const mergedStyle = $derived(composeInlineStyle(style, colorAttrs.customStyle));
// Register as the floating anchor so the portaled content aligns to the
// whole field (width + gap below the control), not just the input.
@ -26,7 +30,9 @@
data-combobox-control=""
data-size={resolvedSize}
data-variant={resolvedVariant}
data-color={resolvedColor}
data-color={colorAttrs.dataColor}
data-color-custom={colorAttrs.isCustom ? '' : undefined}
style={mergedStyle}
>
{@render children?.()}
</div>

@ -1,21 +1,27 @@
<script lang="ts">
import { ActiveEidos } from '$uix/eidos';
import { composeInlineStyle } from '$uix/eidos/lib/style';
import { resolveComponentColor } from '$uix/eidos/lib/component-color';
import * as Combobox from '$soma/components/combobox';
import { getComboboxVisualContext } from './context.svelte';
import type { ComboboxInputProps } from './types';
let { size, variant, color, ...rest }: ComboboxInputProps = $props();
let { size, variant, color, style, ...rest }: ComboboxInputProps = $props();
const eidos = ActiveEidos.require();
const visual = getComboboxVisualContext();
const resolvedSize = $derived(eidos.resolve(size ?? visual?.getSize(), 'md'));
const resolvedVariant = $derived(variant ?? visual?.getVariant() ?? 'surface');
const resolvedColor = $derived(color ?? visual?.getColor() ?? 'primary');
const colorAttrs = $derived(resolveComponentColor(resolvedColor));
const mergedStyle = $derived(composeInlineStyle(style, colorAttrs.customStyle));
</script>
<Combobox.Input
{...rest}
style={mergedStyle}
data-size={resolvedSize}
data-variant={resolvedVariant}
data-color={resolvedColor}
data-color={colorAttrs.dataColor}
data-color-custom={colorAttrs.isCustom ? '' : undefined}
/>

@ -1,23 +1,29 @@
<script lang="ts">
import { ActiveEidos } from '$uix/eidos';
import * as Combobox from '$soma/components/combobox';
import { composeInlineStyle } from '$uix/eidos/lib/style';
import { resolveComponentColor } from '$uix/eidos/lib/component-color';
import { getComboboxVisualContext } from './context.svelte';
import type { ComboboxTriggerProps } from './types';
let { size, variant, color, children, ...rest }: ComboboxTriggerProps = $props();
let { size, variant, color, style, children, ...rest }: ComboboxTriggerProps = $props();
const eidos = ActiveEidos.require();
const visual = getComboboxVisualContext();
const resolvedSize = $derived(eidos.resolve(size ?? visual?.getSize(), 'md'));
const resolvedVariant = $derived(variant ?? visual?.getVariant() ?? 'surface');
const resolvedColor = $derived(color ?? visual?.getColor() ?? 'primary');
const colorAttrs = $derived(resolveComponentColor(resolvedColor));
const mergedStyle = $derived(composeInlineStyle(style, colorAttrs.customStyle));
</script>
<Combobox.Trigger
{...rest}
style={mergedStyle}
data-size={resolvedSize}
data-variant={resolvedVariant}
data-color={resolvedColor}
data-color={colorAttrs.dataColor}
data-color-custom={colorAttrs.isCustom ? '' : undefined}
>
{@render children?.()}
</Combobox.Trigger>

@ -7,78 +7,6 @@
color: var(--color-content-primary);
}
[data-combobox-control],
[data-combobox-input],
[data-combobox-trigger],
[data-combobox-content] {
--_combobox-accent-track: var(--color-primary-track);
--_combobox-accent-border: var(--color-primary-border);
--_combobox-accent-text: var(--color-primary-text);
}
[data-combobox-control][data-color='secondary'],
[data-combobox-input][data-color='secondary'],
[data-combobox-trigger][data-color='secondary'],
[data-combobox-content][data-color='secondary'] {
--_combobox-accent-track: var(--color-secondary-track);
--_combobox-accent-border: var(--color-secondary-border);
--_combobox-accent-text: var(--color-secondary-text);
}
[data-combobox-control][data-color='neutral'],
[data-combobox-input][data-color='neutral'],
[data-combobox-trigger][data-color='neutral'],
[data-combobox-content][data-color='neutral'] {
--_combobox-accent-track: var(--color-neutral-track);
--_combobox-accent-border: var(--color-neutral-border);
--_combobox-accent-text: var(--color-neutral-text);
}
[data-combobox-control][data-color='affirm'],
[data-combobox-input][data-color='affirm'],
[data-combobox-trigger][data-color='affirm'],
[data-combobox-content][data-color='affirm'] {
--_combobox-accent-track: var(--color-affirm-track);
--_combobox-accent-border: var(--color-affirm-border);
--_combobox-accent-text: var(--color-affirm-text);
}
[data-combobox-control][data-color='fulfill'],
[data-combobox-input][data-color='fulfill'],
[data-combobox-trigger][data-color='fulfill'],
[data-combobox-content][data-color='fulfill'] {
--_combobox-accent-track: var(--color-fulfill-track);
--_combobox-accent-border: var(--color-fulfill-border);
--_combobox-accent-text: var(--color-fulfill-text);
}
[data-combobox-control][data-color='risk'],
[data-combobox-input][data-color='risk'],
[data-combobox-trigger][data-color='risk'],
[data-combobox-content][data-color='risk'] {
--_combobox-accent-track: var(--color-risk-track);
--_combobox-accent-border: var(--color-risk-border);
--_combobox-accent-text: var(--color-risk-text);
}
[data-combobox-control][data-color='threat'],
[data-combobox-input][data-color='threat'],
[data-combobox-trigger][data-color='threat'],
[data-combobox-content][data-color='threat'] {
--_combobox-accent-track: var(--color-threat-track);
--_combobox-accent-border: var(--color-threat-border);
--_combobox-accent-text: var(--color-threat-text);
}
[data-combobox-control][data-color='loss'],
[data-combobox-input][data-color='loss'],
[data-combobox-trigger][data-color='loss'],
[data-combobox-content][data-color='loss'] {
--_combobox-accent-track: var(--color-loss-track);
--_combobox-accent-border: var(--color-loss-border);
--_combobox-accent-text: var(--color-loss-text);
}
[data-combobox-control] {
--_combobox-control-height: var(--control-height-md);
--_combobox-control-padding-inline: var(--space-3);
@ -148,7 +76,7 @@
[data-combobox-control]:has([data-combobox-input]:focus-visible),
[data-combobox-control]:has([data-combobox-trigger]:focus-visible),
[data-combobox-control]:has([data-state='open']) {
border-color: var(--_combobox-accent-border);
border-color: var(--_combobox-palette-border);
/* Focus ring as a flush outline (no offset → no gap) — survives forced-colors. */
outline: var(--focus-ring-width) solid var(--focus-ring-color);
outline-offset: 0;
@ -254,7 +182,7 @@
[data-combobox-trigger][data-state='open'] [data-combobox-indicator] {
transform: rotate(180deg);
color: var(--_combobox-accent-text);
color: var(--_combobox-palette-text);
}
[data-floating-wrapper]:has(> [data-combobox-content]) {
@ -412,8 +340,8 @@
/* Hover + keyboard highlight come from the canonical archetype layer
(archetypes.css). Only the chosen value's accent stays component-local. */
[data-combobox-item][data-state='checked'] {
background: var(--_combobox-accent-track);
color: var(--_combobox-accent-text);
background: var(--_combobox-palette-track);
color: var(--_combobox-palette-text);
}
[data-combobox-item][data-disabled] {

@ -2,6 +2,8 @@
import './combobox.css';
import { ActiveEidos } from '$uix/eidos';
import * as Combobox from '$soma/components/combobox';
import { composeInlineStyle } from '$uix/eidos/lib/style';
import { resolveComponentColor } from '$uix/eidos/lib/component-color';
import { setComboboxVisualContext } from './context.svelte';
import type { ComboboxProps } from './types';
@ -12,12 +14,15 @@
value = $bindable([]),
inputValue = $bindable(''),
open = $bindable(false),
style,
children,
...rest
}: ComboboxProps = $props();
const eidos = ActiveEidos.require();
const resolvedSize = $derived(eidos.resolve(size, 'md'));
const colorAttrs = $derived(resolveComponentColor(color));
const mergedStyle = $derived(composeInlineStyle(style, colorAttrs.customStyle));
// Shared between Control (registers itself) and Content (anchors the
// portaled popup to it) so the popup spans the whole field.
@ -39,7 +44,9 @@
bind:open
data-size={resolvedSize}
data-variant={variant}
data-color={color}
data-color={colorAttrs.dataColor}
data-color-custom={colorAttrs.isCustom ? '' : undefined}
style={mergedStyle}
>
{@render children?.()}
</Combobox.Provider>

@ -19,14 +19,14 @@ import type {
TriggerProps,
ViewportProps
} from '$soma/components/combobox'
import type { ColorRole, ControlVariant, ResponsiveProp, Size } from '$uix/eidos/lib/types'
import type { ComponentColorProp, ControlVariant, ResponsiveProp, Size } from '$uix/eidos/lib/types'
import type { MotionPresetName } from '$uix/eidos/lib/motion/registry'
import type { Snippet } from 'svelte'
import type { HTMLAttributes } from 'svelte/elements'
export type ComboboxSize = Extract<Size, 'xs' | 'sm' | 'md' | 'lg' | 'xl'>
export type ComboboxVariant = ControlVariant
export type ComboboxColor = ColorRole
export type ComboboxColor = ComponentColorProp
export type ComboboxProps = ProviderProps & {
/** Visual density for input, trigger, content and items. @default 'md' */

@ -30,9 +30,6 @@
--_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;
@ -92,36 +89,6 @@
* 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;
@ -159,12 +126,12 @@
}
[data-listbox-item][data-selected]:not([data-disabled]) {
background: var(--_listbox-accent-soft);
background: var(--_listbox-palette-element);
color: var(--color-content-primary);
}
[data-listbox-item][data-selected][data-highlighted]:not([data-disabled]) {
background: var(--_listbox-accent-soft);
background: var(--_listbox-palette-element);
}
[data-listbox-item][data-disabled] {
@ -185,7 +152,7 @@
inline-size: 0.875rem;
block-size: 0.875rem;
flex: 0 0 auto;
color: var(--_listbox-accent);
color: var(--_listbox-palette-solid);
}
[data-listbox-item][data-selected] [data-listbox-item-indicator] {

@ -20,6 +20,8 @@
* menu-indicator partial (✓ glyph identical to standalone Checkbox).
*/
import { ActiveEidos } from '$uix/eidos';
import { composeInlineStyle } from '$uix/eidos/lib/style';
import { resolveComponentColor } from '$uix/eidos/lib/component-color';
import * as Listbox from '$soma/components/listbox';
import { setListSurfaceSizeContext } from '../../lib/list-surface-context';
import '../../lib/list-surface.css';
@ -31,6 +33,7 @@
color = 'primary',
block = false,
value = $bindable([] as string[]),
style,
children: bodyContent,
...rest
}: ListboxProps = $props();
@ -40,6 +43,10 @@
const resolvedVariant = $derived(eidos.resolve(variant, 'surface'));
const resolvedColor = $derived(eidos.resolve(color, 'primary'));
const resolvedBlock = $derived(eidos.resolve(block, false));
// `color` accepts the full system: canonical → `data-color`, raw value →
// `data-color-custom` + the `--color-custom` seed (shared derivation).
const colorAttrs = $derived(resolveComponentColor(resolvedColor));
const mergedStyle = $derived(composeInlineStyle(style, colorAttrs.customStyle));
// Publish the resolved size on the shared list-surface context so any nested
// list panel matches this listbox's scale (parity with the menu surfaces).
@ -52,8 +59,10 @@
data-list-surface=""
data-size={resolvedSize}
data-variant={resolvedVariant}
data-color={resolvedColor}
data-color={colorAttrs.dataColor}
data-color-custom={colorAttrs.isCustom ? '' : undefined}
data-block={resolvedBlock ? '' : undefined}
style={mergedStyle}
>
{#snippet children(snippetProps)}
{@render bodyContent?.(snippetProps)}

@ -1,4 +1,4 @@
import type { ColorRole, ControlVariant, ResponsiveProp, Size } from '$uix/eidos/lib/types';
import type { ComponentColorProp, ControlVariant, ResponsiveProp, Size } from '$uix/eidos/lib/types';
import type {
ProviderProps,
ItemProps,
@ -17,7 +17,7 @@ import type {
export type ListboxSize = Extract<Size, 'xs' | 'sm' | 'md' | 'lg' | 'xl'>;
export type ListboxVariant = ControlVariant;
export type ListboxColor = ColorRole;
export type ListboxColor = ComponentColorProp;
export type ListboxProps = ProviderProps & {
/** Visual size — drives item height + font-size. @default 'md' */

@ -25,13 +25,6 @@
--_timeline-font-size: var(--timeline-font-size-md);
--_timeline-time-font-size: var(--timeline-time-font-size-md);
/* status accent — default primary; data-color overrides below */
--_timeline-accent-solid: var(--color-primary-solid);
--_timeline-accent-track: var(--color-primary-track);
--_timeline-accent-border: var(--color-primary-border);
--_timeline-accent-contrast: var(--color-primary-contrast);
--_timeline-accent-text: var(--color-primary-text);
display: flex;
flex-direction: column;
gap: 0;
@ -72,45 +65,14 @@
--_timeline-marker-icon-size: var(--timeline-marker-icon-size-lg);
}
/* ── Status accent by data-color (root) ─────────────────────────────────── */
[data-timeline][data-color='secondary'] {
--_timeline-accent-solid: var(--color-secondary-solid);
--_timeline-accent-track: var(--color-secondary-track);
--_timeline-accent-border: var(--color-secondary-border);
--_timeline-accent-contrast: var(--color-secondary-contrast);
--_timeline-accent-text: var(--color-secondary-text);
}
[data-timeline][data-color='neutral'] {
--_timeline-accent-solid: var(--color-neutral-solid);
--_timeline-accent-track: var(--color-neutral-track);
--_timeline-accent-border: var(--color-neutral-border);
--_timeline-accent-contrast: var(--color-neutral-contrast);
--_timeline-accent-text: var(--color-neutral-text);
}
[data-timeline][data-color='affirm'] {
--_timeline-accent-solid: var(--color-affirm-solid);
--_timeline-accent-track: var(--color-affirm-track);
--_timeline-accent-border: var(--color-affirm-border);
--_timeline-accent-contrast: var(--color-affirm-contrast);
--_timeline-accent-text: var(--color-affirm-text);
}
[data-timeline][data-color='fulfill'] {
--_timeline-accent-solid: var(--color-fulfill-solid);
--_timeline-accent-track: var(--color-fulfill-track);
--_timeline-accent-border: var(--color-fulfill-border);
--_timeline-accent-contrast: var(--color-fulfill-contrast);
--_timeline-accent-text: var(--color-fulfill-text);
}
/* ── Item: owns the hue (accent by default, intent overrides) ───────────── */
[data-timeline-item] {
--_timeline-hue-solid: var(--_timeline-accent-solid);
--_timeline-hue-track: var(--_timeline-accent-track);
--_timeline-hue-border: var(--_timeline-accent-border);
--_timeline-hue-contrast: var(--_timeline-accent-contrast);
--_timeline-hue-text: var(--_timeline-accent-text);
--_timeline-hue-solid: var(--_timeline-palette-solid);
--_timeline-hue-track: var(--_timeline-palette-track);
--_timeline-hue-border: var(--_timeline-palette-border);
--_timeline-hue-contrast: var(--_timeline-palette-contrast);
--_timeline-hue-text: var(--_timeline-palette-text);
position: relative;
min-inline-size: 0;
}

@ -1,6 +1,8 @@
<script lang="ts">
import './timeline.css';
import { ActiveEidos } from '$uix/eidos';
import { composeInlineStyle } from '$uix/eidos/lib/style';
import { resolveComponentColor } from '$uix/eidos/lib/component-color';
import * as Timeline from '$soma/components/timeline';
import type { TimelineProps } from './types';
@ -10,6 +12,7 @@
color = 'primary',
orientation = 'vertical',
align = 'start',
style,
children: bodyContent,
...rest
}: TimelineProps = $props();
@ -17,15 +20,21 @@
const eidos = ActiveEidos.require();
const resolvedSize = $derived(eidos.resolve(size, 'md'));
const resolvedMarkerSize = $derived(eidos.resolve(markerSize, 'md'));
// `color` accepts the full system: canonical → `data-color`, raw value →
// `data-color-custom` + the `--color-custom` seed (shared derivation).
const colorAttrs = $derived(resolveComponentColor(color));
const mergedStyle = $derived(composeInlineStyle(style, colorAttrs.customStyle));
</script>
<Timeline.Provider
{...rest}
data-size={resolvedSize}
data-marker-size={resolvedMarkerSize}
data-color={color}
data-color={colorAttrs.dataColor}
data-color-custom={colorAttrs.isCustom ? '' : undefined}
data-orientation={orientation}
data-align={align}
style={mergedStyle}
>
{@render bodyContent?.()}
</Timeline.Provider>

@ -1,12 +1,12 @@
import type { Snippet } from 'svelte';
import type { HTMLAttributes, HTMLTimeAttributes } from 'svelte/elements';
import type { ProgressiveColorRole, ResponsiveProp, Size } from '$uix/eidos/lib/types';
import type { ComponentColorProp, ResponsiveProp, Size } from '$uix/eidos/lib/types';
import type { ItemProps, ProviderProps, TimelineStatus } from '$soma/components/timeline';
/** Visual density. Timeline is a composite panel — `sm · md · lg`. */
export type TimelineSize = Extract<Size, 'sm' | 'md' | 'lg'>;
/** Status accent (done / current). Progressive roles, like Stepper. */
export type TimelineColor = ProgressiveColorRole;
/** Status accent (done / current). Full colour system (role · intent · scale · value). */
export type TimelineColor = ComponentColorProp;
export type TimelineOrientation = 'vertical' | 'horizontal';
/** Which side of the rail the content sits on. `alternate` zig-zags by parity. */
export type TimelineAlign = 'start' | 'end' | 'alternate';

@ -848,6 +848,36 @@
--card-risk-border: var(--color-risk-border);
--card-threat-track: var(--color-threat-track);
--card-threat-border: var(--color-threat-border);
--chat-log-padding-inline: var(--space-3);
--chat-log-padding-block: var(--space-2);
--chat-log-separator-gap: var(--space-3);
--chat-log-separator-font-size: var(--size-xs-font-size);
--chat-log-separator-color: var(--color-content-muted);
--chat-log-separator-bg: var(--color-neutral-track);
--chat-log-separator-padding-inline: var(--space-2-5);
--chat-log-separator-padding-block: var(--space-0-5);
--chat-log-separator-radius: var(--radius-full);
--chat-log-divider-color: var(--color-primary-solid);
--chat-log-divider-line: var(--color-primary-border);
--chat-log-divider-font-size: var(--size-xs-font-size);
--chat-log-divider-gap: var(--space-2);
--chat-log-event-font-size: var(--size-sm-font-size);
--chat-log-event-color: var(--color-content-muted);
--chat-log-event-padding-block: var(--space-1);
--chat-log-pill-bg: var(--color-primary-solid);
--chat-log-pill-fg: var(--color-primary-contrast);
--chat-log-pill-font-size: var(--size-sm-font-size);
--chat-log-pill-padding-inline: var(--space-3);
--chat-log-pill-height: var(--size-sm-control-height);
--chat-log-pill-radius: var(--radius-full);
--chat-log-pill-shadow: var(--shadow-3);
--chat-log-pill-offset: var(--space-3);
--chat-log-to-latest-size: var(--size-md-control-height);
--chat-log-to-latest-bg: var(--color-neutral-element);
--chat-log-to-latest-fg: var(--color-content-primary);
--chat-log-to-latest-border: var(--color-neutral-border);
--chat-log-to-latest-shadow: var(--shadow-2);
--chat-log-to-latest-offset: var(--space-3);
--chat-message-gap: var(--space-2);
--chat-message-row-gap-run: var(--space-0-5);
--chat-message-row-gap-group: var(--space-3);
@ -4489,6 +4519,16 @@
--_carousel-palette-solid: var(--palette-solid, var(--color-primary-solid));
}
[data-listbox] {
--_listbox-palette-solid: var(--color-primary-solid);
--_listbox-palette-element: var(--color-primary-element);
}
[data-listbox][data-color], [data-listbox][data-color-custom] {
--_listbox-palette-element: var(--palette-element, var(--color-primary-element));
--_listbox-palette-solid: var(--palette-solid, var(--color-primary-solid));
}
[data-calendar] {
--_calendar-palette-solid: var(--color-primary-solid);
--_calendar-palette-solid-hover: var(--color-primary-solid-hover);
@ -4879,6 +4919,22 @@
--_stepper-palette-text: var(--palette-text, var(--stepper-primary-text));
}
[data-timeline] {
--_timeline-palette-solid: var(--color-primary-solid);
--_timeline-palette-track: var(--color-primary-track);
--_timeline-palette-border: var(--color-primary-border);
--_timeline-palette-contrast: var(--color-primary-contrast);
--_timeline-palette-text: var(--color-primary-text);
}
[data-timeline][data-color], [data-timeline][data-color-custom] {
--_timeline-palette-track: var(--palette-track, var(--color-primary-track));
--_timeline-palette-border: var(--palette-border, var(--color-primary-border));
--_timeline-palette-solid: var(--palette-solid, var(--color-primary-solid));
--_timeline-palette-text: var(--palette-text, var(--color-primary-text));
--_timeline-palette-contrast: var(--palette-contrast, var(--color-primary-contrast));
}
[data-surface] {
--_surface-palette-track: var(--surface-neutral-track);
--_surface-palette-solid: var(--surface-neutral-solid);
@ -5088,6 +5144,18 @@
--toggle-palette-contrast: var(--palette-contrast, var(--toggle-neutral-contrast));
}
[data-combobox-control], [data-combobox-input], [data-combobox-trigger], [data-combobox-content] {
--_combobox-palette-track: var(--color-primary-track);
--_combobox-palette-border: var(--color-primary-border);
--_combobox-palette-text: var(--color-primary-text);
}
[data-combobox-control][data-color], [data-combobox-control][data-color-custom], [data-combobox-input][data-color], [data-combobox-input][data-color-custom], [data-combobox-trigger][data-color], [data-combobox-trigger][data-color-custom], [data-combobox-content][data-color], [data-combobox-content][data-color-custom] {
--_combobox-palette-track: var(--palette-track, var(--color-primary-track));
--_combobox-palette-border: var(--palette-border, var(--color-primary-border));
--_combobox-palette-text: var(--palette-text, var(--color-primary-text));
}
[data-drag-drop-root] {
--_drag-drop-palette-solid: var(--color-primary-solid);
--_drag-drop-palette-element: var(--color-primary-element);

@ -660,6 +660,44 @@ export const THEME_BASE_RECIPE_TOKENS = defineRecipes({
]
}
},
'chat-log': {
// ── Shell ─────────────────────────────────────────────────────────
'padding-inline': 'var(--space-3)',
'padding-block': 'var(--space-2)',
// ── Day separator (sticky pill) ──────────────────────────────────
'separator-gap': 'var(--space-3)',
'separator-font-size': 'var(--size-xs-font-size)',
'separator-color': 'var(--color-content-muted)',
'separator-bg': 'var(--color-neutral-track)',
'separator-padding-inline': 'var(--space-2-5)',
'separator-padding-block': 'var(--space-0-5)',
'separator-radius': 'var(--radius-full)',
// ── Unread divider ───────────────────────────────────────────────
'divider-color': 'var(--color-primary-solid)',
'divider-line': 'var(--color-primary-border)',
'divider-font-size': 'var(--size-xs-font-size)',
'divider-gap': 'var(--space-2)',
// ── System event row ─────────────────────────────────────────────
'event-font-size': 'var(--size-sm-font-size)',
'event-color': 'var(--color-content-muted)',
'event-padding-block': 'var(--space-1)',
// ── Floating pill ("N new messages") ─────────────────────────────
'pill-bg': 'var(--color-primary-solid)',
'pill-fg': 'var(--color-primary-contrast)',
'pill-font-size': 'var(--size-sm-font-size)',
'pill-padding-inline': 'var(--space-3)',
'pill-height': 'var(--size-sm-control-height)',
'pill-radius': 'var(--radius-full)',
'pill-shadow': 'var(--shadow-3)',
'pill-offset': 'var(--space-3)',
// ── To-latest button ─────────────────────────────────────────────
'to-latest-size': 'var(--size-md-control-height)',
'to-latest-bg': 'var(--color-neutral-element)',
'to-latest-fg': 'var(--color-content-primary)',
'to-latest-border': 'var(--color-neutral-border)',
'to-latest-shadow': 'var(--shadow-2)',
'to-latest-offset': 'var(--space-3)'
},
'chat-message': {
// ── Row layout ────────────────────────────────────────────────────
gap: 'var(--space-2)',
@ -1694,6 +1732,10 @@ export const THEME_BASE_RECIPE_TOKENS = defineRecipes({
'_palette-solid': { value: 'var(--color-primary-solid)', scope: 'host' },
'_palette-element': { value: 'var(--color-primary-element)', scope: 'host' }
},
listbox: {
'_palette-solid': { value: 'var(--color-primary-solid)', scope: 'host' },
'_palette-element': { value: 'var(--color-primary-element)', scope: 'host' }
},
calendar: {
gap: 'var(--space-3)',
'padding-xs': 'var(--space-2)',
@ -3189,6 +3231,15 @@ export const THEME_BASE_RECIPE_TOKENS = defineRecipes({
}
},
timeline: {
// Status-accent palette forward (THM-2): host = primary; the shared layer
// routes roles + 33 scales + custom into `--_timeline-palette-*`, which the
// item's `--_timeline-hue-*` reads (data-intent still overrides per item).
// Was a per-role cascade over `--_timeline-accent-*` in timeline.css.
'_palette-solid': { value: 'var(--color-primary-solid)', scope: 'host' },
'_palette-track': { value: 'var(--color-primary-track)', scope: 'host' },
'_palette-border': { value: 'var(--color-primary-border)', scope: 'host' },
'_palette-contrast': { value: 'var(--color-primary-contrast)', scope: 'host' },
'_palette-text': { value: 'var(--color-primary-text)', scope: 'host' },
// ── Rhythm (size-scaled) ──
'gap-sm': 'var(--space-3)',
'gap-md': 'var(--space-4)',
@ -4227,7 +4278,22 @@ export const THEME_BASE_RECIPE_TOKENS = defineRecipes({
// band is deliberately NOT the global `--z-index-*` ladder — see
// STATIC_Z_INDEX_OVERLAY.
combobox: {
'content-z': 'var(--z-index-overlay-floating)'
'content-z': 'var(--z-index-overlay-floating)',
// Accent palette forward (THM-2) — the cascade lived on FOUR parts
// (control / input / trigger / content), so the forward targets all four
// via `parts`. Host = primary; shared layer routes roles + 33 scales + custom.
'_palette-track': {
parts: ['control', 'input', 'trigger', 'content'],
declarations: [{ value: 'var(--color-primary-track)', scope: 'host' }]
},
'_palette-border': {
parts: ['control', 'input', 'trigger', 'content'],
declarations: [{ value: 'var(--color-primary-border)', scope: 'host' }]
},
'_palette-text': {
parts: ['control', 'input', 'trigger', 'content'],
declarations: [{ value: 'var(--color-primary-text)', scope: 'host' }]
}
},
// DragDrop — the drag preview/ghost that tracks the cursor. Floats above

Loading…
Cancel
Save

Powered by TurnKey Linux.