You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/src/uix/eidos/components/slider/slider.css

213 lines
6.8 KiB

[data-slider] {
--_slider-track-size: var(--slider-track-size-md);
--_slider-thumb-size: var(--slider-thumb-size-md);
--_slider-hit-size: var(--slider-hit-size-md);
--_slider-tick-size: var(--slider-tick-size-md);
cursor: pointer;
}
eidos: theming fixes + size/variant parity batch + docs - theme: add surface.muted + content.muted to color contract (neutral-3 + neutral-10). Plugs 17 broken --color-content-muted and 3 broken --color-surface-muted references in recipes/components. - form.css: fix --color-neutral-element-hover typo → --color-neutral-hover. - archetypes.css + events.css: replace raw hsl/rgba indigo with color-mix(var(--color-primary-solid) …) — no raw colors left in eidos. - combobox dark scrollbar: unscope ::-webkit-scrollbar rules in uix.css and duplicate --uix-line on :root + :root[data-mode='dark'] so portaled overlays (Combobox listbox, Popover, Dialog, Drawer) inherit the theme. - sizes: 20 components expand from sm/md/lg to xs..xl (form controls, text inputs, progress/meter, field/form) or xs..lg (nav controls: breadcrumb, pagination, tag-group, toolbar). Composite panels keep sm/md/lg deliberately. - variants: field + toolbar drop arbitrary ControlVariant narrowings; both expose all 3 (surface | outline | ghost) with new outline CSS. - pagination demo: disambiguate siblingCount/boundaryCount as "per side" in label + API table (Radix/MUI convention). - docs: CHECKLIST §C-2.6 (contract token presence) + §D-7.4 (chip parity) + §D-7.5 (size category) added. DEMO_AUTHORING §12.7 (chip parity) + §12.8 (size category cheatsheet) added. eidos/README + active_architecture.md sync texts/migration nomenclature. - PENDIENTES.md: normas N-1..N-5 implantadas en esta sesión. Verification: 88/88 eidos tests, 0 type errors, 67/67 component:audit PASS. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
[data-slider][data-size='xs'] {
--_slider-track-size: var(--slider-track-size-xs);
--_slider-thumb-size: var(--slider-thumb-size-xs);
--_slider-hit-size: var(--slider-hit-size-xs);
--_slider-tick-size: var(--slider-tick-size-xs);
}
[data-slider][data-size='sm'] {
--_slider-track-size: var(--slider-track-size-sm);
--_slider-thumb-size: var(--slider-thumb-size-sm);
--_slider-hit-size: var(--slider-hit-size-sm);
--_slider-tick-size: var(--slider-tick-size-sm);
}
[data-slider][data-size='lg'] {
--_slider-track-size: var(--slider-track-size-lg);
--_slider-thumb-size: var(--slider-thumb-size-lg);
--_slider-hit-size: var(--slider-hit-size-lg);
--_slider-tick-size: var(--slider-tick-size-lg);
}
eidos: theming fixes + size/variant parity batch + docs - theme: add surface.muted + content.muted to color contract (neutral-3 + neutral-10). Plugs 17 broken --color-content-muted and 3 broken --color-surface-muted references in recipes/components. - form.css: fix --color-neutral-element-hover typo → --color-neutral-hover. - archetypes.css + events.css: replace raw hsl/rgba indigo with color-mix(var(--color-primary-solid) …) — no raw colors left in eidos. - combobox dark scrollbar: unscope ::-webkit-scrollbar rules in uix.css and duplicate --uix-line on :root + :root[data-mode='dark'] so portaled overlays (Combobox listbox, Popover, Dialog, Drawer) inherit the theme. - sizes: 20 components expand from sm/md/lg to xs..xl (form controls, text inputs, progress/meter, field/form) or xs..lg (nav controls: breadcrumb, pagination, tag-group, toolbar). Composite panels keep sm/md/lg deliberately. - variants: field + toolbar drop arbitrary ControlVariant narrowings; both expose all 3 (surface | outline | ghost) with new outline CSS. - pagination demo: disambiguate siblingCount/boundaryCount as "per side" in label + API table (Radix/MUI convention). - docs: CHECKLIST §C-2.6 (contract token presence) + §D-7.4 (chip parity) + §D-7.5 (size category) added. DEMO_AUTHORING §12.7 (chip parity) + §12.8 (size category cheatsheet) added. eidos/README + active_architecture.md sync texts/migration nomenclature. - PENDIENTES.md: normas N-1..N-5 implantadas en esta sesión. Verification: 88/88 eidos tests, 0 type errors, 67/67 component:audit PASS. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
[data-slider][data-size='xl'] {
--_slider-track-size: var(--slider-track-size-xl);
--_slider-thumb-size: var(--slider-thumb-size-xl);
--_slider-hit-size: var(--slider-hit-size-xl);
--_slider-tick-size: var(--slider-tick-size-xl);
}
[data-slider][data-disabled] {
cursor: not-allowed;
opacity: var(--slider-disabled-opacity);
}
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress) Re-audit of the whole component catalog at pilot depth (91 fichas + the checkpoint verdicts in docs/audit/components/) and the executed fix packages. - P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 + API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d, component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts (role=application removed ×4, aria-selected off the Day, drp translationRef, field data-state prune, pin-input commit-set, media-player renames), 13 new sema packs + 12 morfos family-default → pack. - P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar); onValueCommit terminal-callback norm (pin-input/search/password/textarea + date/time/color-field add); typed validation reason + onInvalid (tags-input, css-field); index/onIndexChange (carousel); deselectable; openDelay/ groupSkipDelay; allowCustomValue; defaultValue prune. - P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel); touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual, 44 AAA). S6 (Field composition) + S8 (calendar-surface) pending. Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline); morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched components; per-component vitest suites green. Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md Excluded (broken by the N1 rename, left broken per user decision, not staged): words/**, palabras/**, chronos, web/routes/alpha/**. Reconciliation pending: the touch-rows ::before for checkbox/switch reverses changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed markers to a labeled-row/Field task — flagged for the user in the handoff. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
/* Readonly — value visible, thumb stays focusable, NOT dimmed (distinct from
disabled). Just drops the grab/edit cursor; the value lock lives in soma. */
[data-slider][data-readonly] {
cursor: default;
}
[data-slider-thumb][data-readonly],
[data-slider-thumb][data-readonly]:active {
cursor: default;
scale: 1;
}
fix(slider,media-player): el anillo del commit no enmarca contenedores, y saltar al final no es completar Cuatro defectos que solo aparecieron cuando el usuario MIRO la pagina. 1) EL BORDE DE LOS SLIDERS ("se viene arrastrando desde el principio"). No era del arquetipo ni del slider: es la firma GLOBAL de la familia commit. BUILTIN_SIGNATURES.commit monta commit-settle, cuyo 30% pinta box-shadow 0 0 0 3px. El commit-set del Slider apunta al PROVIDER, asi que cada paso de flecha y cada release dibujaban un aro alrededor de la caja entera (medido en vivo: 544x40). Un aro lee como feedback en un boton y como borde parasito en un rail ancho y transparente. Exencion acotada en slider.css — el patron que gradient-builder.css ya practicaba. Alcanza a todo slider embebido (waveform, media-player, color-picker, time-picker) porque todos llevan [data-slider]. `animation` es la palanca, no `box-shadow`: las declaraciones de keyframe ganan a las normales, asi que solo reiniciar el NOMBRE de la animacion detiene la pintura. 2) EL MISMO ANILLO EN EL PLAYER: commit-complete / commit-fail apuntan al provider -> borde alrededor de los ~700px del reproductor. Misma exencion en media-player.css. Los botones conservan su aro pequeno (20x36), que es donde el feedback pertenece. 3) SALTAR AL FINAL ANUNCIABA UNA COMPLECION FALSA. Los botones de salto no tienen evento propio: seek() delega a proposito (D-AP2.13, "el release del scrubber ya ES el commit-set del Slider") — cierto para el scrubber y VACIO para los botones, que no tienen Slider detras. Al saltar mas alla del final el elemento dispara `ended` -> commit-complete en el provider -> sonido (es audible por D-AP2.7) + anillo, mientras el boton de retroceso quedaba mudo. Saltar al final no es completar: es abandonar la obra en el final. seek()/scrubTo() marcan el salto que aterriza en el final y `ended` no anuncia; un `play` posterior limpia la marca, asi que el final natural se sigue anunciando. Los dos botones quedan simetricos y mudos, que es la doctrina del transporte. 4) MEDIASESSION SIN OVERLAY DEL SO: el modo audio YA funcionaba (metadata completa, medida en vivo); el roto era VIDEO, el modo por defecto de la demo. <AudioPlayer> deriva metadata de title/artist/artwork; el <MediaPlayer> compositivo no puede leer su hijo Title sin volverse data-driven, asi que la declara el consumidor — y la demo no lo hacia. El arreglo es de la demo; el art se comporta como D-SR.6/D-AP2.5 lo firmo. Guard nuevo (no anunciar compleccion al saltar al final; si al final natural) VISTO EN ROJO antes de darlo por bueno. Nota de doctrina, tras leer el corpus a peticion del usuario: la exencion acotada en la receta ES la puerta, no un olor. motion.md §4 fija que la firma es generica por decision estructural ("un commit asienta igual en todo el sistema"), eidos es el UNICO dueno del canal visual, los packs de sema solo transportan sound/haptic, y el VisualChannel no consulta activeChannels — no hay palanca declarativa para callar solo lo visual. Queda anotado, menor: commit-settle escribe 3px a pelo donde sus hermanos announce-pulse-* usan var(--focus-ring-width). Verificado: check 75 = baseline, 0 propios; 11/11 media-player; motion.test.ts verde (la firma global intacta); sonda en Chrome vivo — slider real -> animation none, elemento no-slider con el mismo sello -> sigue commit-settle, aro de foco intacto. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
/* The global `commit` firma pulses a 3px primary box-shadow RING on whatever the
commit event targets. Our `commit-set` targets the PROVIDER, so every arrow
step and every drag release drew a border around the WHOLE control — a ring
reads as feedback on a small box, as a stray border on a wide thin rail. Kill
the VISUAL ring here (the sound/haptic firma is unaffected — it runs in JS,
not CSS); the value change is itself the visible confirmation. Same opt-out as
gradient-builder.css, and it reaches every embedded slider (media-player,
waveform, color-picker, time-picker…) because they all carry [data-slider].
`animation` is the lever, not `box-shadow`: keyframed declarations outrank
normal ones, so only resetting the animation NAME stops the paint. */
[data-slider][data-event-family='commit'][data-event-phase='active'] {
animation: none;
}
[data-slider][data-orientation='horizontal'] {
uix(slider): temable — 97 % → 100 %, y con un solo valor el tick ACTIVO no existe CERO claves acuñadas. El componente llegaba con `global` a cero, `private` a cero —sus cuatro privados son la escala por talla, que ya deriva de públicos— y treinta y nueve claves cubriendo su cromo entero; lo único fuera de alcance era UN literal. Este commit lo firma, adjudica sus siete silenciosas y escribe los artefactos que le faltaban. QUÉ ENTRA - **El literal se firma como identidad**: `inline-size: 100%` en `[data-slider][data-orientation='horizontal']` — un rail ocupa su contenedor. El knob de verdad ya existe y es su SUELO: `min-inline-size`. Mismo precedente que `separator.css:26`, cerrado el mismo día también sin acuñar. - **Siete adjudicaciones en el ledger**, todas MEDIDAS sobre los nodos reales, con `transition` congelada salvo donde el token ES la transición: - `min-block-size` — sólo la lee `[data-orientation='vertical']` y el escenario arranca horizontal. Con el chip de orientación: 160px -> 1234px. - `active-thumb-scale` / `active-thumb-shadow` — el pickup vive en `[data-slider-thumb]:active`, que pide el puntero MANTENIDO, y el pase estático del guard lo aparca. Con `mouse.down()` real sobre el thumb real: scale 1.15 -> 4.56, y la sombra sigue al centinela. - `active-tick-bg` — un tick lleva `data-active` sólo mientras su valor cae DENTRO de [min(valores), max(valores)] (soma, `SliderTickProvider.isActive`), así que un pulgar único en 40 con ticks en 0/50/100 deja los tres inactivos: la marca activa no es un estado del tick, es una RELACIÓN CON EL RANGO. Con el chip `multiple` (20–80) el tick 50 se activa y el token alcanza. - `disabled-opacity` — el escenario arranca habilitado; con su interruptor, 0.4 -> 0.123. - `transition-duration` / `transition-ease` — SON la transición que el guard congela; medidos sin congelar, 0.12s -> 11.5s y steps(4). - README con su sección «Talla y tema» (39 claves por grupo, con la distinción `hit-size-{k}` = agarre vs `track-size-{k}` = pintura) y veredicto §5 de la ficha con las cifras. QUÉ NO SE TOCA - **Nada se retira ni se renombra**, y no es prudencia: este contrato lo COMPONEN otros SIETE componentes que re-tiñen o re-dimensionan estas mismas claves sobre su Slider embebido — `media-player`, `skin-media-player`, `waveform` (su playhead ES este thumb), `color-picker`, `gradient-builder`, `image-adjustments` y `time-range-picker`. `min-block-size`, que el centinela leía muerta, es justo la que `media-player` escribe para su volumen vertical. - `base.ts` y `generated/base.css`: sin cambio de contrato no hay nada que regenerar. 39 -> 39 claves. ARTEFACTOS - Censo: 97 % (33 knobs · 31 públicos · 1 literal · 1 sistema) -> **100 %** (32 · 31 · 0 literal · 1 excepción · 1 sistema). Global 68 %, no baja. - Sonda antes/después: **0 diffs sobre 1.376 valores computados**, 7 estados, 7 nodos (raíz · secondary-range · range · thumb · 3 ticks). - Captura 2× antes y después: idénticas. - Centinela R-5.4: **32/39**, las 7 restantes adjudicadas -> verde. - component-audit PASS · eidos-lint 25 morfo-backed / 5 eidos-only / **0 invalid** · `vitest run src/uix/eidos` 35/36 ficheros (el rojo conocido es `skin-media-player`) · rtl:check 0 · docs:check 0. - `npm run check`: UN error en `web/routes/uix/components/slider/+page.svelte` (`Property 'keyboard' does not exist`, línea del `sliderMorfo.parts[2]`), **PREEXISTENTE** desde `1857c7854` y ajeno a este commit; ningún otro error en ficheros de slider. - Prettier: los hunks de deriva del fichero son los MISMOS que en HEAD (2 en el README, 0 en el CSS); mi bloque no añade ninguno. NOTA DE CONCURRENCIA: la pestaña `Tokens` de su demo (paso 11 del protocolo) la escribí yo, pero entró en HEAD por `9fc38b19a` + `c88b2f861` de otra sesión, que la arrastró con un `git add` derivado y lo declaró. No se re-commitea aquí. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
inline-size: 100%; /* literal: a rail spans its container — identity; its floor is `min-inline-size` */
block-size: var(--_slider-hit-size);
min-inline-size: var(--slider-min-inline-size);
}
[data-slider][data-orientation='vertical'] {
inline-size: var(--_slider-hit-size);
block-size: var(--slider-min-block-size);
min-block-size: var(--slider-min-block-size);
}
[data-slider]::before {
content: '';
position: absolute;
border-radius: var(--slider-track-radius);
background: var(--slider-track-bg);
}
[data-slider][data-orientation='horizontal']::before {
inset-inline: 0;
inset-block-start: 50%;
block-size: var(--_slider-track-size);
transform: translateY(-50%);
}
fix(direction): getDir() no era reactivo, y 33 componentes estampaban su valor congelado El slider no respondia al cambio rtl/ltr. Resultaron ser dos defectos independientes, y el segundo era del ecosistema entero. 1) EL SLIDER. slider.css emparejaba un inset LOGICO con un transform FISICO en las tres reglas verticales (inset-inline-start: 50% + translateX(-50%)). Los transforms no se voltean, asi que en RTL el rail y el relleno quedaban fuera de eje un ancho entero. Medido en Chrome: raiz/pulgar/ticks en x=961, rail y relleno en x=955. El Tick se libraba porque ya usaba margin-inline-start — ese era el idioma correcto del propio fichero y es el que aplico. Mismo defecto en accordion.css: text-align: left en el trigger, que era el UNICO text-align fisico de todo eidos; ahora quedan cero. 2) LA RAIZ. makeDimension().get() leia engine.snapshot() esquivando la celda $state, asi que prefs.<dim>.get() — y con ella el soma.prefs.getDir() que la guia manda usar — era una lectura sin tracking. Los ~64 componentes que resuelven su direccion desde prefs la congelaban al montarse. La proyeccion DOM se salvaba porque usa onChange: misma dimension, dos caminos de lectura, solo uno reactivo. Arreglado con un contador de cambios POR dimension, bombeado desde el effectiveDiff que el motor ya calcula, de modo que cubre tambien el cambio por DERIVACION (direction siguiendo a language) y no solo el intent. Un contador por clave y no una celda global: con una sola celda, tocar motion despertaria las 64 derivaciones de direccion. El test negativo lo clava. 3) VALOR NO ES AFIRMACION. 33 de 37 componentes estampaban dir en su raiz con un valor siempre concreto, asi que una app que ponga html dir=rtl sin registrar la preferencia se encontraba 33 islas volteadas del reves. Investigadas las cinco librerias de referencia: MUI y react-aria no estampan nunca, Radix estampa siempre (sus mantenedores arrastran discussions/1405 por esto), y Zag estampa prop("dir") — presente si alguien lo pidio, ausente si no. Adoptado el modelo de Zag pero uniforme: el atributo NUNCA tiene defecto. La herencia la hace el navegador por AUSENCIA de atributo, sin ninguna lectura del DOM, asi que la cadena prop -> prefs -> 'ltr' se queda intacta. 4) HOMOGENEIDAD. Habia DIEZ formas distintas de resolver lo mismo en los wrappers y CINCO de declarar el tipo, con un cuarto escalon semantico —heredar del menu padre— enterrado en dos expresiones sueltas. Normalizado a activeDir(getter, soma) en los 36, un solo tipo Direction | undefined, y el escalon de los submenus declarado en la llamada, donde se ve. 5) LA SHELL. El toggle de direccion pasa de 2 a 3 estados: auto hace clearIntent, que es lo unico que deja ganar al derive. Y entra el arabe en el catalogo de idiomas, porque sin un idioma RTL la derivacion no era disparable. Con las dos cosas, elegir arabe voltea la direccion sin tocar el control — la cadena documentada funcionando de punta a punta por primera vez. Verificado: check 74 = linea base exacta, cero nuevos. 602/602 tests en 101 ficheros. En Chrome real: el topbar mueve al slider y al accordion en vivo, y el arabe voltea por derivacion. QUEDA: 48 demos siguen clavando dir='ltr'; el interrogante desplazado del accordion necesita dir="ltr" lang="en" en el contenedor de la demo (no es un fallo nuestro, es bidi correcto sobre texto ingles en parrafo RTL); html lang no sigue al idioma; smoke y perm:check sin ejecutar. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
/* Centring on the inline axis is a LOGICAL offset (`margin-inline-start`), never
`translateX(-50%)`: transforms are physical, so pairing one with
`inset-inline-start` centres in LTR and lands a full track-width off-axis in
RTL. Same idiom the Tick already used below. */
[data-slider][data-orientation='vertical']::before {
inset-block: 0;
inset-inline-start: 50%;
inline-size: var(--_slider-track-size);
fix(direction): getDir() no era reactivo, y 33 componentes estampaban su valor congelado El slider no respondia al cambio rtl/ltr. Resultaron ser dos defectos independientes, y el segundo era del ecosistema entero. 1) EL SLIDER. slider.css emparejaba un inset LOGICO con un transform FISICO en las tres reglas verticales (inset-inline-start: 50% + translateX(-50%)). Los transforms no se voltean, asi que en RTL el rail y el relleno quedaban fuera de eje un ancho entero. Medido en Chrome: raiz/pulgar/ticks en x=961, rail y relleno en x=955. El Tick se libraba porque ya usaba margin-inline-start — ese era el idioma correcto del propio fichero y es el que aplico. Mismo defecto en accordion.css: text-align: left en el trigger, que era el UNICO text-align fisico de todo eidos; ahora quedan cero. 2) LA RAIZ. makeDimension().get() leia engine.snapshot() esquivando la celda $state, asi que prefs.<dim>.get() — y con ella el soma.prefs.getDir() que la guia manda usar — era una lectura sin tracking. Los ~64 componentes que resuelven su direccion desde prefs la congelaban al montarse. La proyeccion DOM se salvaba porque usa onChange: misma dimension, dos caminos de lectura, solo uno reactivo. Arreglado con un contador de cambios POR dimension, bombeado desde el effectiveDiff que el motor ya calcula, de modo que cubre tambien el cambio por DERIVACION (direction siguiendo a language) y no solo el intent. Un contador por clave y no una celda global: con una sola celda, tocar motion despertaria las 64 derivaciones de direccion. El test negativo lo clava. 3) VALOR NO ES AFIRMACION. 33 de 37 componentes estampaban dir en su raiz con un valor siempre concreto, asi que una app que ponga html dir=rtl sin registrar la preferencia se encontraba 33 islas volteadas del reves. Investigadas las cinco librerias de referencia: MUI y react-aria no estampan nunca, Radix estampa siempre (sus mantenedores arrastran discussions/1405 por esto), y Zag estampa prop("dir") — presente si alguien lo pidio, ausente si no. Adoptado el modelo de Zag pero uniforme: el atributo NUNCA tiene defecto. La herencia la hace el navegador por AUSENCIA de atributo, sin ninguna lectura del DOM, asi que la cadena prop -> prefs -> 'ltr' se queda intacta. 4) HOMOGENEIDAD. Habia DIEZ formas distintas de resolver lo mismo en los wrappers y CINCO de declarar el tipo, con un cuarto escalon semantico —heredar del menu padre— enterrado en dos expresiones sueltas. Normalizado a activeDir(getter, soma) en los 36, un solo tipo Direction | undefined, y el escalon de los submenus declarado en la llamada, donde se ve. 5) LA SHELL. El toggle de direccion pasa de 2 a 3 estados: auto hace clearIntent, que es lo unico que deja ganar al derive. Y entra el arabe en el catalogo de idiomas, porque sin un idioma RTL la derivacion no era disparable. Con las dos cosas, elegir arabe voltea la direccion sin tocar el control — la cadena documentada funcionando de punta a punta por primera vez. Verificado: check 74 = linea base exacta, cero nuevos. 602/602 tests en 101 ficheros. En Chrome real: el topbar mueve al slider y al accordion en vivo, y el arabe voltea por derivacion. QUEDA: 48 demos siguen clavando dir='ltr'; el interrogante desplazado del accordion necesita dir="ltr" lang="en" en el contenedor de la demo (no es un fallo nuestro, es bidi correcto sobre texto ingles en parrafo RTL); html lang no sigue al idioma; smoke y perm:check sin ejecutar. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
margin-inline-start: calc(var(--_slider-track-size) / -2);
}
[data-slider-range] {
border-radius: var(--slider-track-radius);
background: var(--slider-range-bg);
pointer-events: none;
}
[data-slider-range][data-orientation='horizontal'] {
inset-block-start: 50%;
block-size: var(--_slider-track-size);
transform: translateY(-50%);
}
[data-slider-range][data-orientation='vertical'] {
inset-inline-start: 50%;
inline-size: var(--_slider-track-size);
fix(direction): getDir() no era reactivo, y 33 componentes estampaban su valor congelado El slider no respondia al cambio rtl/ltr. Resultaron ser dos defectos independientes, y el segundo era del ecosistema entero. 1) EL SLIDER. slider.css emparejaba un inset LOGICO con un transform FISICO en las tres reglas verticales (inset-inline-start: 50% + translateX(-50%)). Los transforms no se voltean, asi que en RTL el rail y el relleno quedaban fuera de eje un ancho entero. Medido en Chrome: raiz/pulgar/ticks en x=961, rail y relleno en x=955. El Tick se libraba porque ya usaba margin-inline-start — ese era el idioma correcto del propio fichero y es el que aplico. Mismo defecto en accordion.css: text-align: left en el trigger, que era el UNICO text-align fisico de todo eidos; ahora quedan cero. 2) LA RAIZ. makeDimension().get() leia engine.snapshot() esquivando la celda $state, asi que prefs.<dim>.get() — y con ella el soma.prefs.getDir() que la guia manda usar — era una lectura sin tracking. Los ~64 componentes que resuelven su direccion desde prefs la congelaban al montarse. La proyeccion DOM se salvaba porque usa onChange: misma dimension, dos caminos de lectura, solo uno reactivo. Arreglado con un contador de cambios POR dimension, bombeado desde el effectiveDiff que el motor ya calcula, de modo que cubre tambien el cambio por DERIVACION (direction siguiendo a language) y no solo el intent. Un contador por clave y no una celda global: con una sola celda, tocar motion despertaria las 64 derivaciones de direccion. El test negativo lo clava. 3) VALOR NO ES AFIRMACION. 33 de 37 componentes estampaban dir en su raiz con un valor siempre concreto, asi que una app que ponga html dir=rtl sin registrar la preferencia se encontraba 33 islas volteadas del reves. Investigadas las cinco librerias de referencia: MUI y react-aria no estampan nunca, Radix estampa siempre (sus mantenedores arrastran discussions/1405 por esto), y Zag estampa prop("dir") — presente si alguien lo pidio, ausente si no. Adoptado el modelo de Zag pero uniforme: el atributo NUNCA tiene defecto. La herencia la hace el navegador por AUSENCIA de atributo, sin ninguna lectura del DOM, asi que la cadena prop -> prefs -> 'ltr' se queda intacta. 4) HOMOGENEIDAD. Habia DIEZ formas distintas de resolver lo mismo en los wrappers y CINCO de declarar el tipo, con un cuarto escalon semantico —heredar del menu padre— enterrado en dos expresiones sueltas. Normalizado a activeDir(getter, soma) en los 36, un solo tipo Direction | undefined, y el escalon de los submenus declarado en la llamada, donde se ve. 5) LA SHELL. El toggle de direccion pasa de 2 a 3 estados: auto hace clearIntent, que es lo unico que deja ganar al derive. Y entra el arabe en el catalogo de idiomas, porque sin un idioma RTL la derivacion no era disparable. Con las dos cosas, elegir arabe voltea la direccion sin tocar el control — la cadena documentada funcionando de punta a punta por primera vez. Verificado: check 74 = linea base exacta, cero nuevos. 602/602 tests en 101 ficheros. En Chrome real: el topbar mueve al slider y al accordion en vivo, y el arabe voltea por derivacion. QUEDA: 48 demos siguen clavando dir='ltr'; el interrogante desplazado del accordion necesita dir="ltr" lang="en" en el contenedor de la demo (no es un fallo nuestro, es bidi correcto sobre texto ingles en parrafo RTL); html lang no sigue al idioma; smoke y perm:check sin ejecutar. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
margin-inline-start: calc(var(--_slider-track-size) / -2);
}
feat(sound): $sound orquestador (buses+voces+media) y player v2 F1-F3 sobre el servicio Rediseno clean-room del sonido (gate D-SR.1-12 firmado; la doctrina "la voz es del art" de PLAN-sound-engine s16.1 queda revocada): - Mezcla: master -> buses ui/content (gain/mute/duck refcount, gana la mas fuerte; politica pre-contexto). prefs de UI jamas tocan la obra. - Voces registradas: la calibracion es DATO del consumidor (sema registra 'sema'; el default conserva la calibracion historica - cero cambio audible, A/B del usuario: "suena igual"). - Ciudadania de media: sound.media(el) PROVEE el transporte (la forma exacta del puerto MediaProvider) + foco mixed/exclusive/duck PERSISTENTE (estado derivado; el ducker mas reciente retiene la palabra) + duckUiWhileContent + MediaSession de la fuente activa (play/pause/seekto + posicion, liberacion y promocion) + visibilidad content-aware + attach() opt-in (irreversible, CORS declarado). Re-registro idempotente por elemento. - diagnostics.ts con catalogo tipado (contrato de arts) y guards post-dispose (ningun camino abre un contexto huerfano). Auditoria AU-1..9 resuelta; bundle medido: 16,2 KB min / 5,6 KB gz. Player v2 (gate D-AP2.1-13 firmado) F1-F3: - F1 gaps de framework: aria-valuetext en Slider (valueText por thumb) + parte SecondaryRange con token --slider-secondary-bg (buffer/clip/capitulos) + formatDuration en $libs/days + fix del selector del pack (H-1: la regla de play/pause construia sobre provider y el stamp aterriza en play-button). - F2 morfo: 6 partes audio-only (artwork/artist/identity/transport/ rate-button/live-indicator), commit-set-rate, commit-set-time RETIRADO (D-AP2.13: el scrubber delega en el commit-set del Slider compuesto), sustain-loading cableado (H-9), teclado j/l, </> y 0-9 (con guard de modificadores para no pisar atajos del navegador). - F3 la migracion: el transporte default es uix.sound.media(el, {metadata}) (pineado con el motor real); nativeMediaProvider RETIRADO (media-provider.ts queda como contrato puro del puerto, sin shim); G-5: los commits de play/mute/rate cabalgan el evento de RESULTADO (play/pause saltando el pause de ended, volumechange con transicion de muted, ratechange); formatTime -> formatDuration; sub-providers + wrappers + exports de las 6 partes; prop metadata publica (captura al registrar, documentado). Verificacion: 45 suites / 458 tests verdes en el alcance (sound, sema, active-uix, active-app, morfo, slider, media-player, days) - check 74 = baseline de la rama, 0 propios - docs:check 0 - invariante de UN AudioContext intacta (e2e sin tocar). Planes de proceso y handoff al dia (PLAN-sound-redesign, PLAN-audio-player-v2, CONTINUE-sound-engine). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
/* Secondary track (buffer / preloaded region) — same geometry as the range,
layered UNDER it (source order: the range is painted after and on top). */
[data-slider-secondary-range] {
border-radius: var(--slider-track-radius);
background: var(--slider-secondary-bg);
pointer-events: none;
}
[data-slider-secondary-range][data-orientation='horizontal'] {
inset-block-start: 50%;
block-size: var(--_slider-track-size);
transform: translateY(-50%);
}
[data-slider-secondary-range][data-orientation='vertical'] {
inset-inline-start: 50%;
inline-size: var(--_slider-track-size);
fix(direction): getDir() no era reactivo, y 33 componentes estampaban su valor congelado El slider no respondia al cambio rtl/ltr. Resultaron ser dos defectos independientes, y el segundo era del ecosistema entero. 1) EL SLIDER. slider.css emparejaba un inset LOGICO con un transform FISICO en las tres reglas verticales (inset-inline-start: 50% + translateX(-50%)). Los transforms no se voltean, asi que en RTL el rail y el relleno quedaban fuera de eje un ancho entero. Medido en Chrome: raiz/pulgar/ticks en x=961, rail y relleno en x=955. El Tick se libraba porque ya usaba margin-inline-start — ese era el idioma correcto del propio fichero y es el que aplico. Mismo defecto en accordion.css: text-align: left en el trigger, que era el UNICO text-align fisico de todo eidos; ahora quedan cero. 2) LA RAIZ. makeDimension().get() leia engine.snapshot() esquivando la celda $state, asi que prefs.<dim>.get() — y con ella el soma.prefs.getDir() que la guia manda usar — era una lectura sin tracking. Los ~64 componentes que resuelven su direccion desde prefs la congelaban al montarse. La proyeccion DOM se salvaba porque usa onChange: misma dimension, dos caminos de lectura, solo uno reactivo. Arreglado con un contador de cambios POR dimension, bombeado desde el effectiveDiff que el motor ya calcula, de modo que cubre tambien el cambio por DERIVACION (direction siguiendo a language) y no solo el intent. Un contador por clave y no una celda global: con una sola celda, tocar motion despertaria las 64 derivaciones de direccion. El test negativo lo clava. 3) VALOR NO ES AFIRMACION. 33 de 37 componentes estampaban dir en su raiz con un valor siempre concreto, asi que una app que ponga html dir=rtl sin registrar la preferencia se encontraba 33 islas volteadas del reves. Investigadas las cinco librerias de referencia: MUI y react-aria no estampan nunca, Radix estampa siempre (sus mantenedores arrastran discussions/1405 por esto), y Zag estampa prop("dir") — presente si alguien lo pidio, ausente si no. Adoptado el modelo de Zag pero uniforme: el atributo NUNCA tiene defecto. La herencia la hace el navegador por AUSENCIA de atributo, sin ninguna lectura del DOM, asi que la cadena prop -> prefs -> 'ltr' se queda intacta. 4) HOMOGENEIDAD. Habia DIEZ formas distintas de resolver lo mismo en los wrappers y CINCO de declarar el tipo, con un cuarto escalon semantico —heredar del menu padre— enterrado en dos expresiones sueltas. Normalizado a activeDir(getter, soma) en los 36, un solo tipo Direction | undefined, y el escalon de los submenus declarado en la llamada, donde se ve. 5) LA SHELL. El toggle de direccion pasa de 2 a 3 estados: auto hace clearIntent, que es lo unico que deja ganar al derive. Y entra el arabe en el catalogo de idiomas, porque sin un idioma RTL la derivacion no era disparable. Con las dos cosas, elegir arabe voltea la direccion sin tocar el control — la cadena documentada funcionando de punta a punta por primera vez. Verificado: check 74 = linea base exacta, cero nuevos. 602/602 tests en 101 ficheros. En Chrome real: el topbar mueve al slider y al accordion en vivo, y el arabe voltea por derivacion. QUEDA: 48 demos siguen clavando dir='ltr'; el interrogante desplazado del accordion necesita dir="ltr" lang="en" en el contenedor de la demo (no es un fallo nuestro, es bidi correcto sobre texto ingles en parrafo RTL); html lang no sigue al idioma; smoke y perm:check sin ejecutar. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
margin-inline-start: calc(var(--_slider-track-size) / -2);
feat(sound): $sound orquestador (buses+voces+media) y player v2 F1-F3 sobre el servicio Rediseno clean-room del sonido (gate D-SR.1-12 firmado; la doctrina "la voz es del art" de PLAN-sound-engine s16.1 queda revocada): - Mezcla: master -> buses ui/content (gain/mute/duck refcount, gana la mas fuerte; politica pre-contexto). prefs de UI jamas tocan la obra. - Voces registradas: la calibracion es DATO del consumidor (sema registra 'sema'; el default conserva la calibracion historica - cero cambio audible, A/B del usuario: "suena igual"). - Ciudadania de media: sound.media(el) PROVEE el transporte (la forma exacta del puerto MediaProvider) + foco mixed/exclusive/duck PERSISTENTE (estado derivado; el ducker mas reciente retiene la palabra) + duckUiWhileContent + MediaSession de la fuente activa (play/pause/seekto + posicion, liberacion y promocion) + visibilidad content-aware + attach() opt-in (irreversible, CORS declarado). Re-registro idempotente por elemento. - diagnostics.ts con catalogo tipado (contrato de arts) y guards post-dispose (ningun camino abre un contexto huerfano). Auditoria AU-1..9 resuelta; bundle medido: 16,2 KB min / 5,6 KB gz. Player v2 (gate D-AP2.1-13 firmado) F1-F3: - F1 gaps de framework: aria-valuetext en Slider (valueText por thumb) + parte SecondaryRange con token --slider-secondary-bg (buffer/clip/capitulos) + formatDuration en $libs/days + fix del selector del pack (H-1: la regla de play/pause construia sobre provider y el stamp aterriza en play-button). - F2 morfo: 6 partes audio-only (artwork/artist/identity/transport/ rate-button/live-indicator), commit-set-rate, commit-set-time RETIRADO (D-AP2.13: el scrubber delega en el commit-set del Slider compuesto), sustain-loading cableado (H-9), teclado j/l, </> y 0-9 (con guard de modificadores para no pisar atajos del navegador). - F3 la migracion: el transporte default es uix.sound.media(el, {metadata}) (pineado con el motor real); nativeMediaProvider RETIRADO (media-provider.ts queda como contrato puro del puerto, sin shim); G-5: los commits de play/mute/rate cabalgan el evento de RESULTADO (play/pause saltando el pause de ended, volumechange con transicion de muted, ratechange); formatTime -> formatDuration; sub-providers + wrappers + exports de las 6 partes; prop metadata publica (captura al registrar, documentado). Verificacion: 45 suites / 458 tests verdes en el alcance (sound, sema, active-uix, active-app, morfo, slider, media-player, days) - check 74 = baseline de la rama, 0 propios - docs:check 0 - invariante de UN AudioContext intacta (e2e sin tocar). Planes de proceso y handoff al dia (PLAN-sound-redesign, PLAN-audio-player-v2, CONTINUE-sound-engine). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
}
[data-slider-thumb] {
inline-size: var(--_slider-thumb-size);
block-size: var(--_slider-thumb-size);
border: var(--slider-thumb-border-width) solid var(--slider-thumb-border);
border-radius: var(--slider-thumb-radius);
background: var(--slider-thumb-bg);
box-shadow: var(--slider-thumb-shadow);
cursor: grab;
outline: none;
/* `scale` is its own property, independent of the `transform: translate()`
the soma uses to centre the thumb — they compose, so the lift scales the
thumb in place. */
transition:
background var(--slider-transition-duration) var(--slider-transition-ease),
border-color var(--slider-transition-duration) var(--slider-transition-ease),
scale var(--slider-transition-duration) var(--slider-transition-ease),
box-shadow var(--slider-transition-duration) var(--slider-transition-ease);
}
/* Pickup lift on grab: scale toward the viewer + a higher shadow. */
[data-slider-thumb]:active {
cursor: grabbing;
refactor(eidos): el catálogo habla un idioma — 269 claves al vocabulario firmado D-TH.6, ejecutada. El slot de tinta pasa a `fg` y el modificador interactivo se pone delante, que es lo que theming §6.7 r7 documentaba sin guard desde que se escribió. Value-preserving: renombra la clave en `recipes/base.ts` y sus 793 referencias en 101 ficheros; no toca un solo valor. claves renombradas 269 en 66 componentes referencias 793 en 101 ficheros censo antes/después 162 recetas · 5.203 knobs · 1.841 públicos (37 %) · 56 sin contrato — IDÉNTICO, como debe ser un rename --names antes/después 269 desviadas → 0 Qué NO entra, y por qué: 47 `{rol}-{slot-de-rol}` canónicas: COLOR_ROLE_SLOTS pone el modificador detrás POR CONSTRUCCIÓN (`primary-solid-hover`) 13 exentas firmadas `--focus-ring-*` es familia del sistema; el color de `aura` es un sustantivo; `stop-color` ES una parte de gradient-builder 47 hovers neutros por VALOR: migran a la capa de estado (§38 + R-4.3), no se renombran — firma 3 6 ocurrencias en historia changelog, errores-toxico, PLAN-affix/background: reescribir un registro fechado lo vuelve mentira El clasificador vive en el censo (`--names`), no en un script suelto, para que el guard R-5.3 consuma la MISMA gramática que el codemod. Su muta-prueba (`__names-mutatest.ts`, 27 casos) es lo que hizo el trabajo: cazó que yo promovía al frente CUALQUIER valor declarado por el morfo, y así `--sidebar-width-icon` (la anchura del raíl colapsado) se convertía en `--sidebar-icon-width` (la anchura de un icono), que es otra cosa. La firma dice «delante lo interactivo, detrás lo dimensional y contextual»: ahora sólo promociona el vocabulario interactivo cerrado, y lo contextual —`below`, `loaded`, `vertical`, `icon`— se queda donde estaba. Los cinco casos de regresión están en la muta-prueba. También cazó que `dropdown-menu.item-bg-hover` lee `var(--color-primary-element)`: es un hover CON VALENCIA (el palette swap de recipe-contract §2), no el hover bespoke que §38 deprecó. Clasificar por el nombre lo habría metido en una migración que no le toca; se clasifica por el VALOR. Verificación (§7.4, artefacto por paso): diff de generated/ 269 renombres 1:1 · 0 cambios de valor · 22 privados reapuntados a los nombres nuevos (`__names-verify-diff`) computed tag-group · field · tabs: 6.467 valores en 23 estados, 0 diffs — y comprobado en el navegador que sirve el CSS nuevo, para que ese 0 no sea el de una copia cacheada huérfanos 0 en código suite eidos 434 pasan · 1 rojo, el conocido (`skin-media-player`). `recipe-css-contract` verde: es el guard que caza el `var()` sin fallback a un nombre que ya no se emite, o sea el fallo exacto de un rename a medias component:audit 163 PASS · 3 NEEDS-WORK, los tres SIN TOCAR por esto check 0 errores en ficheros tocados (los 73 globales son de otras sesiones de la rama; se atribuye por fichero) rtl:check 0 · docs:check 0 formato el renombrado no alarga ninguna línea: ningún fichero tiene más líneas de 100 chars que antes, así que no se pasa prettier — hacerlo reformateaba 300 ficheros de deriva ajena Entra aquí la corrección del repaso de los 7 ya hechos: `gradient-builder.checker-color` → `checker-fg` (el damero de transparencia; ahí `color` era slot). Sus `stop-color-*` no se tocan. Queda para el paso siguiente: R-5.3 no puede graduar a `error` directo mientras los 47 hovers neutros sigan hablando el idioma viejo — o migran antes (firma 3, ya firmada), o el guard necesita una exención greppable. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
scale: var(--slider-active-thumb-scale);
box-shadow: var(--slider-active-thumb-shadow);
}
[data-slider-thumb]:focus-visible {
fix(eidos): component-coherence audit FASE 2 — focus-ring, fields, segments, langs, API nits Bundles the uncommitted audit work on this branch (active-uix): - DIM 5A: focus-ring unified to `outline` across the control/field set (kills ~18 box-shadow rings; closes forced-colors a11y). NOTE: spin-field + drawer-close orphan-token loose ends from this migration still trip the eidos contract test — to clean when that migration settles. - DIM 5D: field/input backgrounds homogenized to surface-default; list-surface padding recalibrated 2/4/6/8/10; menu-dial internal vars privatized. - DIM 5E: date/time/color-field + date-range-picker segments delegate the active state to the shared field-segment-state layer; the input transition is reduced to `background` only so the focus border no longer flickers on keyboard increment (border-color is now instant; the ring is an outline). - Langs (cero-hardcodes): IconButton-cascade wrappers (carousel/combobox/ tree-grid/table/search-field/banner) + onion-menu + cropper + time-picker day-period now route aria-labels through morfo langs instead of hardcoded English (the hardcode was overriding soma's already-resolved label). Fixes 2 contract divergences (combobox Clear->Clear value, table toggle label). - DIM 5B: code-block/kbd variants aliased to ControlVariant (no duplicate literals); image-picker default size sm->md. Excludes words/palabras/chronos and the metrics/demos tracks (separate work). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4 months ago
/* Keep the thumb's own drop shadow; the focus ring is a flush outline (no gap)
so it survives forced-colors. */
box-shadow: var(--slider-thumb-shadow);
outline: var(--focus-ring-width) solid var(--focus-ring-color);
outline-offset: 0;
}
[data-slider-thumb][data-disabled] {
cursor: not-allowed;
}
[data-slider-tick] {
inline-size: var(--_slider-tick-size);
block-size: var(--_slider-tick-size);
border-radius: var(--slider-tick-radius);
background: var(--slider-tick-bg);
pointer-events: none;
}
[data-slider-tick][data-orientation='horizontal'] {
inset-block-start: 50%;
margin-block-start: calc(var(--_slider-tick-size) / -2);
}
[data-slider-tick][data-orientation='vertical'] {
inset-inline-start: 50%;
margin-inline-start: calc(var(--_slider-tick-size) / -2);
}
[data-slider-tick][data-active] {
refactor(eidos): el catálogo habla un idioma — 269 claves al vocabulario firmado D-TH.6, ejecutada. El slot de tinta pasa a `fg` y el modificador interactivo se pone delante, que es lo que theming §6.7 r7 documentaba sin guard desde que se escribió. Value-preserving: renombra la clave en `recipes/base.ts` y sus 793 referencias en 101 ficheros; no toca un solo valor. claves renombradas 269 en 66 componentes referencias 793 en 101 ficheros censo antes/después 162 recetas · 5.203 knobs · 1.841 públicos (37 %) · 56 sin contrato — IDÉNTICO, como debe ser un rename --names antes/después 269 desviadas → 0 Qué NO entra, y por qué: 47 `{rol}-{slot-de-rol}` canónicas: COLOR_ROLE_SLOTS pone el modificador detrás POR CONSTRUCCIÓN (`primary-solid-hover`) 13 exentas firmadas `--focus-ring-*` es familia del sistema; el color de `aura` es un sustantivo; `stop-color` ES una parte de gradient-builder 47 hovers neutros por VALOR: migran a la capa de estado (§38 + R-4.3), no se renombran — firma 3 6 ocurrencias en historia changelog, errores-toxico, PLAN-affix/background: reescribir un registro fechado lo vuelve mentira El clasificador vive en el censo (`--names`), no en un script suelto, para que el guard R-5.3 consuma la MISMA gramática que el codemod. Su muta-prueba (`__names-mutatest.ts`, 27 casos) es lo que hizo el trabajo: cazó que yo promovía al frente CUALQUIER valor declarado por el morfo, y así `--sidebar-width-icon` (la anchura del raíl colapsado) se convertía en `--sidebar-icon-width` (la anchura de un icono), que es otra cosa. La firma dice «delante lo interactivo, detrás lo dimensional y contextual»: ahora sólo promociona el vocabulario interactivo cerrado, y lo contextual —`below`, `loaded`, `vertical`, `icon`— se queda donde estaba. Los cinco casos de regresión están en la muta-prueba. También cazó que `dropdown-menu.item-bg-hover` lee `var(--color-primary-element)`: es un hover CON VALENCIA (el palette swap de recipe-contract §2), no el hover bespoke que §38 deprecó. Clasificar por el nombre lo habría metido en una migración que no le toca; se clasifica por el VALOR. Verificación (§7.4, artefacto por paso): diff de generated/ 269 renombres 1:1 · 0 cambios de valor · 22 privados reapuntados a los nombres nuevos (`__names-verify-diff`) computed tag-group · field · tabs: 6.467 valores en 23 estados, 0 diffs — y comprobado en el navegador que sirve el CSS nuevo, para que ese 0 no sea el de una copia cacheada huérfanos 0 en código suite eidos 434 pasan · 1 rojo, el conocido (`skin-media-player`). `recipe-css-contract` verde: es el guard que caza el `var()` sin fallback a un nombre que ya no se emite, o sea el fallo exacto de un rename a medias component:audit 163 PASS · 3 NEEDS-WORK, los tres SIN TOCAR por esto check 0 errores en ficheros tocados (los 73 globales son de otras sesiones de la rama; se atribuye por fichero) rtl:check 0 · docs:check 0 formato el renombrado no alarga ninguna línea: ningún fichero tiene más líneas de 100 chars que antes, así que no se pasa prettier — hacerlo reformateaba 300 ficheros de deriva ajena Entra aquí la corrección del repaso de los 7 ya hechos: `gradient-builder.checker-color` → `checker-fg` (el damero de transparencia; ahí `color` era slot). Sus `stop-color-*` no se tocan. Queda para el paso siguiente: R-5.3 no puede graduar a `error` directo mientras los 47 hovers neutros sigan hablando el idioma viejo — o migran antes (firma 3, ya firmada), o el guard necesita una exención greppable. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
background: var(--slider-active-tick-bg);
}
@media (prefers-reduced-motion: reduce) {
[data-slider-thumb] {
transition: none;
}
/* No pickup zoom under reduced motion — the raised shadow still conveys grab. */
[data-slider-thumb]:active {
scale: 1;
}
}

Powered by TurnKey Linux.