ColorPicker Sélecteur de Couleur
Introduction
Un sélecteur de couleurs avec une zone de saturation/luminosité, des curseurs de teinte et alpha, des entrées de canal modifiables (HEX/RGBA/HSLA), des échantillons prédéfinis et un déclencheur d'échantillon en option qui ouvre le panneau dans une fenêtre contextuelle. Il suit l'anatomie du sélecteur de couleurs Ark UI / Park UI — chaque partie porte data-scope="colorPicker" et une data-part correspondante — mais est entièrement implémenté sur Hono JSX et Panda CSS, avec pas de React et pas de dépendance @ark-ui/react.
Le composant est divisé de la même manière que le reste de la bibliothèque :
app/components/ui/color-picker-primitive.tsx— calcul des couleurs, contexte et chaque partie anatomique, tous rendus par le serveur.-app/islands/color-picker.tsx— mince enveloppe d'îlot autour deInteractiveColorPicker, qui possède l'état et attache des gestionnaires de pointeur/clavier.-app/theme/recipes/color-picker.ts— la recette du slot Panda CSS. Les couleurs sont conservées en interne sous forme de HSVA ({ h : 0 à 360, s : 0 à 100, v : 0 à 100, a : 0 à 1 }), le modèle naturel pour une zone de saturation/luminosité, et converties sur les bords.
Quoi de neuf dans cette révision
- La sortie HSLA est du vrai HSL.
hsvaToHslaStringa préalablement marqué les nombres bruts de saturation/valeur HSV dans une chaînehsla(), produisant une couleur différente de celle sélectionnée (par exemple, un rouge pur rendu sous la formehsla(0, 100%, 100%, 1)— blanc). Il convertit désormais correctement HSV → HSL (hsla(0, 100%, 50%, 1)).- La ligne d'entrée HSLA modifie les véritables canaux HSL. Les entrées de saturation/luminosité utilisées pour afficher les valeurs HSV sous les étiquettes HSL ; les modifications et les valeurs affichées concordent désormais, et un canal « l » dédié mappe les modifications de légèreté dans le modèle HSV.- Une entrée hexadécimale non valide est rejetée au lieu d'être validée. La saisie de déchets dans le champ hexadécimal revenait auparavant au blanc et l'émettait sous la forme d'un changement de valeur. Le champ valide désormais#RGB,#RGBA,#RRGGBBet#RRGGBBAA(avec ou sans le#initial) et ignore tout le reste.- La sélection du format est étiquetée pour la technologie d'assistance.
Prise en charge du clavier
La zone et les deux curseurs sont focalisables (« Tab ») et utilisables au clavier :
| Key | Area | Hue slider | Alpha slider |
|---|---|---|---|
| ← / → | Saturation ±1 | Hue ±1° | Alpha ±1% |
| ↑ / ↓ | Brightness ±1 | — | — |
| Shift + arrows | ±10 steps | ±10° | ±10% |
| Home / End | Min / max corner | 0° / 360° | 0% / 100% |
Avec trigger, Esc et des clics extérieurs ferment le popover.
Accessibilité
- Les curseurs de zone et de canal exposent
role="slider"avecaria-valuemin/aria-valuemax/aria-valuenow(la zone signale en outre les deux canaux viaaria-valuetext).- Les entrées de canal, la sélection de format, les échantillons prédéfinis et le déclencheur de la pipette portent tous un « aria-label » ; l'échantillon prédéfini actif est annoncé viaaria-pressed.-disabledetreadOnlysuppriment les taquets de tabulation interactifs et désactivent les boutons d'échantillon ; l'état est reflété sur « data-disabled » / « data-readonly » sur chaque partie.- Le bouton compte-gouttes est désactivé lorsque l'APIEyeDroppern'est pas disponible, ce n'est donc jamais un contrôle mort.
Emplacements de style
La recette du slot Panda CSS (app/theme/recipes/color-picker.ts) stylise les parties répertoriées sous Anatomie. Les états sont déterminés par les attributs de données (data-disabled, data-readonly, data-state="checked" sur l'échantillon actif, data-channel sur les parties du curseur), afin que les skins personnalisés puissent les cibler sans toucher à la recette. La variante « taille » met à l'échelle la zone, les échantillons et l'espacement.
Utilisation
import { ColorPicker } from "../components/ui";
export default function MyPage() {
return (
<>
{/* Inline picker, static SSR (no signal, no island) */}
<ColorPicker interactive={false} />
{/* Interactive inline picker */}
<ColorPicker
label="Brand colour"
defaultValue="#3b82f6"
onValueChange={({ value }) => console.log(value)}
/>
{/* Swatch trigger + popover, custom presets */}
<ColorPicker
trigger
label="Accent"
defaultValue="#22c55e"
presets={["#ef4444", "#f97316", "#22c55e", "#3b82f6"]}
closeOnSelect
/>
{/* Inside a native form — submits as `theme` (hex) */}
<form method="post" action="/settings">
<ColorPicker name="theme" defaultValue="#7c3aed" />
<button type="submit">Save</button>
</form>
</>
);
}
Constructeur de pages CMS
Ce composant est disponible sous forme de bloc colorPicker dans Page Builder (content/pages/*.json) :
{
"type": "colorPicker",
"label": "Brand colour",
"defaultValue": "#3b82f6"
}
Propriétés
<ColorPicker /> (le wrapper stylisé) accepte :
| Propriété | Type | Description |
|---|
| value | `string \ | HSVA` | Current colour (controlled). Strings accept hex (#rgb, #rgba, #rrggbb, #rrggbbaa), rgb()/rgba(), and hsl()/hsla(). |
| defaultValue | `string \ | HSVA` | Initial colour (uncontrolled). Defaults to #7c3aed. |
| format | `"hex" \ | "rgba" \ | "hsla"` | Active input format (controlled). |
| defaultFormat | `"hex" \ | "rgba" \ | "hsla"` | Initial input format (uncontrolled). Defaults to "hex". |
| onValueChange | (details: { value: string; hsva: HSVA }) => void | Appelé à chaque changement de couleur ; value est la chaîne hexadécimale (avec suffixe alpha lorsqu'elle est translucide). |
| onFormatChange | (details: { format: ColorFormat }) => void | Appelé lorsque la sélection de format change. |
| trigger | boolean | Créez un déclencheur d'échantillon qui ouvre le panneau dans une fenêtre contextuelle plutôt qu'en ligne. |
| open / defaultOpen | boolean | État popover (contrôlé/non contrôlé) ; n'a de sens qu'avec trigger. |
| onOpenChange | (details: { open: boolean }) => void | Appelé lorsque le popover s'ouvre ou se ferme. |
| closeOnSelect | boolean | Fermez le popover après avoir sélectionné un échantillon prédéfini. La valeur par défaut est « false ». |
| presets | string[] | Couleurs d'échantillon prédéfinies. La valeur par défaut est une palette de 14 couleurs organisée ; passez [] pour vous cacher. |
| name | string | Renvoie une entrée masquée portant la valeur hexadécimale pour la soumission native <form>. |
| label | `string \ | JSX.Élément` | Optional label rendered above the picker. |
| showArea / showSliders / showInputs / showSwatches | boolean | Basculez les sections individuelles du panneau. Tous sont par défaut « true ». |
| disabled | boolean | Désactive toutes les interactions. |
| readOnly | boolean | La valeur est visible mais ne peut pas être modifiée. |
| size | `"sm" \ | "md" \ | "lg"` | Recipe size variant (area height, swatch size, spacing). Defaults to "md". |
| interactive | boolean | Force (ou supprime) l’hydratation comme une île. |
Hydratation
Le wrapper s'hydrate automatiquement lorsqu'un signal comportemental est présent — n'importe quel rappel, une « valeur »/« defaultValue », « open »/defaultOpen » ou « trigger » — et restitue du HTML statique dans le cas contraire. interactive=se désinscrit toujours ;interactive(ouinteractive=) s'engage toujours. La décision passe par l'assistant shouldHydrate()` partagé, comme chaque île de la bibliothèque.
Utilitaires de couleur
Exporté depuis la primitive pour réutilisation et test :
| Helper | Description |
|---|---|
parseColor(input) | Analyse les chaînes hex/rgb(a)/hsl(a), les objets HSVA-ish ou RGB-ish dans un « HSVA » serré. Retombe au blanc. |
hsvToRgb(h, s, v) / rgbToHsv(r, g, b) | Conversion HSV ↔ RVB. |
hsvToHsl(h, s, v) / hslToHsv(h, s, l) | Conversion HSV ↔ HSL (s/v/l de 0 à 100). |
hexToRgb(hex) | Analyse l'hexadécimal à 3/4/6/8 chiffres en { r, g, b, a } ou null en cas de malformation. |
hsvaToHex(c, includeAlpha?) | Chaîne hexagonale ; ajoute l'octet alpha uniquement lorsque cela est demandé et translucide. |
hsvaToRgbaString(c) / hsvaToHslaString(c) | Chaînes CSS rgba() / hsla(). |
Notes de production
- Aucune nouvelle dépendance. Tous les calculs de couleurs (conversions HSV/HSL/RVB/hex, analyse) sont implémentés localement et testés unitairement ; rien n'est extrait de
@rc-component,@ark-uiou React.- Sûr SSR. Chaque partie affiche un balisage significatif sur le serveur — la variante statique est un aperçu fidèle et non interactif du même DOM exact que l'île hydrate.- Contrôlé ou non contrôlé.value/format/openprennent chacun en charge les deux modes avec les homologuespar défaut*habituels.- Précision. Attribuez des objetsHSVA(à partir dedetails.hsvadeonValueChange) plutôt que des chaînes ré-analysées dans des scénarios contrôlés pour éviter la dérive d'arrondi aller-retour entre les formats.- Prêt pour le formulaire. La propnamerestitue une entrée cachée avec la valeur hexadécimale actuelle, synchronisée à chaque modification.