diff --git a/src/uix/soma/layers/floating/floating.svelte.ts b/src/uix/soma/layers/floating/floating.svelte.ts index 680d4f932..f92c6f5d1 100644 --- a/src/uix/soma/layers/floating/floating.svelte.ts +++ b/src/uix/soma/layers/floating/floating.svelte.ts @@ -310,7 +310,10 @@ export class FloatingContent { () => this.contentRef.current, (contentNode) => { if (!contentNode || !this.opts.enabled.current) return; - const rafId = this.provider.opts.dom.requestFrame(() => { + // Read the resolved content z-index post-layout (coalesced) via dom.measure + // instead of a raw requestFrame — a layout read must never force a sync reflow, + // and measure returns the disposer directly (no manual cancelFrame bookkeeping). + return this.provider.opts.dom.measure(() => { if (this.contentRef.current !== contentNode || !this.opts.enabled.current) return; const cs = this.provider.opts.dom.getWindow(contentNode).getComputedStyle(contentNode); const zIndex = cs.zIndex; @@ -318,10 +321,6 @@ export class FloatingContent { this.contentZIndex = zIndex; } }, contentNode); - - return () => { - this.provider.opts.dom.cancelFrame(rafId, contentNode); - }; } ); diff --git a/web/routes/uix/components/color-picker/+page.svelte b/web/routes/uix/components/color-picker/+page.svelte index 2067ed0d9..98c54b1ee 100644 --- a/web/routes/uix/components/color-picker/+page.svelte +++ b/web/routes/uix/components/color-picker/+page.svelte @@ -16,9 +16,11 @@ import { colorFieldMorfo } from '@/uix/morfo/components/color-field'; import { popoverMorfo } from '@/uix/morfo/components/popover'; import { getActiveUix } from '$active-uix'; + import { ActiveEidos } from '$uix/eidos'; import { colorValueFromRgb, type ColorValue, type ColorFormat } from '$libs/color'; const uix = getActiveUix(); + const eidos = ActiveEidos.require(); type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'a11y'; type TraceEntry = { event: string; family: string; intent?: string; at: number }; @@ -191,42 +193,23 @@ }); // SwatchTrigger commits a parsed ColorValue, so a raw `var(--token)` can't be - // passed — resolve each token to its computed rgb() via a hidden probe. Re-run - // when :root theme attrs change so System/Intent swatches follow retints + // passed. Resolve each token to a concrete sRGB hex via the eidos colour engine + // (config + `$color`) — pure JS, NO DOM probe, so no forced reflow. Re-resolve + // when the :root theme attrs change so System/Intent swatches follow retints // (palette scales are theme-independent but re-resolve cheaply too). let resolvedTokens = $state>({}); $effect(() => { const doc = uix.dom?.getDocument?.() ?? document; - const win = uix.dom?.getWindow?.() ?? window; - if (!doc?.body) return; - const probe = doc.createElement('span'); - probe.style.cssText = 'position:absolute;visibility:hidden;pointer-events:none'; - doc.body.appendChild(probe); - const reprobe = () => { - const next: Record = {}; - for (const token of presetTokens) { - // Scale tokens resolve to an oklch() sibling the color parser can't read. - // color-mix(in srgb) coerces to the sRGB gamut; serialize to rgb() so the - // SwatchTrigger can both display AND commit (parse) the value. - probe.style.color = `color-mix(in srgb, var(${token}), var(${token}))`; - const computed = win.getComputedStyle(probe).color; - const srgb = computed.match(/srgb\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)/); - next[token] = srgb - ? `rgb(${Math.round(+srgb[1] * 255)}, ${Math.round(+srgb[2] * 255)}, ${Math.round(+srgb[3] * 255)})` - : computed; - } - resolvedTokens = next; + const resolve = () => { + resolvedTokens = eidos.resolveTokens(presetTokens); }; - reprobe(); - const themeObs = new MutationObserver(reprobe); + resolve(); + const themeObs = new MutationObserver(resolve); themeObs.observe(doc.documentElement, { attributes: true, - attributeFilter: ['class', 'style', 'data-theme', 'data-uix-theme'] + attributeFilter: ['class', 'style', 'data-theme', 'data-mode', 'data-uix-theme'] }); - return () => { - themeObs.disconnect(); - probe.remove(); - }; + return () => themeObs.disconnect(); }); const valueLabel = $derived(value?.hex ?? 'empty'); @@ -277,8 +260,7 @@ showSwatches && ' ', ' ', showFooter && ' ', - showFooter && - ' ', + showFooter && ' ', showFooter && showClear && ' ', showFooter && showCancel && ' ', showFooter && showClose && ' ', @@ -334,8 +316,7 @@ showSwatches && ' ', ' ', showFooter && ' ', - showFooter && - ' ', + showFooter && ' ', showFooter && showClear && ' ', showFooter && showCancel && ' ', showFooter && showClose && ' ', @@ -421,12 +402,7 @@ - + Selección Presets @@ -522,6 +498,7 @@ {#if showFooter} + {#if showClear}{/if} {#if showCancel}{/if} {#if showClose}{/if}