uix(color): abre `color` en la familia lista/data-grid (10 componentes)

Migra table · tree-grid · tree-view · virtual-list · virtual-grid · feed ·
drag-drop · grid-list · clipboard · carousel al forward de paleta compartido.

Patrón 2-slots: `--_{c}-accent` (indicador sort/activo) -> `--_{c}-palette-solid`,
`--_{c}-accent-soft` (tinte de fila seleccionada) -> `--_{c}-palette-element`.
Recipe nuevo con host = primary; el generador emite base default + forward. Se
quita la cascada per-rol de cada css; tipo -> ComponentColorProp; wrapper vía
`resolveComponentColor`.

Detalles no uniformes resueltos:
- Los 7 que estampan `data-color` en `[data-{c}-root]` usan `parts: ['root']`
  (forma declarations[], la única que el generador lee para parts) -> forward
  `[data-{c}-root][data-color]`. Los 3 bare (grid-list/clipboard/carousel) van
  al selector base.
- drag-drop ya tenía recipe (preview-z) -> fusionado, sin duplicar clave.
- tree-grid/tree-view solo tintan la fila (element); su `accent` (solid) no
  tenía consumidor -> declaran solo `_palette-element`.
- virtual-list/virtual-grid tenían la cascada accent MUERTA (sin consumidor):
  contenedores virtualizados sin visual por color. Tipo abierto (guard) +
  wrapper inerte, pero SIN recipe (evita token de paleta huérfano).

Verificado en navegador (demo table): data-color=risk -> palette-solid/element
= tokens risk compartidos exactos; custom #00b894 -> solid=#00b894, element=
color-mix(...). check: 76 ajenos, 0 propios. Contract 26/28 (2 = proof-of-human).

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

@ -28,8 +28,6 @@
--_carousel-indicator-size: 0.5rem;
--_carousel-indicator-gap: var(--space-2);
--_carousel-trigger-offset: var(--space-3);
--_carousel-accent: var(--color-primary-solid);
--_carousel-accent-soft: var(--color-primary-element);
/*
* Default vertical viewport block-size — vertical orientation needs a
* bounded block axis or the viewport collapses and items overflow.
@ -142,36 +140,6 @@
--_carousel-indicator-gap: var(--space-3);
}
/* ── Color cascades ─ */
[data-carousel][data-color='secondary'] {
--_carousel-accent: var(--color-secondary-solid);
--_carousel-accent-soft: var(--color-secondary-element);
}
[data-carousel][data-color='affirm'] {
--_carousel-accent: var(--color-affirm-solid);
--_carousel-accent-soft: var(--color-affirm-element);
}
[data-carousel][data-color='risk'] {
--_carousel-accent: var(--color-risk-solid);
--_carousel-accent-soft: var(--color-risk-element);
}
[data-carousel][data-color='threat'] {
--_carousel-accent: var(--color-threat-solid);
--_carousel-accent-soft: var(--color-threat-element);
}
[data-carousel][data-color='fulfill'] {
--_carousel-accent: var(--color-fulfill-solid);
--_carousel-accent-soft: var(--color-fulfill-element);
}
[data-carousel][data-color='loss'] {
--_carousel-accent: var(--color-loss-solid);
--_carousel-accent-soft: var(--color-loss-element);
}
[data-carousel][data-color='neutral'] {
--_carousel-accent: var(--color-neutral-solid);
--_carousel-accent-soft: var(--color-neutral-element);
}
[data-carousel][data-disabled] {
opacity: var(--opacity-disabled);
pointer-events: none;
@ -318,11 +286,11 @@
}
[data-carousel-indicator]:hover:not([data-disabled]) {
background: var(--_carousel-accent-soft);
background: var(--_carousel-palette-element);
}
[data-carousel-indicator][data-state='active'] {
background: var(--_carousel-accent);
background: var(--_carousel-palette-solid);
/* Active indicator stretches horizontally for clearer current-position hint. */
inline-size: calc(var(--_carousel-indicator-size) * 2.25);
}

