El eje X del area lleva un CANAL de color repartido por la dimension inline —
un eje de lectura— asi que espeja, igual que el `ColorArea` de react-aria
(«orientation of the gradient background, positioning of the thumb, and
dragging behavior is automatically mirrored»). Una rueda radial no lo haria:
es el mismo corte lectura-vs-radial que ya usa la regla de las graficas. Mi
analogia previa con el `cropper` era mala y queda retirada — alli paneas una
imagen, aqui recorres un canal.
Medido antes: pinchar el MISMO punto fisico daba el mismo valor en las dos
direcciones, y el degradado no seguia al thumb.
Lo que faltaba, por pieza:
area · puntero la fraccion fisica se espeja sobre el canal
area · thumb ancla PHYSICA (`left`) alimentada ya espejada
area · flechas ArrowRight mueve el thumb a la derecha ⇒ BAJA el canal
area · degradados saturacion y arcoiris de hue voltean con `:dir(rtl)`
sliders de canal solo el degradado
⚠️ El thumb conserva `left` FISICO a proposito: el recipe lo centra con
`translate(-50%,-50%)`, y ancla logica + translate fisico es justo la trampa
que el contrato de RTL prohibe. Soma le pasa la fraccion ya espejada
(`physicalXProgress`).
Los sliders de canal casi no necesitaban nada: desde el refactor de 2026-05-21
componen el `SliderProvider` generico, que ya espeja thumb, puntero y teclado.
Pero su rampa estaba clavada a `to right`, asi que el color BAJO el thumb
dejaba de ser el valor — medido con hue 210: thumb a 123px del borde derecho y
degradado contando aun desde la izquierda.
Verificado en Chrome en las dos direcciones: pinchando al 10% del borde
izquierdo fisico, LTR da saturation 10 y RTL da 90; el blanco del area y el
rojo del hue quedan a la derecha en RTL; ArrowRight mueve el thumb a la derecha
en ambas. Los dos tests nuevos salen ROJOS con el provider anterior.
`Home`/`End` se quedan en min/max del CANAL (un valor, no un borde fisico),
igual que en un slider.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ -185,6 +185,26 @@ The 2D `Area` by default maps X = `saturation`, Y = `brightness`. Override via t
- **EyeDropper**: Enter / Space invokes the browser picker.
- **Escape** inside the Popover: closes and returns focus to the Trigger.
## RTL
The x axis carries a colour **channel** across the inline dimension — a reading
axis — so **the Area mirrors in RTL**, together with the channel sliders. This
follows react-aria's `ColorArea` ("orientation of the gradient background,
positioning of the thumb, and dragging behavior is automatically mirrored"); a
radial wheel would not mirror, the same reading-vs-radial split the chart rule
uses. The Y axis is the block axis and never flips.
| Piece | In RTL |
| --- | --- |
| Area pointer | the physical fraction is mirrored onto the channel: clicking 25% from the left edge yields channel 75 |
| Area thumb | anchor stays **physical** (`left`) — the recipe centres it with `translate(-50%,-50%)`, and a logical anchor paired with a physical translate is the trap the RTL contract forbids. Soma feeds it the already-mirrored fraction (`physicalXProgress`) |
| Area arrows | `ArrowRight` moves the thumb physically right, so it **decreases** the channel |
| Area gradients | the saturation ramp and the hue rainbow flip via `:dir(rtl)` in the recipe — `to right` has no logical form |
| Channel sliders | the generic `SliderProvider` underneath already mirrors thumb, pointer and keyboard; only `channelGradient` needed the direction, or the swatch under the thumb showed the wrong colour |
`Home`/`End` stay **min/max of the channel** (a value, not a physical edge), the
same way a slider's do.
## Data attributes
All parts emit their canonical `data-color-picker-{part}` attribute. State-bearing attributes: