@ -3,10 +3,11 @@
ColorPicker,
type ColorPickerColor,
type ColorPickerSize,
type ColorPickerVariant,
type ChannelInputSnippetProps
type ColorPickerVariant
} from '$uix/eidos/components/color-picker';
import { PickerShell } from '$uix/eidos/components/picker-shell';
import { Tabs } from '$uix/eidos/components/tabs';
import { ColorField } from '$uix/eidos/components/color-field';
import { compileMorfo } from '$uix/morfo';
import { colorPickerMorfo } from '@/uix/morfo/components/color-picker';
import { colorFieldMorfo } from '@/uix/morfo/components/color-field';
@ -64,6 +65,8 @@
let showEyeDropper = $state(true);
let showSwatches = $state(true);
let showLabel = $state(true);
// Untitled-UI layout: the popover body is split into two tabs.
let pickerTab = $state('selection');
let open = $state(false);
let inline = $state(false);
let closeOnSelect = $state(false);
@ -111,7 +114,9 @@
if (set.has(f)) set.delete(f);
else set.add(f);
if (set.size === 0) set.add(f);
allowedFormats = ['hex', 'rgb', 'hsl'].filter((x) => set.has(x as ColorFormat)) as ColorFormat[];
allowedFormats = ['hex', 'rgb', 'hsl'].filter((x) =>
set.has(x as ColorFormat)
) as ColorFormat[];
if (!set.has(format)) format = allowedFormats[0];
}
@ -183,7 +188,9 @@
showHue & & ' < ColorPicker.ChannelSliderTrack / > < ColorPicker.ChannelSliderThumb / > ',
showHue & & ' < / ColorPicker.ChannelSlider > ',
showAlpha & & enableAlpha & & ' < ColorPicker.ChannelSlider channel = "alpha" > ',
showAlpha & & enableAlpha & & ' < ColorPicker.ChannelSliderTrack / > < ColorPicker.ChannelSliderThumb / > ',
showAlpha & &
enableAlpha & &
' < ColorPicker.ChannelSliderTrack / > < ColorPicker.ChannelSliderThumb / > ',
showAlpha & & enableAlpha & & ' < / ColorPicker.ChannelSlider > ',
' < ColorPicker.ValueSwatch / > ',
showChannelInput & & ' < ColorPicker.ChannelInput / > ',
@ -247,7 +254,8 @@
showChannelInput & & ' < ColorPicker.ChannelInput / > ',
showEyeDropper & & ' < ColorPicker.EyeDropper / > ',
showSwatches & & ' < ColorPicker.SwatchGroup > ',
showSwatches && ' { #each presets as c } < ColorPicker.SwatchTrigger color = { c } / > { /each } ',
showSwatches & &
' { #each presets as c } < ColorPicker.SwatchTrigger color = { c } / > { /each } ',
showSwatches & & ' < / ColorPicker.SwatchGroup > ',
' < / PickerShell.Body > ',
showFooter & & (showClear || showCancel || showClose) & & ' < ColorPicker.Footer > ',
@ -269,18 +277,17 @@
< div data-uix-eyebrow > Form · ColorPicker< / div >
< h1 data-uix-page-title > ColorPicker< / h1 >
< p data-uix-page-lede >
Trigger + popover with a 2D saturation/brightness Area, hue + alpha
channel sliders, preset swatches, native EyeDropper API integration
and a segmented channel input composed from ColorField.
< span data-uix-layer-badge = "soma" > soma< / span > owns pointer +
keyboard, gradient math, the HSV ⇄ RGB ⇄ HSL conversions, the
range state machine and the 9 own events
(< code >open< / code > /< code > close-commit< / code > /< code > close-cancel< / code > /< code > close-dismiss< / code > /< code > commit-clear< / code >
Trigger + popover with a 2D saturation/brightness Area, hue + alpha channel sliders, preset
swatches, native EyeDropper API integration and a segmented channel input composed from
ColorField.
< span data-uix-layer-badge = "soma" > soma< / span > owns pointer + keyboard, gradient math, the HSV
⇄ RGB ⇄ HSL conversions, the range state machine and the 9 own events (< code > open< / code > /< code
>close-commit< /code
>/< code > close-cancel< / code > /< code > close-dismiss< / code > /< code > commit-clear< / code >
plus < code > handle-pick< / code > /< code > handle-drag< / code > /< code > commit-set< / code >
fired by the Area + Sliders);
< span data-uix-layer-badge = "eidos" > eidos< / span > adds size /
variant / color recipe, the popover shell, transparency grid
under alpha, swatch chrome and the canonical Footer composition.
< span data-uix-layer-badge = "eidos" > eidos< / span > adds size / variant / color recipe, the popover
shell, transparency grid under alpha, swatch chrome and the canonical Footer composition.
< / p >
< div data-uix-page-meta >
< span data-uix-meta-pill >
@ -290,7 +297,8 @@
< span data-uix-meta-key > events</ span > { events . length }
< / span >
< span data-uix-meta-pill >
< span data-uix-meta-key > inherited</ span > { fieldEvents . length } ColorField · { popoverEvents . length } Popover
< span data-uix-meta-key > inherited</ span > { fieldEvents . length } ColorField · { popoverEvents . length }
Popover
< / span >
< span data-uix-meta-pill >
< span data-uix-meta-key > sizes< / span > 3
@ -333,55 +341,83 @@
< ColorPicker.Portal >
< ColorPicker.Content >
< PickerShell.Body >
< ColorPicker.Area / >
< Tabs
variant="pills"
fitted
size={ size }
bind:value={ pickerTab }
style="--tabs-trigger-font-size: var(--font-size-{ size } )"
>
< Tabs.List >
< Tabs.Trigger value = "selection" > Selección< / Tabs.Trigger >
< Tabs.Trigger value = "presets" > Presets< / Tabs.Trigger >
< Tabs.Indicator / >
< / Tabs.List >
<!-- Channel sliders with explicit row labels so users
know what each gradient drives. -->
{ #if showHue }
< div class = "cp-slider-row" >
< span class = "cp-slider-label" > Hue< / span >
< ColorPicker.ChannelSlider channel = "hue" / >
< / div >
{ /if }
{ #if showAlpha && enableAlpha }
< div class = "cp-slider-row" >
< span class = "cp-slider-label" > Alpha< / span >
< ColorPicker.ChannelSlider channel = "alpha" / >
< / div >
{ /if }
< Tabs.Content value = "selection" >
< div class = "cp-selection" >
< ColorPicker.Area / >
<!-- Compact value row: live preview swatch + segmented channel
input + format select + eyedropper, all on one line. -->
{ #if showChannelInput || showEyeDropper }
< div class = "cp-value-row" >
< ColorPicker.ValueSwatch / >
{ #if showChannelInput }
< ColorPicker.ChannelInput >
{ # snippet children ({ segments } : ChannelInputSnippetProps )}
{ #each segments as { part , value }, i ( `$ { part } - $ { i } `) }
< ColorPicker.ChannelSegment { part } > { value } </ ColorPicker.ChannelSegment >
{ /each }
{ #if showFormatSelect } < ColorPicker.FormatSelect /> { /if }
{ /snippet }
< / ColorPicker.ChannelInput >
{ /if }
{ #if showEyeDropper }
< ColorPicker.EyeDropper aria-label = "Pick color from screen" / >
{ /if }
< / div >
{ /if }
<!-- Tools row: eyedropper (square, left) + the two
channel sliders stacked to its right. No labels. -->
< div class = "cp-tools-row" >
{ #if showEyeDropper }
< ColorPicker.EyeDropper
aria-label="Pick color from screen"
style="font-size: var(--icon-size-{ size } )"
/>
{ /if }
< div class = "cp-sliders" >
{ #if showHue }
< ColorPicker.ChannelSlider channel = "hue" / >
{ /if }
{ #if showAlpha && enableAlpha }
< ColorPicker.ChannelSlider channel = "alpha" / >
{ /if }
< / div >
< / div >
{ #if showChannelInput }
<!-- Value input = the REAL ColorField (its own provider, bound to the
picker's value + format), no swatch. Brings the system-Select format
switcher + separator/glyph styling instead of re-implementing them. -->
< ColorField
bind:value
bind:format
{ allowedFormats }
{ enableAlpha }
{ disabled }
{ readonly }
{ size }
>
< ColorField.Input aria-label = "Color value" swatch >
{ # snippet children ({ segments })}
{ #each segments as { part , value }, i ( `$ { part } - $ { i } `) }
< ColorField.Segment { part } > { value } </ ColorField.Segment >
{ /each }
{ #if showFormatSelect } < ColorField.FormatSelect /> { /if }
{ /snippet }
< / ColorField.Input >
< / ColorField >
{ /if }
< / div >
< / Tabs.Content >
{ #if showSwatches }
< div class = "cp-section-label" > Preset colors< / div >
< ColorPicker.SwatchGroup aria-label = "Preset colors" >
{ #each presets as preset }
< ColorPicker.SwatchTrigger color = { preset } >
< ColorPicker.Swatch color = { preset } / >
< ColorPicker.SwatchIndicator / >
< / ColorPicker.SwatchTrigger >
{ /each }
< / ColorPicker.SwatchGroup >
{ /if }
< Tabs.Content value = "presets" >
{ #if showSwatches }
< ColorPicker.SwatchGroup aria-label = "Preset colors" >
{ #each presets as preset }
< ColorPicker.SwatchTrigger color = { preset } >
< ColorPicker.Swatch color = { preset } / >
< ColorPicker.SwatchIndicator / >
< / ColorPicker.SwatchTrigger >
{ /each }
< / ColorPicker.SwatchGroup >
{ : else }
< p class = "cp-empty" > No presets configured.< / p >
{ /if }
< / Tabs.Content >
< / Tabs >
< / PickerShell.Body >
{ #if showFooter && ( showClear || showCancel || showClose )}
@ -428,7 +464,9 @@
< span data-uix-layer-badge = "sema" > sema< / span >
< span data-uix-tab-count > { eventRows . length } </ span >
< / button >
< button data-uix-tab data-active = { tab === 'recipe' } onclick= {() => ( tab = 'recipe' )} > Recipe</ button >
< button data-uix-tab data-active = { tab === 'recipe' } onclick= {() => ( tab = 'recipe' )}
>Recipe< /button
>
< button data-uix-tab data-active = { tab === 'a11y' } onclick= {() => ( tab = 'a11y' )} > A11y</ button >
< / div >
@ -436,8 +474,8 @@
< section data-uix-section >
< h2 data-uix-section-title > Controls< / h2 >
< p data-uix-section-desc >
< span data-uix-layer-badge = "soma" > soma< / span > owns value coordination, channel
gradients, pointer drag and color math.
< span data-uix-layer-badge = "soma" > soma< / span > owns value coordination, channel gradients,
pointer drag and color math.
< span data-uix-layer-badge = "eidos" > eidos< / span > owns visual treatment.
< / p >
@ -491,14 +529,22 @@
< / span >
< / label >
< label data-uix-control >
< span data-uix-control-label > autoFormat < span data-uix-control-hint > popover FormatSelect drives the external Trigger's ValueText< / span > < / span >
< span data-uix-control-label
>autoFormat < span data-uix-control-hint
>popover FormatSelect drives the external Trigger's ValueText< /span
>< /span
>
< span data-uix-switch >
< input type = "checkbox" bind:checked = { autoFormat } / >
< span data-uix-switch-label > { autoFormat ? 'sync' : 'isolated' } </ span >
< / span >
< / label >
< label data-uix-control >
< span data-uix-control-label > Trigger ValueText < span data-uix-control-hint > composition · render the formatted text inside the Trigger< / span > < / span >
< span data-uix-control-label
>Trigger ValueText < span data-uix-control-hint
>composition · render the formatted text inside the Trigger< /span
>< /span
>
< span data-uix-switch >
< input type = "checkbox" bind:checked = { showValueText } / >
< span data-uix-switch-label > { showValueText ? 'shown' : 'swatch only' } </ span >
@ -569,7 +615,8 @@
< div data-uix-subsection-head >
Composition
< span style = "font-weight: normal; color: var(--uix-text-muted);" >
· toggle parts in / out of the popover · pure composition (the parts themselves don't read these flags — the demo's
· toggle parts in / out of the popover · pure composition (the parts themselves don't read
these flags — the demo's
{ `{#if}` } branches decide whether to include them)
< / span >
< / div >
@ -603,14 +650,20 @@
< / span >
< / label >
< label data-uix-control >
< span data-uix-control-label > FormatSelect < span data-uix-control-hint > inside ChannelInput< / span > < / span >
< span data-uix-control-label
>FormatSelect < span data-uix-control-hint > inside ChannelInput< / span > < /span
>
< span data-uix-switch >
< input type = "checkbox" bind:checked = { showFormatSelect } disabled= { ! showChannelInput } />
< span data-uix-switch-label > { showFormatSelect && showChannelInput ? 'shown' : 'hidden' } </ span >
< span data-uix-switch-label
>{ showFormatSelect && showChannelInput ? 'shown' : 'hidden' } < /span
>
< / span >
< / label >
< label data-uix-control >
< span data-uix-control-label > EyeDropper < span data-uix-control-hint > "el gotero"< / span > < / span >
< span data-uix-control-label
>EyeDropper < span data-uix-control-hint > "el gotero"< / span > < /span
>
< span data-uix-switch >
< input type = "checkbox" bind:checked = { showEyeDropper } / >
< span data-uix-switch-label > { showEyeDropper ? 'shown' : 'hidden' } </ span >
@ -692,17 +745,54 @@
< div data-uix-table-wrap >
< table data-uix-table >
< thead >
< tr > < th > Feature< / th > < th > Air< / th > < th > Ark< / th > < th > Chakra< / th > < th > UIX< / th > < th > Decision< / th > < / tr >
< tr
>< th > Feature< / th > < th > Air< / th > < th > Ark< / th > < th > Chakra< / th > < th > UIX< / th > < th > Decision< /th
>< /tr
>
< / thead >
< tbody >
< tr > < td class = "name" > 2D area< / td > < td > yes< / td > < td > yes< / td > < td > yes< / td > < td > yes< / td > < td > Soma owns drag + gradient calc.< / td > < / tr >
< tr > < td class = "name" > Hue / Alpha sliders< / td > < td > yes< / td > < td > yes< / td > < td > yes< / td > < td > yes< / td > < td > Same ChannelSlider component, channel prop drives gradient.< / td > < / tr >
< tr > < td class = "name" > RGB / individual channel sliders< / td > < td > no< / td > < td > no< / td > < td > no< / td > < td > yes< / td > < td > Soma exposes red/green/blue/saturation/brightness channels too.< / td > < / tr >
< tr > < td class = "name" > Segmented channel input< / td > < td > no< / td > < td > no< / td > < td > no< / td > < td > yes< / td > < td > Composed from ColorField — unique to UIX.< / td > < / tr >
< tr > < td class = "name" > Format switch< / td > < td > yes< / td > < td > yes< / td > < td > yes< / td > < td > yes< / td > < td > FormatSelect locks when allowedFormats has one entry.< / td > < / tr >
< tr > < td class = "name" > Preset swatches< / td > < td > yes< / td > < td > yes< / td > < td > yes< / td > < td > yes< / td > < td > SwatchGroup + SwatchTrigger pattern.< / td > < / tr >
< tr > < td class = "name" > Eye dropper< / td > < td > yes< / td > < td > yes< / td > < td > yes< / td > < td > yes< / td > < td > Native EyeDropper API (Chrome/Edge).< / td > < / tr >
< tr > < td class = "name" > Picker contract (mode/Footer/commit/cancel)< / td > < td > no< / td > < td > no< / td > < td > no< / td > < td > partial< / td > < td > Has closeOnSelect + inline. Modal mode + Footer parts pendiente alinear con date/time picker.< / td > < / tr >
< tr
>< td class = "name" > 2D area< / td > < td > yes< / td > < td > yes< / td > < td > yes< / td > < td > yes< / td > < td
>Soma owns drag + gradient calc.< /td
>< /tr
>
< tr
>< td class = "name" > Hue / Alpha sliders< / td > < td > yes< / td > < td > yes< / td > < td > yes< / td > < td
>yes< /td
>< td > Same ChannelSlider component, channel prop drives gradient.< / td > < /tr
>
< tr
>< td class = "name" > RGB / individual channel sliders< / td > < td > no< / td > < td > no< / td > < td
>no< /td
>< td > yes< / td > < td > Soma exposes red/green/blue/saturation/brightness channels too.< /td
>< /tr
>
< tr
>< td class = "name" > Segmented channel input< / td > < td > no< / td > < td > no< / td > < td > no< / td > < td
>yes< /td
>< td > Composed from ColorField — unique to UIX.< / td > < /tr
>
< tr
>< td class = "name" > Format switch< / td > < td > yes< / td > < td > yes< / td > < td > yes< / td > < td > yes< /td
>< td > FormatSelect locks when allowedFormats has one entry.< / td > < /tr
>
< tr
>< td class = "name" > Preset swatches< / td > < td > yes< / td > < td > yes< / td > < td > yes< / td > < td > yes< /td
>< td > SwatchGroup + SwatchTrigger pattern.< / td > < /tr
>
< tr
>< td class = "name" > Eye dropper< / td > < td > yes< / td > < td > yes< / td > < td > yes< / td > < td > yes< / td > < td
>Native EyeDropper API (Chrome/Edge).< /td
>< /tr
>
< tr
>< td class = "name" > Picker contract (mode/Footer/commit/cancel)< / td > < td > no< / td > < td
>no< /td
>< td > no< / td > < td > partial< / td > < td
>Has closeOnSelect + inline. Modal mode + Footer parts pendiente alinear con
date/time picker.< /td
>< /tr
>
< / tbody >
< / table >
< / div >
@ -710,14 +800,45 @@
< table data-uix-table >
< thead > < tr > < th > Prop< / th > < th > Owner< / th > < th > Default< / th > < th > Notes< / th > < / tr > < / thead >
< tbody >
< tr > < td class = "name" > value / placeholder< / td > < td > soma< / td > < td class = "default" > —< / td > < td > Bindable ColorValue (hex + rgb + hsl + hsv en sync).< / td > < / tr >
< tr > < td class = "name" > format< / td > < td > soma< / td > < td class = "default" > hex< / td > < td > Bindable. Drives ValueText, FormatSelect y ChannelInput segments.< / td > < / tr >
< tr > < td class = "name" > allowedFormats< / td > < td > soma< / td > < td class = "default" > ['hex','rgb','hsl']< / td > < td > Whitelist en FormatSelect.< / td > < / tr >
< tr > < td class = "name" > open / inline< / td > < td > soma< / td > < td class = "default" > false / false< / td > < td > Modo popover vs inline.< / td > < / tr >
< tr > < td class = "name" > closeOnSelect< / td > < td > soma< / td > < td class = "default" > false< / td > < td > Cierra el popover al elegir swatch / eyedropper.< / td > < / tr >
< tr > < td class = "name" > enableAlpha< / td > < td > soma< / td > < td class = "default" > true< / td > < td > Quita el slider alpha cuando false.< / td > < / tr >
< tr > < td class = "name" > areaChannels< / td > < td > soma< / td > < td class = "default" > < code > { x: 'saturation', y: 'brightness' } < / code > < / td > < td > Eje X/Y de la 2D area.< / td > < / tr >
< tr > < td class = "name" > size / variant / color< / td > < td > eidos< / td > < td class = "default" > md / surface / primary< / td > < td > Visual-only data attrs.< / td > < / tr >
< tr
>< td class = "name" > value / placeholder< / td > < td > soma< / td > < td class = "default" > —< / td > < td
>Bindable ColorValue (hex + rgb + hsl + hsv en sync).< /td
>< /tr
>
< tr
>< td class = "name" > format< / td > < td > soma< / td > < td class = "default" > hex< / td > < td
>Bindable. Drives ValueText, FormatSelect y ChannelInput segments.< /td
>< /tr
>
< tr
>< td class = "name" > allowedFormats< / td > < td > soma< / td > < td class = "default"
>['hex','rgb','hsl']< /td
>< td > Whitelist en FormatSelect.< / td > < /tr
>
< tr
>< td class = "name" > open / inline< / td > < td > soma< / td > < td class = "default" > false / false< /td
>< td > Modo popover vs inline.< / td > < /tr
>
< tr
>< td class = "name" > closeOnSelect< / td > < td > soma< / td > < td class = "default" > false< / td > < td
>Cierra el popover al elegir swatch / eyedropper.< /td
>< /tr
>
< tr
>< td class = "name" > enableAlpha< / td > < td > soma< / td > < td class = "default" > true< / td > < td
>Quita el slider alpha cuando false.< /td
>< /tr
>
< tr
>< td class = "name" > areaChannels< / td > < td > soma< / td > < td class = "default"
>< code > { x: 'saturation', y: 'brightness' } < / code > < /td
>< td > Eje X/Y de la 2D area.< / td > < /tr
>
< tr
>< td class = "name" > size / variant / color< / td > < td > eidos< / td > < td class = "default"
>md / surface / primary< /td
>< td > Visual-only data attrs.< / td > < /tr
>
< / tbody >
< / table >
< / div >
@ -731,12 +852,10 @@
· declarative contract
< / h2 >
< p data-uix-section-desc >
The largest picker contract: 20+ parts (Provider, Label,
Control, Trigger, ValueSwatch / ValueText, HiddenInput,
Content, Area + AreaBackground + AreaThumb, ChannelSlider +
ChannelInput + ChannelSegment, FormatSelect, TransparencyGrid,
EyeDropper, SwatchGroup + SwatchTrigger + Swatch +
SwatchIndicator) + 9 own events. Source:
The largest picker contract: 20+ parts (Provider, Label, Control, Trigger, ValueSwatch /
ValueText, HiddenInput, Content, Area + AreaBackground + AreaThumb, ChannelSlider +
ChannelInput + ChannelSegment, FormatSelect, TransparencyGrid, EyeDropper, SwatchGroup +
SwatchTrigger + Swatch + SwatchIndicator) + 9 own events. Source:
< code > src/uix/morfo/components/color-picker.ts< / code > .
< / p >
@ -746,9 +865,24 @@
< tbody >
< tr >< td class = "name" > name</ td >< td class = "type" > "{ colorPickerMorfo . name } "</ td ></ tr >
< tr >< td class = "name" > kebab</ td >< td class = "type" > "{ colorPickerMorfo . kebab } "</ td ></ tr >
< tr >< td class = "name" > scope</ td >< td class = "type" > [{ colorPickerMorfo . scope . map (( s ) => `"$ { s } " `).join(', ') } ]</ td ></ tr >
< tr >< td class = "name" > parts.length</ td >< td class = "default" > { colorPickerMorfo . parts . length } </ td ></ tr >
< tr >< td class = "name" > events.length</ td >< td class = "default" > { colorPickerMorfo . events ? . length ?? 0 } < span style = "color: var(--uix-text-faint)" > (+ { fieldEvents . length } ColorField + { popoverEvents . length } Popover inherited)</ span ></ td ></ tr >
< tr
>< td class = "name" > scope< / td > < td class = "type"
>[{ colorPickerMorfo . scope . map (( s ) => `"$ { s } " `).join(', ') } ]< /td
>< /tr
>
< tr
>< td class = "name" > parts.length< / td > < td class = "default"
>{ colorPickerMorfo . parts . length } < /td
>< /tr
>
< tr
>< td class = "name" > events.length< / td > < td class = "default"
>{ colorPickerMorfo . events ? . length ?? 0 }
< span style = "color: var(--uix-text-faint)"
>(+ { fieldEvents . length } ColorField + { popoverEvents . length } Popover inherited)< /span
>< /td
>< /tr
>
< / tbody >
< / table >
< / div >
@ -756,7 +890,13 @@
< div data-uix-subsection-head > Parts< / div >
< div data-uix-table-wrap >
< table data-uix-table >
< thead > < tr > < th > Part< / th > < th > Marker< / th > < th > Element< / th > < th > Role< / th > < th > Archetype< / th > < th > States< / th > < th > Optional< / th > < / tr > < / thead >
< thead
>< tr
>< th > Part< / th > < th > Marker< / th > < th > Element< / th > < th > Role< / th > < th > Archetype< / th > < th
>States< /th
>< th > Optional< / th > < /tr
>< /thead
>
< tbody >
{ #each partsList as part ( part . kebab )}
< tr >
@ -774,7 +914,16 @@
< / div >
{ #each colorPickerMorfo . parts as rawPart ( rawPart . kebab )}
{ @const partAny = rawPart as unknown as { kebab : string ; aria? : ReadonlyArray < { attr : string ; value : { kind : string }; condition ?: { when : string ; prop? : string ; part? : string }; severity? : string } > ; keyboard? : ReadonlyArray < { key : string ; action : string } > }}
{ @const partAny = rawPart as unknown as {
kebab: string;
aria?: ReadonlyArray< {
attr: string;
value: { kind : string } ;
condition?: { when : string ; prop? : string ; part? : string } ;
severity?: string;
}>;
keyboard?: ReadonlyArray< { key : string ; action : string } >;
}}
{ @const dataAttrs = compiled . contracts . dataAttrsByPart . get ( partAny . kebab ) ?? []}
{ @const ariaAttrs = partAny . aria ?? []}
{ @const keyboard = partAny . keyboard ?? []}
@ -790,7 +939,11 @@
< tr >
< td class = "name" > { attr . attr } </ td >
< td class = "type" > { attr . values ? attr . values . join ( ' | ' ) : '—' } </ td >
< td class = "default" > { 'value' in attr && attr . value ? ( attr as { value : { kind : string } }). value . kind : '—' } </ td >
< td class = "default"
>{ 'value' in attr && attr . value
? (attr as { value : { kind : string } } ).value.kind
: '—'}< /td
>
< / tr >
{ /each }
< / tbody >
@ -801,13 +954,20 @@
{ #if ariaAttrs . length }
< div data-uix-table-wrap style = "margin-top: var(--uix-space-2);" >
< table data-uix-table >
< thead > < tr > < th > aria-attr< / th > < th > Source kind< / th > < th > Condition< / th > < th > Severity< / th > < / tr > < / thead >
< thead
>< tr > < th > aria-attr< / th > < th > Source kind< / th > < th > Condition< / th > < th > Severity< / th > < /tr
>< /thead
>
< tbody >
{ #each ariaAttrs as a ( a . attr )}
< tr >
< td class = "name" > { a . attr } </ td >
< td class = "type" > { a . value . kind } </ td >
< td class = "default" > { a . condition ? `when $ { a . condition . when } $ { a . condition . prop ? ` ($ { a . condition . prop }) ` : '' } $ { a . condition . part ? ` ($ { a . condition . part }) ` : '' } ` : 'always' } </ td >
< td class = "default"
>{ a . condition
? `when ${ a . condition . when } ${ a . condition . prop ? ` ($ { a . condition . prop }) ` : '' } ${ a . condition . part ? ` ($ { a . condition . part }) ` : '' } `
: 'always'}< /td
>
< td class = "default" > { a . severity ?? 'required' } </ td >
< / tr >
{ /each }
@ -841,20 +1001,30 @@
< code > close-dismiss< / code > / < code > close-dismiss-outside< / code >
follow the picker open-state machine with prewrites stamping
< code > data-last-action< / code > causa. < code > commit-clear< / code >
fires on the Clear button. The 2D Area + each ChannelSlider
fire < code > handle-pick< / code > (pointerdown landing on track),
fires on the Clear button. The 2D Area + each ChannelSlider fire < code > handle-pick< / code >
(pointerdown landing on track),
< code > handle-drag< / code > (continuous during drag) and
< code > commit-set< / code > (release / Enter); these are wired
transversally from the slider primitive.
< code > commit-set< / code > (release / Enter); these are wired transversally from the slider primitive.
< / p >
< div data-uix-table-wrap >
< table data-uix-table >
< thead > < tr > < th > name< / th > < th > family< / th > < th > verb< / th > < th > sequence< / th > < th > intent< / th > < th > target< / th > < th > prewrite< / th > < th > commit< / th > < / tr > < / thead >
< thead
>< tr
>< th > name< / th > < th > family< / th > < th > verb< / th > < th > sequence< / th > < th > intent< / th > < th
>target< /th
>< th > prewrite< / th > < th > commit< / th > < /tr
>< /thead
>
< tbody >
{ #each events as action ( action . name )}
{ @const sem = action . semantic }
{ @const intentDecl = 'intent' in sem ? sem.intent : undefined }
{ @const intentStr = typeof intentDecl === 'string' ? intentDecl : intentDecl ? `fromProp:$ {( intentDecl as { fromProp? : string }). fromProp ?? '?' } ` : '—' }
{ @const intentStr =
typeof intentDecl === 'string'
? intentDecl
: intentDecl
? `fromProp:${( intentDecl as { fromProp? : string }). fromProp ?? '?' } `
: '—'}
< tr >
< td class = "name" > { action . name } </ td >
< td class = "type" > { sem . family } </ td >
@ -862,8 +1032,14 @@
< td > { sem . sequence ?? 'pre' } </ td >
< td class = "default" > { intentStr } </ td >
< td > { action . target } </ td >
< td class = "default" > { action . prewrite . length ? action . prewrite . map (( p ) => `$ { p . attr } = $ { p . value } `).join(', ') : '—' } </ td >
< td class = "default" > { action . commit ? `$ { action . commit . attr } = $ { action . commit . value } ` : '—' } </ td >
< td class = "default"
>{ action . prewrite . length
? action.prewrite.map((p) => `${ p . attr } =${ p . value } `).join(', ')
: '—'}< /td
>
< td class = "default"
>{ action . commit ? `$ { action . commit . attr } = $ { action . commit . value } ` : '—' } < /td
>
< / tr >
{ /each }
< / tbody >
@ -882,16 +1058,20 @@
< code > close-commit< / code > , < code > close-cancel< / code > ,
< code > close-dismiss< / code > , < code > close-dismiss-outside< / code > ,
< code > commit-clear< / code > , < code > handle-pick< / code > ,
< code > handle-drag< / code > , < code > commit-set< / code > . The Area
and per-channel Sliders share the slider-family perceptual
signature (handle-pick on press, handle-drag continuous,
commit-set on release). Open / close events follow the picker
open-state machine; close-commit fires intent='fulfill' so
Save lands with the affirmative chime.
< code > handle-drag< / code > , < code > commit-set< / code > . The Area and per-channel Sliders share
the slider-family perceptual signature (handle-pick on press, handle-drag continuous,
commit-set on release). Open / close events follow the picker open-state machine;
close-commit fires intent='fulfill' so Save lands with the affirmative chime.
< / p >
< div data-uix-table-wrap >
< table data-uix-table >
< thead > < tr > < th > Owner< / th > < th > Name< / th > < th > Family< / th > < th > Verb< / th > < th > Sequence< / th > < th > Target< / th > < th > Play< / th > < / tr > < / thead >
< thead
>< tr
>< th > Owner< / th > < th > Name< / th > < th > Family< / th > < th > Verb< / th > < th > Sequence< / th > < th
>Target< /th
>< th > Play< / th > < /tr
>< /thead
>
< tbody >
{ #each eventRows as row }
< tr >
@ -911,9 +1091,7 @@
: row.owner === 'ColorField'
? '[data-color-field]'
: '[data-popover-content]';
const target = stageRef?.querySelector(
selector
) as HTMLElement | null;
const target = stageRef?.querySelector(selector) as HTMLElement | null;
if (!target) return;
void uix.events?.emit({
name: row.action.name,
@ -936,19 +1114,48 @@
< h2 data-uix-section-title > Eidos recipe< / h2 >
< p data-uix-section-desc >
Selectors live in < code > src/uix/eidos/components/color-picker/color-picker.css< / code > .
Reuses < code > color-field< / code > tokens for the segmented input and adds picker-specific
tokens for the area, channel sliders, swatches and eye-dropper.
Reuses < code > color-field< / code > tokens for the segmented input and adds picker-specific tokens
for the area, channel sliders, swatches and eye-dropper.
< / p >
< div data-uix-table-wrap >
< table data-uix-table >
< thead > < tr > < th > Selector< / th > < th > Owner< / th > < th > Purpose< / th > < / tr > < / thead >
< tbody >
< tr > < td class = "name" > < code > [data-color-picker]< / code > < / td > < td > < span data-uix-tag data-kind = "morfo" > morfo< / span > < / td > < td > Root scope + size/variant/color tokens.< / td > < / tr >
< tr > < td class = "name" > < code > [data-color-picker-trigger]< / code > < / td > < td > < span data-uix-tag data-kind = "morfo" > morfo< / span > < / td > < td > Open button con swatch dot (CSS var < code > --cp-trigger-color< / code > ).< / td > < / tr >
< tr > < td class = "name" > < code > [data-color-picker-area]< / code > < / td > < td > < span data-uix-tag data-kind = "morfo" > morfo< / span > < / td > < td > Contenedor 2D; soma calcula < code > backgroundStyle< / code > y posición del thumb.< / td > < / tr >
< tr > < td class = "name" > < code > [data-color-picker-channel-slider][data-channel]< / code > < / td > < td > < span data-uix-tag data-kind = "morfo" > morfo< / span > < / td > < td > Slider 1D por canal; < code > data-channel< / code > permite tintar el gradient en CSS.< / td > < / tr >
< tr > < td class = "name" > < code > [data-color-picker-swatch-trigger][data-checked]< / code > < / td > < td > < span data-uix-tag data-kind = "morfo" > morfo< / span > < / td > < td > Preset swatch; ring acento cuando matchea el valor actual.< / td > < / tr >
< tr > < td class = "name" > < code > [data-color-picker-eye-dropper][data-unsupported]< / code > < / td > < td > < span data-uix-tag data-kind = "morfo" > morfo< / span > < / td > < td > Botón eye-dropper; disabled cuando el browser no implementa la API.< / td > < / tr >
< tr
>< td class = "name" > < code > [data-color-picker]< / code > < / td > < td
>< span data-uix-tag data-kind = "morfo" > morfo< / span > < /td
>< td > Root scope + size/variant/color tokens.< / td > < /tr
>
< tr
>< td class = "name" > < code > [data-color-picker-trigger]< / code > < / td > < td
>< span data-uix-tag data-kind = "morfo" > morfo< / span > < /td
>< td > Open button con swatch dot (CSS var < code > --cp-trigger-color< / code > ).< / td > < /tr
>
< tr
>< td class = "name" > < code > [data-color-picker-area]< / code > < / td > < td
>< span data-uix-tag data-kind = "morfo" > morfo< / span > < /td
>< td
>Contenedor 2D; soma calcula < code > backgroundStyle< / code > y posición del thumb.< /td
>< /tr
>
< tr
>< td class = "name" > < code > [data-color-picker-channel-slider][data-channel]< / code > < /td
>< td > < span data-uix-tag data-kind = "morfo" > morfo< / span > < / td > < td
>Slider 1D por canal; < code > data-channel< / code > permite tintar el gradient en CSS.< /td
>< /tr
>
< tr
>< td class = "name" > < code > [data-color-picker-swatch-trigger][data-checked]< / code > < /td
>< td > < span data-uix-tag data-kind = "morfo" > morfo< / span > < / td > < td
>Preset swatch; ring acento cuando matchea el valor actual.< /td
>< /tr
>
< tr
>< td class = "name" > < code > [data-color-picker-eye-dropper][data-unsupported]< / code > < /td
>< td > < span data-uix-tag data-kind = "morfo" > morfo< / span > < / td > < td
>Botón eye-dropper; disabled cuando el browser no implementa la API.< /td
>< /tr
>
< / tbody >
< / table >
< / div >
@ -959,9 +1166,8 @@
< section data-uix-section >
< h2 data-uix-section-title > Accessibility< / h2 >
< p data-uix-section-desc >
Composes a < strong > Popover< / strong > (the WAI-ARIA dialog)
for the popover + slider per channel + radiogroup for the
preset swatches. The 2D Area exposes its thumb as a
Composes a < strong > Popover< / strong > (the WAI-ARIA dialog) for the popover + slider per
channel + radiogroup for the preset swatches. The 2D Area exposes its thumb as a
< code > role='slider'< / code > reporting both axes via
< code > aria-valuetext< / code > . EyeDropper uses the native
< code > window.EyeDropper< / code > API when available and stamps
@ -974,11 +1180,30 @@
< table data-uix-table >
< thead > < tr > < th > Key< / th > < th > Action< / th > < / tr > < / thead >
< tbody >
< tr > < td > < span data-uix-kbd > Enter< / span > / < span data-uix-kbd > Space< / span > on Trigger< / td > < td > Open the popover; focus first interactive element (typically Area).< / td > < / tr >
< tr > < td > < span data-uix-kbd > ArrowLeft< / span > / < span data-uix-kbd > ArrowRight< / span > on AreaThumb< / td > < td > Step the X-axis channel (saturation by default) by 1.< / td > < / tr >
< tr > < td > < span data-uix-kbd > ArrowUp< / span > / < span data-uix-kbd > ArrowDown< / span > on AreaThumb< / td > < td > Step the Y-axis channel (brightness by default) by 1.< / td > < / tr >
< tr > < td > < span data-uix-kbd > PageUp< / span > / < span data-uix-kbd > PageDown< / span > < / td > < td > Step by 10 on the active axis.< / td > < / tr >
< tr > < td > < span data-uix-kbd > Home< / span > / < span data-uix-kbd > End< / span > < / td > < td > Jump to min / max on the X-axis channel.< / td > < / tr >
< tr
>< td > < span data-uix-kbd > Enter< / span > / < span data-uix-kbd > Space< / span > on Trigger< /td
>< td > Open the popover; focus first interactive element (typically Area).< / td > < /tr
>
< tr
>< td
>< span data-uix-kbd > ArrowLeft< / span > / < span data-uix-kbd > ArrowRight< / span > on AreaThumb< /td
>< td > Step the X-axis channel (saturation by default) by 1.< / td > < /tr
>
< tr
>< td
>< span data-uix-kbd > ArrowUp< / span > / < span data-uix-kbd > ArrowDown< / span > on AreaThumb< /td
>< td > Step the Y-axis channel (brightness by default) by 1.< / td > < /tr
>
< tr
>< td > < span data-uix-kbd > PageUp< / span > / < span data-uix-kbd > PageDown< / span > < / td > < td
>Step by 10 on the active axis.< /td
>< /tr
>
< tr
>< td > < span data-uix-kbd > Home< / span > / < span data-uix-kbd > End< / span > < / td > < td
>Jump to min / max on the X-axis channel.< /td
>< /tr
>
< / tbody >
< / table >
< / div >
@ -988,15 +1213,50 @@
< table data-uix-table >
< thead > < tr > < th > Key< / th > < th > Action< / th > < / tr > < / thead >
< tbody >
< tr > < td > < span data-uix-kbd > Tab< / span > / < span data-uix-kbd > Shift+Tab< / span > < / td > < td > Cycle focus: Area → Hue slider → Alpha slider → ChannelInput segments → FormatSelect → EyeDropper → Swatches → Footer buttons.< / td > < / tr >
< tr > < td > < span data-uix-kbd > ArrowLeft< / span > / < span data-uix-kbd > ArrowRight< / span > on ChannelSlider< / td > < td > Step by 1 (Shift+Arrow = 10).< / td > < / tr >
< tr > < td > < span data-uix-kbd > Home< / span > / < span data-uix-kbd > End< / span > < / td > < td > Jump to min / max for the channel.< / td > < / tr >
< tr > < td > < span data-uix-kbd > ArrowUp< / span > / < span data-uix-kbd > ArrowDown< / span > on ChannelSegment< / td > < td > Increment / decrement the segment value.< / td > < / tr >
< tr > < td > Digit keys on ChannelSegment< / td > < td > Type the value directly; auto-advance.< / td > < / tr >
< tr > < td > < span data-uix-kbd > ArrowKeys< / span > on SwatchGroup< / td > < td > Roving focus across preset swatches.< / td > < / tr >
< tr > < td > < span data-uix-kbd > Enter< / span > / < span data-uix-kbd > Space< / span > on Swatch< / td > < td > Apply the swatch color (fires < code > commit-set< / code > on root).< / td > < / tr >
< tr > < td > < span data-uix-kbd > Enter< / span > on Close (Footer)< / td > < td > Save + close (fires < code > close-commit< / code > ).< / td > < / tr >
< tr > < td > < span data-uix-kbd > Escape< / span > < / td > < td > inline mode: dismiss. modal mode: ignored unless Cancel is wired.< / td > < / tr >
< tr
>< td > < span data-uix-kbd > Tab< / span > / < span data-uix-kbd > Shift+Tab< / span > < / td > < td
>Cycle focus: Area → Hue slider → Alpha slider → ChannelInput segments →
FormatSelect → EyeDropper → Swatches → Footer buttons.< /td
>< /tr
>
< tr
>< td
>< span data-uix-kbd > ArrowLeft< / span > / < span data-uix-kbd > ArrowRight< / span > on ChannelSlider< /td
>< td > Step by 1 (Shift+Arrow = 10).< / td > < /tr
>
< tr
>< td > < span data-uix-kbd > Home< / span > / < span data-uix-kbd > End< / span > < / td > < td
>Jump to min / max for the channel.< /td
>< /tr
>
< tr
>< td
>< span data-uix-kbd > ArrowUp< / span > / < span data-uix-kbd > ArrowDown< / span > on ChannelSegment< /td
>< td > Increment / decrement the segment value.< / td > < /tr
>
< tr
>< td > Digit keys on ChannelSegment< / td > < td > Type the value directly; auto-advance.< /td
>< /tr
>
< tr
>< td > < span data-uix-kbd > ArrowKeys< / span > on SwatchGroup< / td > < td
>Roving focus across preset swatches.< /td
>< /tr
>
< tr
>< td > < span data-uix-kbd > Enter< / span > / < span data-uix-kbd > Space< / span > on Swatch< /td
>< td > Apply the swatch color (fires < code > commit-set< / code > on root).< / td > < /tr
>
< tr
>< td > < span data-uix-kbd > Enter< / span > on Close (Footer)< / td > < td
>Save + close (fires < code > close-commit< / code > ).< /td
>< /tr
>
< tr
>< td > < span data-uix-kbd > Escape< / span > < / td > < td
>inline mode: dismiss. modal mode: ignored unless Cancel is wired.< /td
>< /tr
>
< / tbody >
< / table >
< / div >
@ -1006,19 +1266,67 @@
< table data-uix-table >
< thead > < tr > < th > Part< / th > < th > Attribute< / th > < th > Value< / th > < / tr > < / thead >
< tbody >
< tr >< td class = "name" > trigger</ td >< td > aria-haspopup / aria-expanded / aria-controls</ td >< td class = "type" > "dialog" / { `{open}` } / { `<content id>` } </ td ></ tr >
< tr >< td class = "name" > trigger</ td >< td > aria-label</ td >< td class = "type" > "Pick color" or { `{aria-label}` } </ td ></ tr >
< tr >< td class = "name" > content</ td >< td > role / aria-modal / aria-labelledby</ td >< td class = "type" > "dialog" / "true" when mode='modal' / { `<label id>` } </ td ></ tr >
< tr
>< td class = "name" > trigger< / td > < td > aria-haspopup / aria-expanded / aria-controls< /td
>< td class = "type" > "dialog" / { `{open}` } / { `<content id>` } </ td > < /tr
>
< tr
>< td class = "name" > trigger< / td > < td > aria-label< / td > < td class = "type"
>"Pick color" or { `{aria-label}` } < /td
>< /tr
>
< tr
>< td class = "name" > content< / td > < td > role / aria-modal / aria-labelledby< / td > < td
class="type">"dialog" / "true" when mode='modal' / { `<label id>` } < /td
>< /tr
>
< tr > < td class = "name" > area< / td > < td > role< / td > < td class = "type" > "group"< / td > < / tr >
< tr > < td class = "name" > area-thumb< / td > < td > role / aria-valuenow / aria-valuetext< / td > < td class = "type" > "slider" with 2-axis report< / td > < / tr >
< tr > < td class = "name" > channel-slider[data-channel]< / td > < td > role / aria-valuenow / aria-valuetext< / td > < td class = "type" > "slider" per channel< / td > < / tr >
< tr > < td class = "name" > channel-input (group of segments)< / td > < td > role< / td > < td class = "type" > "group"< / td > < / tr >
< tr > < td class = "name" > channel-segment< / td > < td > role / aria-valuemin/max/now/text< / td > < td class = "type" > "spinbutton"< / td > < / tr >
< tr > < td class = "name" > format-select< / td > < td > role / aria-label< / td > < td class = "type" > "combobox" or native < select> < / td > < / tr >
< tr > < td class = "name" > eye-dropper< / td > < td > type / aria-label / disabled / data-unsupported< / td > < td class = "type" > "button" / "Pick color from screen" / "true" when API absent< / td > < / tr >
< tr > < td class = "name" > swatch-group< / td > < td > role / aria-label< / td > < td class = "type" > "radiogroup" / "Preset colors"< / td > < / tr >
< tr > < td class = "name" > swatch-trigger< / td > < td > role / aria-checked / data-checked< / td > < td class = "type" > "radio" / matches current value< / td > < / tr >
< tr >< td class = "name" > hidden-input</ td >< td > name / required</ td >< td class = "type" > { `{name}` } / mirrors required state · emits formatted color string</ td ></ tr >
< tr
>< td class = "name" > area-thumb< / td > < td > role / aria-valuenow / aria-valuetext< / td > < td
class="type">"slider" with 2-axis report< /td
>< /tr
>
< tr
>< td class = "name" > channel-slider[data-channel]< / td > < td
>role / aria-valuenow / aria-valuetext< /td
>< td class = "type" > "slider" per channel< / td > < /tr
>
< tr
>< td class = "name" > channel-input (group of segments)< / td > < td > role< / td > < td class = "type"
>"group"< /td
>< /tr
>
< tr
>< td class = "name" > channel-segment< / td > < td > role / aria-valuemin/max/now/text< / td > < td
class="type">"spinbutton"< /td
>< /tr
>
< tr
>< td class = "name" > format-select< / td > < td > role / aria-label< / td > < td class = "type"
>"combobox" or native < select> < /td
>< /tr
>
< tr
>< td class = "name" > eye-dropper< / td > < td
>type / aria-label / disabled / data-unsupported< /td
>< td class = "type" > "button" / "Pick color from screen" / "true" when API absent< /td
>< /tr
>
< tr
>< td class = "name" > swatch-group< / td > < td > role / aria-label< / td > < td class = "type"
>"radiogroup" / "Preset colors"< /td
>< /tr
>
< tr
>< td class = "name" > swatch-trigger< / td > < td > role / aria-checked / data-checked< / td > < td
class="type">"radio" / matches current value< /td
>< /tr
>
< tr
>< td class = "name" > hidden-input< / td > < td > name / required< / td > < td class = "type"
>{ `{name}` } / mirrors required state · emits formatted color string< /td
>< /tr
>
< / tbody >
< / table >
< / div >
@ -1027,47 +1335,66 @@
< / div >
< style >
.cp-slider-row {
display: flex;
align-items: center;
gap: var(--uix-space-2, 0.5rem);
/* Picker tabs flush: kill the recipe's content-panel padding so the area /
sliders / field align with the popover edges. The footer buttons stay
OUTSIDE the tabs (rendered after < Tabs > , shared across both panels). */
:global([data-color-picker-content] [data-tabs-content]) {
padding: 0;
}
.cp-slider-label {
flex: 0 0 3.25rem;
font-size: 11px;
font-weight: 500;
color: var(--uix-text-faint) ;
text-transform: uppercas e;
letter-spacing: 0.04em ;
/* Rail = flat segmented band: the active segment is a solid fill, NOT the
default raised pill — drop its border + shadow and use a minimal radius.
All via the tabs' own tokens (no ad-hoc values). */
:global([data-color-picker-content] [data-tabs]) {
--tabs-pills-trigger-border-active: transparent ;
--tabs-pills-trigger-shadow-active: non e;
--tabs-pills-trigger-radius: var(--radius-sm) ;
}
/* Compact value row: swatch + channel input + eyedropper inline. */
.cp-value-row {
.cp-selection {
display: grid;
gap: var(--uix-space-3, 0.75rem);
}
/* Tools row: eyedropper square (left) spanning the two stacked sliders. */
.cp-tools-row {
display: flex;
align-items: center;
align-items: stretch ;
gap: var(--uix-space-2, 0.5rem);
min-inline-size: 0;
}
.cp-value-row :global([data-color-picker-value-swatch ]) {
.cp-tools-row :global([data-color-picker-eye-dropper ]) {
flex: 0 0 auto;
inline-size: 1.75rem;
block-size: 1.75rem;
align-self: stretch;
aspect-ratio: 1;
block-size: auto;
inline-size: auto;
/* Outline style: visible border, transparent fill. The pipette icon is
`size="1em"`, so scale it via font-size to stay proportional to the
(taller) square button rather than the default ~1em. */
border-color: var(--color-border-default);
background: transparent;
}
.cp-value-row :global([data-color-field]) {
.cp-sliders {
flex: 1 1 auto;
min-inline-size: 0;
display: grid;
gap: var(--uix-space-2, 0.5rem);
align-content: center;
}
.cp-value-row :global([data-color-field-input]) {
flex-wrap: nowrap;
/* Keep the gutter (thumb) within the rail: inset each slider by the thumb's
diameter, centered, so the thumb never overruns the left / right edges at
0 % / 100 % (the soma positions it edge-to-edge with `left: { pct } %`). */
.cp-sliders :global([data-color-picker-channel-slider][data-slider]) {
inline-size: calc(100% - var(--_slider-thumb-size, 20px) - 2 * var(--border-width-medium, 2px));
margin-inline: auto;
--_slider-track-size: 12px;
}
.cp-value-row :global([data-color-picker-eye-dropper]) {
flex: 0 0 auto;
/* Value input = the ColorField (no swatch); fill the row. */
.cp-selection :global([data-color-field-input]) {
inline-size: 100%;
flex-wrap: nowrap;
}
.cp-section-label {
font-size: 11px;
font-weight: 500;
.cp-empty {
font-size: 12px;
color: var(--uix-text-faint);
text-transform: uppercase;
letter-spacing: 0.04em;
margin-block-start: var(--uix-space-1, 0.25rem);
text-align: center;
padding-block: var(--uix-space-4, 1rem);
}
< / style >