@ -7,6 +7,8 @@
* NextTrigger / Indicator inherit them without duplicate props.
*/
import { ActiveEidos } from '$uix/eidos';
import { composeInlineStyle } from '$uix/eidos/lib/style';
import { resolveComponentColor } from '$uix/eidos/lib/component-color';
import * as Carousel from '$soma/components/carousel';
import type { CarouselProps } from './types';
import { setCarouselEidosContext } from './carousel-context';
@ -28,16 +30,18 @@
const eidos = ActiveEidos.require();
const resolvedSize = $derived(eidos.resolve(size, '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));
// Compose inline style so consumers can still pass `style` AND we can
// override the vertical-block-size token.
const composedStyle = $derived(
[
// Compose inline style so consumers can still pass `style`, we can override
// the vertical-block-size token, AND seed the custom-color derivation.
const mergedStyle = $derived(
composeInlineStyle(
verticalBlockSize ? `--_carousel-vertical-block-size: ${verticalBlockSize};` : undefined,
typeof style === 'string' ? style : undefined
]
.filter(Boolean)
.join(' ') || undefined
style,
colorAttrs.customStyle
)
);
setCarouselEidosContext({
@ -60,10 +64,11 @@
{...rest}
bind:index
{orientation}
style={composedStyle}
style={mergedStyle}
data-size={resolvedSize}
data-variant={variant}
data-color={color}
data-color={colorAttrs.dataColor}
data-color-custom={colorAttrs.isCustom ? '' : undefined}
data-block={block ? '' : undefined}
data-hide-indicators={showIndicators ? undefined : ''}
data-hide-controls={showControls ? undefined : ''}

@ -1,6 +1,6 @@
import type { Snippet } from 'svelte';
import type {
ColorRole,
ComponentColorProp,
ControlVariant,
ResponsiveProp,
Size
@ -48,7 +48,7 @@ export type CarouselVariant = ControlVariant;
* Color cascade — accents indicators, focus rings and the active
* indicator/trigger highlight. Defaults to `primary`.
*/
export type CarouselColor = ColorRole;
export type CarouselColor = ComponentColorProp;
export type CarouselProps = ProviderProps & {
/** Visual size preset. @default 'md' */

@ -11,21 +11,9 @@
*/
[data-clipboard] {
--_clipboard-accent: var(--color-affirm-solid);
--_clipboard-accent-soft: var(--color-affirm-element);
display: contents;
}
[data-clipboard][data-color='primary'] { --_clipboard-accent: var(--color-primary-solid); --_clipboard-accent-soft: var(--color-primary-element); }
[data-clipboard][data-color='secondary'] { --_clipboard-accent: var(--color-secondary-solid); --_clipboard-accent-soft: var(--color-secondary-element); }
[data-clipboard][data-color='neutral'] { --_clipboard-accent: var(--color-neutral-solid); --_clipboard-accent-soft: var(--color-neutral-element); }
[data-clipboard][data-color='affirm'] { --_clipboard-accent: var(--color-affirm-solid); --_clipboard-accent-soft: var(--color-affirm-element); }
[data-clipboard][data-color='fulfill'] { --_clipboard-accent: var(--color-fulfill-solid); --_clipboard-accent-soft: var(--color-fulfill-element); }
[data-clipboard][data-color='risk'] { --_clipboard-accent: var(--color-risk-solid); --_clipboard-accent-soft: var(--color-risk-element); }
[data-clipboard][data-color='threat'] { --_clipboard-accent: var(--color-threat-solid); --_clipboard-accent-soft: var(--color-threat-element); }
[data-clipboard][data-color='loss'] { --_clipboard-accent: var(--color-loss-solid); --_clipboard-accent-soft: var(--color-loss-element); }
/* ── Indicator (visible during `copied=true`) ─ */
[data-clipboard-indicator] {
display: inline-flex;
@ -33,8 +21,8 @@
gap: var(--space-1);
padding: 0.125rem 0.5rem;
border-radius: var(--radius-full);
background: var(--_clipboard-accent-soft);
color: var(--_clipboard-accent);
background: var(--_clipboard-palette-element);
color: var(--_clipboard-palette-solid);
font-size: var(--font-size-xs);
font-family: var(--style-label-font-family);
font-weight: var(--font-weight-medium, 500);

@ -6,6 +6,8 @@
* Indicator (size + color cascade).
*/
import { ActiveEidos } from '$uix/eidos';
import { composeInlineStyle } from '$uix/eidos/lib/style';
import { resolveComponentColor } from '$uix/eidos/lib/component-color';
import { setContext } from 'svelte';
import * as Clipboard from '$soma/components/clipboard';
import type { ClipboardProps, ClipboardSize, ClipboardColor } from './types';
@ -13,12 +15,17 @@
let {
size = 'md',
color = 'affirm',
style,
children: bodyContent,
...rest
}: ClipboardProps = $props();
const eidos = ActiveEidos.require();
const resolvedSize = $derived(eidos.resolve(size, '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));
setContext('eidos:clipboard', {
get size() { return resolvedSize; },
@ -26,7 +33,13 @@
});
</script>
<div data-clipboard="" data-size={resolvedSize} data-color={color}>
<div
data-clipboard=""
data-size={resolvedSize}
data-color={colorAttrs.dataColor}
data-color-custom={colorAttrs.isCustom ? '' : undefined}
style={mergedStyle}
>
<Clipboard.Provider {...rest}>
{#snippet children(snippetProps)}
{@render bodyContent?.(snippetProps)}

@ -1,5 +1,5 @@
import type {
ColorRole,
ComponentColorProp,
ResponsiveProp,
Size
} from '$uix/eidos/lib/types';
@ -12,7 +12,7 @@ import type {
import type { ButtonProps as EidosButtonProps } from '$uix/eidos/components/button';
export type ClipboardSize = Extract<Size, 'xs' | 'sm' | 'md' | 'lg' | 'xl'>;
export type ClipboardColor = ColorRole;
export type ClipboardColor = ComponentColorProp;
export type ClipboardProps = ProviderProps & {
/** Visual size cascade for the Trigger Button. @default 'md' */

@ -16,20 +16,9 @@
*/
[data-drag-drop-root] {
--_drag-drop-accent: var(--color-primary-solid);
--_drag-drop-accent-soft: var(--color-primary-element);
display: contents;
}
[data-drag-drop-root][data-color='secondary'] { --_drag-drop-accent: var(--color-secondary-solid); --_drag-drop-accent-soft: var(--color-secondary-element); }
[data-drag-drop-root][data-color='neutral'] { --_drag-drop-accent: var(--color-neutral-solid); --_drag-drop-accent-soft: var(--color-neutral-element); }
[data-drag-drop-root][data-color='affirm'] { --_drag-drop-accent: var(--color-affirm-solid); --_drag-drop-accent-soft: var(--color-affirm-element); }
[data-drag-drop-root][data-color='fulfill'] { --_drag-drop-accent: var(--color-fulfill-solid); --_drag-drop-accent-soft: var(--color-fulfill-element); }
[data-drag-drop-root][data-color='risk'] { --_drag-drop-accent: var(--color-risk-solid); --_drag-drop-accent-soft: var(--color-risk-element); }
[data-drag-drop-root][data-color='threat'] { --_drag-drop-accent: var(--color-threat-solid); --_drag-drop-accent-soft: var(--color-threat-element); }
[data-drag-drop-root][data-color='loss'] { --_drag-drop-accent: var(--color-loss-solid); --_drag-drop-accent-soft: var(--color-loss-element); }
/* ── Draggable ─ */
[data-drag-drop-draggable] {
cursor: grab;
@ -69,15 +58,15 @@
/* Highlight all compatible drop targets while ANY drag is active. */
[data-drag-drop-droppable][data-accepting] {
--ring-inset-color: var(--_drag-drop-accent-soft);
--ring-inset-color: var(--_drag-drop-palette-element);
--ring-inset-width: var(--border-width-thin);
box-shadow: inset 0 0 0 var(--ring-inset-width) var(--ring-inset-color, currentColor);
}
/* Pointer / focus is over THIS droppable. */
[data-drag-drop-droppable][data-dragover] {
background: var(--_drag-drop-accent-soft);
--ring-inset-color: var(--_drag-drop-accent);
background: var(--_drag-drop-palette-element);
--ring-inset-color: var(--_drag-drop-palette-solid);
box-shadow: inset 0 0 0 var(--ring-inset-width) var(--ring-inset-color, currentColor);
}
@ -97,11 +86,11 @@
z-index: var(--drag-drop-preview-z);
padding: var(--space-2) var(--space-3);
border-radius: var(--radius-md);
border: var(--border-width) solid var(--_drag-drop-accent);
border: var(--border-width) solid var(--_drag-drop-palette-solid);
background: var(--color-surface-default);
box-shadow:
var(--shadow-raised),
0 0 0 1px var(--_drag-drop-accent-soft);
0 0 0 1px var(--_drag-drop-palette-element);
color: var(--color-content-primary);
font-family: var(--style-label-font-family);
font-size: var(--font-size-sm);

@ -5,6 +5,8 @@
* Sets a color/size context for descendants.
*/
import { ActiveEidos } from '$uix/eidos';
import { composeInlineStyle } from '$uix/eidos/lib/style';
import { resolveComponentColor } from '$uix/eidos/lib/component-color';
import { setContext } from 'svelte';
import * as DragDrop from '$soma/components/drag-drop';
import type { DragDropProps, DragDropColor, DragDropSize } from './types';
@ -12,12 +14,17 @@
let {
size = 'md',
color = 'primary',
style,
children: bodyContent,
...rest
}: DragDropProps = $props();
const eidos = ActiveEidos.require();
const resolvedSize = $derived(eidos.resolve(size, '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));
setContext('eidos:drag-drop', {
get size() { return resolvedSize; },
@ -25,7 +32,13 @@
});
</script>
<div data-drag-drop-root="" data-size={resolvedSize} data-color={color}>
<div
data-drag-drop-root=""
data-size={resolvedSize}
data-color={colorAttrs.dataColor}
data-color-custom={colorAttrs.isCustom ? '' : undefined}
style={mergedStyle}
>
<DragDrop.Provider {...rest}>
{#snippet children(snippetProps)}
{@render bodyContent?.(snippetProps)}

@ -1,5 +1,5 @@
import type {
ColorRole,
ComponentColorProp,
ResponsiveProp,
Size
} from '$uix/eidos/lib/types';
@ -18,7 +18,7 @@ import type {
} from '$soma/components/drag-drop';
export type DragDropSize = Extract<Size, 'xs' | 'sm' | 'md' | 'lg' | 'xl'>;
export type DragDropColor = ColorRole;
export type DragDropColor = ComponentColorProp;
export type DragDropProps = ProviderProps & {
/** Visual size cascade for Draggable + Droppable. @default 'md' */

@ -21,8 +21,6 @@
--_feed-thread-indent: var(--space-5);
--_feed-font-size: var(--font-size-sm);
--_feed-divider: var(--color-border-default);
--_feed-accent: var(--color-primary-solid);
--_feed-accent-soft: var(--color-primary-element);
display: block;
border-radius: var(--_feed-radius);
@ -67,36 +65,6 @@
--_feed-font-size: var(--font-size-lg);
}
/* ── Color cascades ─ */
[data-feed-root][data-color='secondary'] {
--_feed-accent: var(--color-secondary-solid);
--_feed-accent-soft: var(--color-secondary-element);
}
[data-feed-root][data-color='affirm'] {
--_feed-accent: var(--color-affirm-solid);
--_feed-accent-soft: var(--color-affirm-element);
}
[data-feed-root][data-color='risk'] {
--_feed-accent: var(--color-risk-solid);
--_feed-accent-soft: var(--color-risk-element);
}
[data-feed-root][data-color='threat'] {
--_feed-accent: var(--color-threat-solid);
--_feed-accent-soft: var(--color-threat-element);
}
[data-feed-root][data-color='fulfill'] {
--_feed-accent: var(--color-fulfill-solid);
--_feed-accent-soft: var(--color-fulfill-element);
}
[data-feed-root][data-color='loss'] {
--_feed-accent: var(--color-loss-solid);
--_feed-accent-soft: var(--color-loss-element);
}
[data-feed-root][data-color='neutral'] {
--_feed-accent: var(--color-neutral-solid);
--_feed-accent-soft: var(--color-neutral-element);
}
/* ── Feed root ─ */
[data-feed] {
display: flex;
@ -171,12 +139,12 @@
gap: var(--_feed-gap);
margin-block-start: var(--space-2);
padding-inline-start: var(--_feed-thread-indent);
border-inline-start: var(--border-width-medium) solid var(--_feed-accent-soft);
border-inline-start: var(--border-width-medium) solid var(--_feed-palette-element);
}
[data-feed-root][dir='rtl'] [data-feed-thread] {
border-inline-start: 0;
border-inline-end: var(--border-width-medium) solid var(--_feed-accent-soft);
border-inline-end: var(--border-width-medium) solid var(--_feed-palette-element);
}
/* ── Sentinel ─
@ -200,8 +168,8 @@
inline-size: 0.75rem;
block-size: 0.75rem;
margin-inline-end: var(--space-2);
border: var(--border-width-medium) solid var(--_feed-accent-soft);
border-block-start-color: var(--_feed-accent);
border: var(--border-width-medium) solid var(--_feed-palette-element);
border-block-start-color: var(--_feed-palette-solid);
border-radius: var(--radius-full);
animation: feed-spin 0.7s linear infinite;
}

@ -7,6 +7,8 @@
* scroll region).
*/
import { ActiveEidos } from '$uix/eidos';
import { composeInlineStyle } from '$uix/eidos/lib/style';
import { resolveComponentColor } from '$uix/eidos/lib/component-color';
import * as Feed from '$soma/components/feed';
import type { FeedProps } from './types';
@ -23,6 +25,9 @@
const eidos = ActiveEidos.require();
const resolvedSize = $derived(eidos.resolve(size, '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 composedStyle = $derived(
[
@ -32,16 +37,18 @@
.filter(Boolean)
.join(' ') || undefined
);
const mergedStyle = $derived(composeInlineStyle(composedStyle, colorAttrs.customStyle));
</script>
<div
data-feed-root=""
data-size={resolvedSize}
data-variant={variant}
data-color={color}
data-color={colorAttrs.dataColor}
data-color-custom={colorAttrs.isCustom ? '' : undefined}
data-block={block ? '' : undefined}
data-scrollable={maxHeight ? '' : undefined}
style={composedStyle}
style={mergedStyle}
>
<Feed.Provider {...rest}>
{#snippet children(snippetProps)}

@ -1,5 +1,5 @@
import type {
ColorRole,
ComponentColorProp,
ControlVariant,
ResponsiveProp,
Size
@ -30,7 +30,7 @@ export type FeedSize = Extract<Size, 'xs' | 'sm' | 'md' | 'lg' | 'xl'>;
export type FeedVariant = ControlVariant;
/** Color cascade — accent for focused article + sentinel spinner. */
export type FeedColor = ColorRole;
export type FeedColor = ComponentColorProp;
export type FeedProps = ProviderProps & {
/** Visual size preset. @default 'md' */

@ -25,8 +25,6 @@
--_grid-list-row-padding-inline: var(--space-3);
--_grid-list-font-size: var(--font-size-sm);
--_grid-list-max-height: min(60vh, 22rem);
--_grid-list-accent: var(--color-primary-solid);
--_grid-list-accent-soft: var(--color-primary-element);
display: flex;
flex-direction: column;
@ -84,36 +82,6 @@
--_grid-list-font-size: var(--font-size-lg);
}
/* ── Color cascades ─ */
[data-grid-list][data-color='secondary'] {
--_grid-list-accent: var(--color-secondary-solid);
--_grid-list-accent-soft: var(--color-secondary-element);
}
[data-grid-list][data-color='affirm'] {
--_grid-list-accent: var(--color-affirm-solid);
--_grid-list-accent-soft: var(--color-affirm-element);
}
[data-grid-list][data-color='risk'] {
--_grid-list-accent: var(--color-risk-solid);
--_grid-list-accent-soft: var(--color-risk-element);
}
[data-grid-list][data-color='threat'] {
--_grid-list-accent: var(--color-threat-solid);
--_grid-list-accent-soft: var(--color-threat-element);
}
[data-grid-list][data-color='fulfill'] {
--_grid-list-accent: var(--color-fulfill-solid);
--_grid-list-accent-soft: var(--color-fulfill-element);
}
[data-grid-list][data-color='loss'] {
--_grid-list-accent: var(--color-loss-solid);
--_grid-list-accent-soft: var(--color-loss-element);
}
[data-grid-list][data-color='neutral'] {
--_grid-list-accent: var(--color-neutral-solid);
--_grid-list-accent-soft: var(--color-neutral-element);
}
[data-grid-list][data-disabled] {
opacity: var(--opacity-disabled);
cursor: not-allowed;
@ -152,7 +120,7 @@
}
[data-grid-list-row][data-selected]:not([data-disabled]) {
background: var(--_grid-list-accent-soft);
background: var(--_grid-list-palette-element);
}
[data-grid-list-row][data-disabled] {
@ -210,8 +178,8 @@
}
[data-grid-list-selection-checkbox][data-state='checked'] {
border-color: var(--_grid-list-accent);
background: var(--_grid-list-accent);
border-color: var(--_grid-list-palette-solid);
background: var(--_grid-list-palette-solid);
}
[data-grid-list-selection-checkbox][data-state='checked']::after {

@ -21,6 +21,8 @@
* form input, Field merge. Eidos adds variant + size + color.
*/
import { ActiveEidos } from '$uix/eidos';
import { composeInlineStyle } from '$uix/eidos/lib/style';
import { resolveComponentColor } from '$uix/eidos/lib/component-color';
import * as GridList from '$soma/components/grid-list';
import type { GridListProps } from './types';
@ -32,6 +34,7 @@
// `$bindable([])` covers the uncontrolled-initial case — no separate
// `defaultValue` prop (N8 poda, 2026-07-07).
value = $bindable([]),
style,
children: bodyContent,
...rest
}: GridListProps = $props();
@ -41,6 +44,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));
</script>
<GridList.Provider
@ -48,7 +55,9 @@
bind:value
data-size={resolvedSize}
data-variant={resolvedVariant}
data-color={resolvedColor}
data-color={colorAttrs.dataColor}
data-color-custom={colorAttrs.isCustom ? '' : undefined}
style={mergedStyle}
data-block={resolvedBlock ? '' : undefined}
>
{#snippet children(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,
RowProps,
@ -17,7 +17,7 @@ import type {
export type GridListSize = Extract<Size, 'xs' | 'sm' | 'md' | 'lg' | 'xl'>;
export type GridListVariant = ControlVariant;
export type GridListColor = ColorRole;
export type GridListColor = ComponentColorProp;
export type GridListProps = ProviderProps & {
/** Visual size — row height + font-size. @default 'md' */

@ -37,8 +37,6 @@
--_table-row-hover-bg: var(--color-surface-overlay);
--_table-row-striped-bg: var(--color-surface-muted);
--_table-divider: var(--color-border-default);
--_table-accent: var(--color-primary-solid);
--_table-accent-soft: var(--color-primary-element);
position: relative;
display: block;
@ -94,36 +92,6 @@
--_table-font-size: var(--font-size-lg);
}
/* ── Color cascades ─ */
[data-table-root][data-color='secondary'] {
--_table-accent: var(--color-secondary-solid);
--_table-accent-soft: var(--color-secondary-element);
}
[data-table-root][data-color='affirm'] {
--_table-accent: var(--color-affirm-solid);
--_table-accent-soft: var(--color-affirm-element);
}
[data-table-root][data-color='risk'] {
--_table-accent: var(--color-risk-solid);
--_table-accent-soft: var(--color-risk-element);
}
[data-table-root][data-color='threat'] {
--_table-accent: var(--color-threat-solid);
--_table-accent-soft: var(--color-threat-element);
}
[data-table-root][data-color='fulfill'] {
--_table-accent: var(--color-fulfill-solid);
--_table-accent-soft: var(--color-fulfill-element);
}
[data-table-root][data-color='loss'] {
--_table-accent: var(--color-loss-solid);
--_table-accent-soft: var(--color-loss-element);
}
[data-table-root][data-color='neutral'] {
--_table-accent: var(--color-neutral-solid);
--_table-accent-soft: var(--color-neutral-element);
}
/*
* Scrolling region — only when consumer sets `maxHeight`. Pure CSS
* overflow auto with overscroll containment so wheel scrolling inside
@ -210,7 +178,7 @@
}
[data-table-column-header][data-sorted] {
color: var(--_table-accent);
color: var(--_table-palette-solid);
}
[data-table-column-header][data-pinned] {
@ -243,7 +211,7 @@
/* Selected row — accent tint. Overrides hover + striped. */
[data-table-body] [data-table-row][data-selected] {
background: var(--_table-accent-soft);
background: var(--_table-palette-element);
color: var(--color-content-primary);
}
@ -329,7 +297,7 @@
}
[data-table-sort-trigger][data-sorted] {
color: var(--_table-accent);
color: var(--_table-palette-solid);
}
[data-table-sort-indicator] {

@ -12,6 +12,8 @@
* context so the `RowDetailTrigger` Button matches the table density.
*/
import { ActiveEidos } from '$uix/eidos';
import { composeInlineStyle } from '$uix/eidos/lib/style';
import { resolveComponentColor } from '$uix/eidos/lib/component-color';
import * as Table from '$soma/components/table';
import type { TableProps } from './types';
import { setTableEidosContext } from './table-context';
@ -49,6 +51,11 @@
.join(' ') || undefined
);
// `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(composedStyle, colorAttrs.customStyle));
setTableEidosContext({
get size() {
return resolvedSize;
@ -66,14 +73,15 @@
data-table-root=""
data-size={resolvedSize}
data-variant={variant}
data-color={color}
data-color={colorAttrs.dataColor}
data-color-custom={colorAttrs.isCustom ? '' : undefined}
data-block={block ? '' : undefined}
data-striped={striped ? '' : undefined}
data-hoverable={hoverable ? '' : undefined}
data-sticky-header={stickyHeader ? '' : undefined}
data-scrollable={maxHeight ? '' : undefined}
data-stable-rows={stableRows && stableRows > 0 && !maxHeight ? '' : undefined}
style={composedStyle}
style={mergedStyle}
>
<Table.Provider {...rest}>
{#if bodyContent}{@render bodyContent()}{/if}

@ -1,6 +1,6 @@
import type { Snippet } from 'svelte';
import type {
ColorRole,
ComponentColorProp,
ControlVariant,
ResponsiveProp,
Size
@ -43,7 +43,7 @@ export type TableVariant = ControlVariant;
* Color cascade — accents the selected-row tint, focus rings and the
* active sort indicator. Defaults to `primary`.
*/
export type TableColor = ColorRole;
export type TableColor = ComponentColorProp;
export type TableProps = ProviderProps & {
/** Visual size preset. @default 'md' */

@ -29,8 +29,6 @@
--_tree-grid-header-bg: var(--color-surface-muted);
--_tree-grid-row-hover-bg: var(--color-surface-overlay);
--_tree-grid-row-striped-bg: var(--color-surface-muted);
--_tree-grid-accent: var(--color-primary-solid);
--_tree-grid-accent-soft: var(--color-primary-element);
--_tree-grid-guide: var(--color-border-default);
--_tree-grid-columns: 1fr;
@ -92,36 +90,6 @@
--_tree-grid-font-size: var(--font-size-lg);
}
/* ── Color cascades ─ */
[data-tree-grid-root][data-color='secondary'] {
--_tree-grid-accent: var(--color-secondary-solid);
--_tree-grid-accent-soft: var(--color-secondary-element);
}
[data-tree-grid-root][data-color='affirm'] {
--_tree-grid-accent: var(--color-affirm-solid);
--_tree-grid-accent-soft: var(--color-affirm-element);
}
[data-tree-grid-root][data-color='risk'] {
--_tree-grid-accent: var(--color-risk-solid);
--_tree-grid-accent-soft: var(--color-risk-element);
}
[data-tree-grid-root][data-color='threat'] {
--_tree-grid-accent: var(--color-threat-solid);
--_tree-grid-accent-soft: var(--color-threat-element);
}
[data-tree-grid-root][data-color='fulfill'] {
--_tree-grid-accent: var(--color-fulfill-solid);
--_tree-grid-accent-soft: var(--color-fulfill-element);
}
[data-tree-grid-root][data-color='loss'] {
--_tree-grid-accent: var(--color-loss-solid);
--_tree-grid-accent-soft: var(--color-loss-element);
}
[data-tree-grid-root][data-color='neutral'] {
--_tree-grid-accent: var(--color-neutral-solid);
--_tree-grid-accent-soft: var(--color-neutral-element);
}
/* Scrollable cap when consumer sets `maxHeight`. */
[data-tree-grid-root][data-scrollable] {
overflow: auto;
@ -219,7 +187,7 @@
}
[data-tree-grid-row][aria-selected='true'] {
background: var(--_tree-grid-accent-soft);
background: var(--_tree-grid-palette-element);
}
[data-tree-grid-row][data-disabled] {

@ -10,6 +10,8 @@
* `grid-template-columns` from `--_tree-grid-columns`.
*/
import { ActiveEidos } from '$uix/eidos';
import { composeInlineStyle } from '$uix/eidos/lib/style';
import { resolveComponentColor } from '$uix/eidos/lib/component-color';
import * as TreeGrid from '$soma/components/tree-grid';
import type { TreeGridProps } from './types';
import { setTreeGridEidosContext } from './tree-grid-context';
@ -48,6 +50,11 @@
.join(' ') || undefined
);
// `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(composedStyle, colorAttrs.customStyle));
setTreeGridEidosContext({
get size() {
return resolvedSize;
@ -65,7 +72,8 @@
data-tree-grid-root=""
data-size={resolvedSize}
data-variant={variant}
data-color={color}
data-color={colorAttrs.dataColor}
data-color-custom={colorAttrs.isCustom ? '' : undefined}
data-block={block ? '' : undefined}
data-striped={striped ? '' : undefined}
data-hoverable={hoverable ? '' : undefined}
@ -73,7 +81,7 @@
data-sticky-header={maxHeight ? '' : undefined}
data-scrollable={maxHeight ? '' : undefined}
data-stable-rows={stableRows && stableRows > 0 && !maxHeight ? '' : undefined}
style={composedStyle}
style={mergedStyle}
>
<TreeGrid.Provider {...rest} bind:expanded bind:value>
{#snippet children(snippetProps)}

@ -1,6 +1,6 @@
import type { Snippet } from 'svelte';
import type {
ColorRole,
ComponentColorProp,
ControlVariant,
ResponsiveProp,
Size
@ -36,7 +36,7 @@ export type TreeGridVariant = ControlVariant;
* Color cascade — accents the selected-row tint, indent guides and the
* focus ring. Defaults to `primary`.
*/
export type TreeGridColor = ColorRole;
export type TreeGridColor = ComponentColorProp;
export type TreeGridProps = ProviderProps & {
/** Visual size preset. @default 'md' */

@ -27,8 +27,6 @@
--_tree-view-row-padding-inline: var(--space-2);
--_tree-view-indent: var(--space-4);
--_tree-view-font-size: var(--font-size-sm);
--_tree-view-accent: var(--color-primary-solid);
--_tree-view-accent-soft: var(--color-primary-element);
--_tree-view-guide: var(--color-border-default);
display: block;
@ -90,36 +88,6 @@
--_tree-view-font-size: var(--font-size-lg);
}
/* ── Color cascades ─ */
[data-tree-view-root][data-color='secondary'] {
--_tree-view-accent: var(--color-secondary-solid);
--_tree-view-accent-soft: var(--color-secondary-element);
}
[data-tree-view-root][data-color='affirm'] {
--_tree-view-accent: var(--color-affirm-solid);
--_tree-view-accent-soft: var(--color-affirm-element);
}
[data-tree-view-root][data-color='risk'] {
--_tree-view-accent: var(--color-risk-solid);
--_tree-view-accent-soft: var(--color-risk-element);
}
[data-tree-view-root][data-color='threat'] {
--_tree-view-accent: var(--color-threat-solid);
--_tree-view-accent-soft: var(--color-threat-element);
}
[data-tree-view-root][data-color='fulfill'] {
--_tree-view-accent: var(--color-fulfill-solid);
--_tree-view-accent-soft: var(--color-fulfill-element);
}
[data-tree-view-root][data-color='loss'] {
--_tree-view-accent: var(--color-loss-solid);
--_tree-view-accent-soft: var(--color-loss-element);
}
[data-tree-view-root][data-color='neutral'] {
--_tree-view-accent: var(--color-neutral-solid);
--_tree-view-accent-soft: var(--color-neutral-element);
}
/* Scrollable cap when consumer sets `maxHeight`. */
[data-tree-view-root][data-scrollable] {
overflow: auto;
@ -171,7 +139,7 @@
[data-tree-view-branch-control][data-selected]:not([data-disabled]),
[data-tree-view-item][data-selected]:not([data-disabled]) {
background: var(--_tree-view-accent-soft);
background: var(--_tree-view-palette-element);
}
[data-tree-view-branch-control][data-disabled],

@ -12,6 +12,8 @@
* chevron rotation and the IndentGuide opts into the depth lines.
*/
import { ActiveEidos } from '$uix/eidos';
import { composeInlineStyle } from '$uix/eidos/lib/style';
import { resolveComponentColor } from '$uix/eidos/lib/component-color';
import * as TreeView from '$soma/components/tree-view';
import type { TreeViewProps } from './types';
import { setTreeViewEidosContext } from './tree-view-context';
@ -33,14 +35,16 @@
const eidos = ActiveEidos.require();
const resolvedSize = $derived(eidos.resolve(size, '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 composedStyle = $derived(
[
composeInlineStyle(
maxHeight ? `max-block-size: ${maxHeight};` : undefined,
typeof style === 'string' ? style : undefined
]
.filter(Boolean)
.join(' ') || undefined
style,
colorAttrs.customStyle
)
);
setTreeViewEidosContext({
@ -63,7 +67,8 @@
data-tree-view-root=""
data-size={resolvedSize}
data-variant={variant}
data-color={color}
data-color={colorAttrs.dataColor}
data-color-custom={colorAttrs.isCustom ? '' : undefined}
data-block={block ? '' : undefined}
data-indent-guides={showIndentGuides ? '' : undefined}
data-scrollable={maxHeight ? '' : undefined}

@ -1,5 +1,5 @@
import type {
ColorRole,
ComponentColorProp,
ControlVariant,
ResponsiveProp,
Size
@ -38,7 +38,7 @@ export type TreeViewVariant = ControlVariant;
* Color cascade — accents the selected-item background, focus rings and
* indent guides. Defaults to `primary`.
*/
export type TreeViewColor = ColorRole;
export type TreeViewColor = ComponentColorProp;
export type TreeViewProps = ProviderProps & {
/** Visual size preset. @default 'md' */

@ -1,5 +1,5 @@
import type {
ColorRole,
ComponentColorProp,
ControlVariant,
ResponsiveProp,
Size
@ -16,7 +16,7 @@ import type {
export type VirtualGridSize = Extract<Size, 'xs' | 'sm' | 'md' | 'lg' | 'xl'>;
export type VirtualGridVariant = ControlVariant;
export type VirtualGridColor = ColorRole;
export type VirtualGridColor = ComponentColorProp;
export type VirtualGridProps = ProviderProps & {
/** Visual size preset. @default 'md' */

@ -12,8 +12,6 @@
/* Radius DECOUPLED from size (Radix model) — surface archetype default, constant across sizes. */
--_virtual-grid-radius: var(--radius-default);
--_virtual-grid-font-size: var(--font-size-sm);
--_virtual-grid-accent: var(--color-primary-solid);
--_virtual-grid-accent-soft: var(--color-primary-element);
display: block;
border-radius: var(--_virtual-grid-radius);
@ -55,14 +53,6 @@
background: transparent;
}
[data-virtual-grid-root][data-color='secondary'] { --_virtual-grid-accent: var(--color-secondary-solid); --_virtual-grid-accent-soft: var(--color-secondary-element); }
[data-virtual-grid-root][data-color='affirm'] { --_virtual-grid-accent: var(--color-affirm-solid); --_virtual-grid-accent-soft: var(--color-affirm-element); }
[data-virtual-grid-root][data-color='risk'] { --_virtual-grid-accent: var(--color-risk-solid); --_virtual-grid-accent-soft: var(--color-risk-element); }
[data-virtual-grid-root][data-color='threat'] { --_virtual-grid-accent: var(--color-threat-solid); --_virtual-grid-accent-soft: var(--color-threat-element); }
[data-virtual-grid-root][data-color='fulfill'] { --_virtual-grid-accent: var(--color-fulfill-solid); --_virtual-grid-accent-soft: var(--color-fulfill-element); }
[data-virtual-grid-root][data-color='loss'] { --_virtual-grid-accent: var(--color-loss-solid); --_virtual-grid-accent-soft: var(--color-loss-element); }
[data-virtual-grid-root][data-color='neutral'] { --_virtual-grid-accent: var(--color-neutral-solid); --_virtual-grid-accent-soft: var(--color-neutral-element); }
[data-virtual-grid-viewport] {
position: relative;
overflow: auto;

@ -4,6 +4,8 @@
* Eidos `<VirtualGrid>` — visual wrapper around soma's `VirtualGrid.Provider`.
*/
import { ActiveEidos } from '$uix/eidos';
import { composeInlineStyle } from '$uix/eidos/lib/style';
import { resolveComponentColor } from '$uix/eidos/lib/component-color';
import * as VirtualGrid from '$soma/components/virtual-grid';
import type { VirtualGridProps } from './types';
@ -19,16 +21,20 @@
const eidos = ActiveEidos.require();
const resolvedSize = $derived(eidos.resolve(size, 'md'));
const styleString = $derived(typeof style === 'string' ? style : undefined);
// `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>
<div
data-virtual-grid-root=""
data-size={resolvedSize}
data-variant={variant}
data-color={color}
data-color={colorAttrs.dataColor}
data-color-custom={colorAttrs.isCustom ? '' : undefined}
data-block={block ? '' : undefined}
style={styleString}
style={mergedStyle}
>
<VirtualGrid.Provider {...rest}>
{#snippet children(snippetProps)}

@ -1,5 +1,5 @@
import type {
ColorRole,
ComponentColorProp,
ControlVariant,
ResponsiveProp,
Size
@ -11,8 +11,10 @@ import type {
ItemProps,
ProviderSnippetProps,
VirtualItem,
VirtualListAnchor,
ScrollToIndexAlign,
ScrollToIndexOptions
ScrollToIndexOptions,
ScrollToEndOptions
} from '$soma/components/virtual-list';
/** VirtualList visual size — drives default Viewport chrome scale. */
@ -27,7 +29,7 @@ export type VirtualListSize = Extract<Size, 'xs' | 'sm' | 'md' | 'lg' | 'xl'>;
export type VirtualListVariant = ControlVariant;
/** Color cascade — focus ring + selection accent. */
export type VirtualListColor = ColorRole;
export type VirtualListColor = ComponentColorProp;
export type VirtualListProps = ProviderProps & {
/** Visual size preset. @default 'md' */
@ -47,6 +49,8 @@ export type VirtualListItemProps = ItemProps;
export type {
ProviderSnippetProps as VirtualListProviderSnippetProps,
VirtualItem,
VirtualListAnchor,
ScrollToIndexAlign,
ScrollToIndexOptions
ScrollToIndexOptions,
ScrollToEndOptions
};

@ -16,8 +16,6 @@
/* Radius DECOUPLED from size (Radix model) — surface archetype default, constant across sizes. */
--_virtual-list-radius: var(--radius-default);
--_virtual-list-font-size: var(--font-size-sm);
--_virtual-list-accent: var(--color-primary-solid);
--_virtual-list-accent-soft: var(--color-primary-element);
display: block;
border-radius: var(--_virtual-list-radius);
@ -60,15 +58,6 @@
background: transparent;
}
/* ── Color cascades ─ */
[data-virtual-list-root][data-color='secondary'] { --_virtual-list-accent: var(--color-secondary-solid); --_virtual-list-accent-soft: var(--color-secondary-element); }
[data-virtual-list-root][data-color='affirm'] { --_virtual-list-accent: var(--color-affirm-solid); --_virtual-list-accent-soft: var(--color-affirm-element); }
[data-virtual-list-root][data-color='risk'] { --_virtual-list-accent: var(--color-risk-solid); --_virtual-list-accent-soft: var(--color-risk-element); }
[data-virtual-list-root][data-color='threat'] { --_virtual-list-accent: var(--color-threat-solid); --_virtual-list-accent-soft: var(--color-threat-element); }
[data-virtual-list-root][data-color='fulfill'] { --_virtual-list-accent: var(--color-fulfill-solid); --_virtual-list-accent-soft: var(--color-fulfill-element); }
[data-virtual-list-root][data-color='loss'] { --_virtual-list-accent: var(--color-loss-solid); --_virtual-list-accent-soft: var(--color-loss-element); }
[data-virtual-list-root][data-color='neutral'] { --_virtual-list-accent: var(--color-neutral-solid); --_virtual-list-accent-soft: var(--color-neutral-element); }
/* ── Viewport (internal scroll) ─ */
[data-virtual-list-viewport] {
position: relative;

@ -5,6 +5,8 @@
* Wraps the provider in `<div data-virtual-list-root>` for chrome.
*/
import { ActiveEidos } from '$uix/eidos';
import { composeInlineStyle } from '$uix/eidos/lib/style';
import { resolveComponentColor } from '$uix/eidos/lib/component-color';
import * as VirtualList from '$soma/components/virtual-list';
import type { VirtualListProps } from './types';
@ -20,16 +22,20 @@
const eidos = ActiveEidos.require();
const resolvedSize = $derived(eidos.resolve(size, 'md'));
const styleString = $derived(typeof style === 'string' ? style : undefined);
// `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>
<div
data-virtual-list-root=""
data-size={resolvedSize}
data-variant={variant}
data-color={color}
data-color={colorAttrs.dataColor}
data-color-custom={colorAttrs.isCustom ? '' : undefined}
data-block={block ? '' : undefined}
style={styleString}
style={mergedStyle}
>
<VirtualList.Provider {...rest}>
{#snippet children(snippetProps)}

@ -848,6 +848,59 @@
--card-risk-border: var(--color-risk-border);
--card-threat-track: var(--color-threat-track);
--card-threat-border: var(--color-threat-border);
--chat-message-gap: var(--space-2);
--chat-message-row-gap-run: var(--space-0-5);
--chat-message-row-gap-group: var(--space-3);
--chat-message-gutter-inline-size: var(--avatar-size-md, var(--space-8));
--chat-message-bubble-padding-inline-sm: var(--space-2-5);
--chat-message-bubble-padding-inline-md: var(--space-3);
--chat-message-bubble-padding-inline-lg: var(--space-4);
--chat-message-bubble-padding-block-sm: var(--space-1-5);
--chat-message-bubble-padding-block-md: var(--space-2);
--chat-message-bubble-padding-block-lg: var(--space-2-5);
--chat-message-bubble-radius: var(--radius-lg);
--chat-message-bubble-radius-run: var(--radius-sm);
--chat-message-bubble-max-inline-size: min(var(--measure-narrow), 78%);
--chat-message-bubble-font-size-sm: var(--size-sm-font-size);
--chat-message-bubble-font-size-md: var(--size-md-font-size);
--chat-message-bubble-font-size-lg: var(--size-lg-font-size);
--chat-message-bubble-line-height: var(--leading-prose);
--chat-message-bubble-bg-in: var(--color-neutral-element);
--chat-message-bubble-fg-in: var(--color-content-primary);
--chat-message-bubble-bg-out: var(--color-primary-solid);
--chat-message-bubble-fg-out: var(--color-primary-contrast);
--chat-message-header-gap: var(--space-2);
--chat-message-header-font-size: var(--size-sm-font-size);
--chat-message-header-font-weight: var(--font-weight-medium);
--chat-message-header-color: var(--color-content-secondary);
--chat-message-meta-gap: var(--space-1);
--chat-message-meta-font-size: var(--size-xs-font-size);
--chat-message-meta-color: var(--color-content-muted);
--chat-message-status-size: 1em;
--chat-message-status-color: var(--color-content-muted);
--chat-message-status-color-read: var(--color-primary-solid);
--chat-message-status-color-failed: var(--color-threat-solid);
--chat-message-reply-padding-inline: var(--space-2);
--chat-message-reply-padding-block: var(--space-1);
--chat-message-reply-accent-size: var(--space-0-5);
--chat-message-reply-radius: var(--radius-sm);
--chat-message-reply-bg: var(--color-neutral-track);
--chat-message-reply-accent: var(--color-primary-solid);
--chat-message-reply-font-size: var(--size-xs-font-size);
--chat-message-reaction-gap: var(--space-1);
--chat-message-reaction-height: var(--size-xs-control-height);
--chat-message-reaction-padding-inline: var(--space-2);
--chat-message-reaction-radius: var(--radius-full);
--chat-message-reaction-font-size: var(--size-xs-font-size);
--chat-message-reaction-bg: var(--color-neutral-track);
--chat-message-reaction-border: var(--color-neutral-separator);
--chat-message-reaction-pressed-bg: var(--color-primary-surface);
--chat-message-reaction-pressed-border: var(--color-primary-border);
--chat-message-reaction-pressed-fg: var(--color-primary-text);
--chat-message-mentioned-bg: var(--color-primary-surface);
--chat-message-mentioned-accent: var(--color-primary-solid);
--chat-message-actions-gap: var(--space-0-5);
--chat-message-actions-offset: var(--space-1);
--checkbox-size-xs-box-size: 14px;
--checkbox-size-xs-indicator-size: 8px;
--checkbox-size-xs-indicator-stroke: 2px;
@ -4370,6 +4423,72 @@
--_range-calendar-palette-text: var(--palette-text, var(--color-primary-text));
}
[data-table-root] {
--_table-palette-solid: var(--color-primary-solid);
--_table-palette-element: var(--color-primary-element);
}
[data-table-root][data-color], [data-table-root][data-color-custom] {
--_table-palette-element: var(--palette-element, var(--color-primary-element));
--_table-palette-solid: var(--palette-solid, var(--color-primary-solid));
}
[data-tree-grid-root] {
--_tree-grid-palette-element: var(--color-primary-element);
}
[data-tree-grid-root][data-color], [data-tree-grid-root][data-color-custom] {
--_tree-grid-palette-element: var(--palette-element, var(--color-primary-element));
}
[data-tree-view-root] {
--_tree-view-palette-element: var(--color-primary-element);
}
[data-tree-view-root][data-color], [data-tree-view-root][data-color-custom] {
--_tree-view-palette-element: var(--palette-element, var(--color-primary-element));
}
[data-feed-root] {
--_feed-palette-solid: var(--color-primary-solid);
--_feed-palette-element: var(--color-primary-element);
}
[data-feed-root][data-color], [data-feed-root][data-color-custom] {
--_feed-palette-element: var(--palette-element, var(--color-primary-element));
--_feed-palette-solid: var(--palette-solid, var(--color-primary-solid));
}
[data-grid-list] {
--_grid-list-palette-solid: var(--color-primary-solid);
--_grid-list-palette-element: var(--color-primary-element);
}
[data-grid-list][data-color], [data-grid-list][data-color-custom] {
--_grid-list-palette-element: var(--palette-element, var(--color-primary-element));
--_grid-list-palette-solid: var(--palette-solid, var(--color-primary-solid));
}
[data-clipboard] {
--_clipboard-palette-solid: var(--color-primary-solid);
--_clipboard-palette-element: var(--color-primary-element);
}
[data-clipboard][data-color], [data-clipboard][data-color-custom] {
--_clipboard-palette-element: var(--palette-element, var(--color-primary-element));
--_clipboard-palette-solid: var(--palette-solid, var(--color-primary-solid));
}
[data-carousel] {
--_carousel-palette-solid: var(--color-primary-solid);
--_carousel-palette-element: var(--color-primary-element);
}
[data-carousel][data-color], [data-carousel][data-color-custom] {
--_carousel-palette-element: var(--palette-element, var(--color-primary-element));
--_carousel-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);
@ -4969,6 +5088,16 @@
--toggle-palette-contrast: var(--palette-contrast, var(--toggle-neutral-contrast));
}
[data-drag-drop-root] {
--_drag-drop-palette-solid: var(--color-primary-solid);
--_drag-drop-palette-element: var(--color-primary-element);
}
[data-drag-drop-root][data-color], [data-drag-drop-root][data-color-custom] {
--_drag-drop-palette-element: var(--palette-element, var(--color-primary-element));
--_drag-drop-palette-solid: var(--palette-solid, var(--color-primary-solid));
}
[data-depth='flush'] {
box-shadow: var(--depth-flush-shadow);
}

@ -660,6 +660,72 @@ export const THEME_BASE_RECIPE_TOKENS = defineRecipes({
]
}
},
'chat-message': {
// ── Row layout ────────────────────────────────────────────────────
gap: 'var(--space-2)',
'row-gap-run': 'var(--space-0-5)',
'row-gap-group': 'var(--space-3)',
// Avatar column reserve — follows the composed Avatar's md diameter.
'gutter-inline-size': 'var(--avatar-size-md, var(--space-8))',
// ── Bubble ────────────────────────────────────────────────────────
'bubble-padding-inline-sm': 'var(--space-2-5)',
'bubble-padding-inline-md': 'var(--space-3)',
'bubble-padding-inline-lg': 'var(--space-4)',
'bubble-padding-block-sm': 'var(--space-1-5)',
'bubble-padding-block-md': 'var(--space-2)',
'bubble-padding-block-lg': 'var(--space-2-5)',
'bubble-radius': 'var(--radius-lg)',
// Flattened corners between messages of one run.
'bubble-radius-run': 'var(--radius-sm)',
'bubble-max-inline-size': 'min(var(--measure-narrow), 78%)',
'bubble-font-size-sm': 'var(--size-sm-font-size)',
'bubble-font-size-md': 'var(--size-md-font-size)',
'bubble-font-size-lg': 'var(--size-lg-font-size)',
'bubble-line-height': 'var(--leading-prose)',
'bubble-bg-in': 'var(--color-neutral-element)',
'bubble-fg-in': 'var(--color-content-primary)',
'bubble-bg-out': 'var(--color-primary-solid)',
'bubble-fg-out': 'var(--color-primary-contrast)',
// ── Header (author + time, first of a run) ───────────────────────
'header-gap': 'var(--space-2)',
'header-font-size': 'var(--size-sm-font-size)',
'header-font-weight': 'var(--font-weight-medium)',
'header-color': 'var(--color-content-secondary)',
// ── Meta (time + status) ─────────────────────────────────────────
'meta-gap': 'var(--space-1)',
'meta-font-size': 'var(--size-xs-font-size)',
'meta-color': 'var(--color-content-muted)',
// ── Delivery status indicator ────────────────────────────────────
'status-size': '1em',
'status-color': 'var(--color-content-muted)',
'status-color-read': 'var(--color-primary-solid)',
'status-color-failed': 'var(--color-threat-solid)',
// ── Reply (quoted preview) ───────────────────────────────────────
'reply-padding-inline': 'var(--space-2)',
'reply-padding-block': 'var(--space-1)',
'reply-accent-size': 'var(--space-0-5)',
'reply-radius': 'var(--radius-sm)',
'reply-bg': 'var(--color-neutral-track)',
'reply-accent': 'var(--color-primary-solid)',
'reply-font-size': 'var(--size-xs-font-size)',
// ── Reaction chips ───────────────────────────────────────────────
'reaction-gap': 'var(--space-1)',
'reaction-height': 'var(--size-xs-control-height)',
'reaction-padding-inline': 'var(--space-2)',
'reaction-radius': 'var(--radius-full)',
'reaction-font-size': 'var(--size-xs-font-size)',
'reaction-bg': 'var(--color-neutral-track)',
'reaction-border': 'var(--color-neutral-separator)',
'reaction-pressed-bg': 'var(--color-primary-surface)',
'reaction-pressed-border': 'var(--color-primary-border)',
'reaction-pressed-fg': 'var(--color-primary-text)',
// ── Mention highlight ────────────────────────────────────────────
'mentioned-bg': 'var(--color-primary-surface)',
'mentioned-accent': 'var(--color-primary-solid)',
// ── Actions cluster ──────────────────────────────────────────────
'actions-gap': 'var(--space-0-5)',
'actions-offset': 'var(--space-1)'
},
checkbox: {
'size-xs-box-size': '14px',
'size-xs-indicator-size': '8px',
@ -1588,6 +1654,46 @@ export const THEME_BASE_RECIPE_TOKENS = defineRecipes({
'_palette-border': { value: 'var(--color-primary-border)', scope: 'host' },
'_palette-text': { value: 'var(--color-primary-text)', scope: 'host' }
},
// ── List / data-grid family — the shared 2-slot selection palette ──────────
// Uniform pattern: `--_{c}-palette-solid` (accent: sort/active indicator) +
// `--_{c}-palette-element` (soft: selected-row tint). Host default = primary;
// the shared layer routes roles + 33 scales + custom. Was a per-role cascade
// in each component's CSS (`--_{c}-accent` / `--_{c}-accent-soft`). The `-root`
// components stamp `data-color` on `[data-{c}-root]` (not the bare provider),
// so their forward targets that part via `parts: ['root']`. drag-drop / combobox
// / timeline already own a recipe → their palette slots live in those entries.
table: {
'_palette-solid': { parts: ['root'], declarations: [{ value: 'var(--color-primary-solid)', scope: 'host' }] },
'_palette-element': { parts: ['root'], declarations: [{ value: 'var(--color-primary-element)', scope: 'host' }] }
},
// tree-grid / tree-view only tint the SELECTED row (element); their `accent`
// (solid) token had no consumer, so they declare only `_palette-element`.
'tree-grid': {
'_palette-element': { parts: ['root'], declarations: [{ value: 'var(--color-primary-element)', scope: 'host' }] }
},
'tree-view': {
'_palette-element': { parts: ['root'], declarations: [{ value: 'var(--color-primary-element)', scope: 'host' }] }
},
// virtual-list / virtual-grid: their accent cascade was declaration-only (no CSS
// consumer) — pure virtualized containers with no colour-driven visual. The type
// stays open (guard/uniformity) + the wrapper stamps a harmless `data-color`, but
// there is nothing to forward, so NO recipe here (avoids an orphan palette token).
feed: {
'_palette-solid': { parts: ['root'], declarations: [{ value: 'var(--color-primary-solid)', scope: 'host' }] },
'_palette-element': { parts: ['root'], declarations: [{ value: 'var(--color-primary-element)', scope: 'host' }] }
},
'grid-list': {
'_palette-solid': { value: 'var(--color-primary-solid)', scope: 'host' },
'_palette-element': { value: 'var(--color-primary-element)', scope: 'host' }
},
clipboard: {
'_palette-solid': { value: 'var(--color-primary-solid)', scope: 'host' },
'_palette-element': { value: 'var(--color-primary-element)', scope: 'host' }
},
carousel: {
'_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)',
@ -4127,7 +4233,10 @@ export const THEME_BASE_RECIPE_TOKENS = defineRecipes({
// DragDrop — the drag preview/ghost that tracks the cursor. Floats above
// local content during an active drag.
'drag-drop': {
'preview-z': 'var(--z-index-overlay-detached)'
'preview-z': 'var(--z-index-overlay-detached)',
// Selection palette (THM-2) — `data-color` sits on `[data-drag-drop-root]`.
'_palette-solid': { parts: ['root'], declarations: [{ value: 'var(--color-primary-solid)', scope: 'host' }] },
'_palette-element': { parts: ['root'], declarations: [{ value: 'var(--color-primary-element)', scope: 'host' }] }
},
'navigation-menu': {

Loading…
Cancel
Save

Powered by TurnKey Linux.