Swatch
Un selector de variantes con radios nombrados y objetivos de 44px, debajo de la vista previa que cambian.
Finish: Blue
Instalación
$ pnpm dlx shadcn@latest add @ssntl/swatchUso
import { Swatch, SwatchGroup } from "@/components/ui/swatch"
<SwatchGroup aria-label="Color">
<Swatch name="color" value="azul" color="var(--blue-600)" aria-label="Azul" defaultChecked />
<Swatch name="color" value="negro" color="var(--neutral-950)" aria-label="Negro" />
</SwatchGroup>- Visibilidad al tocar: pon los swatches debajo de la vista previa que cambian, así el dedo nunca tapa el resultado.
- Cada swatch es un radio con nombre (aria-label): el color solo no significa nada para un lector de pantalla. Muestra también el nombre elegido en texto.
- Un objetivo de 44px alrededor de un punto de 32px; el elegido lleva un anillo de 2px con separación, no solo un cambio de borde.
- Pasa un color del sistema (var(--blue-600)) o el color real del producto; va en un style en línea porque es un dato.
Código
import * as React from "react"
import { cn } from "@/lib/utils"
// A variant picker: color, finish, pattern. Each swatch is a native radio
// with a 44px target around a 32px dot, named with aria-label because the
// color alone means nothing to a screen reader. Touch visibility: put the
// group below the preview it changes, so the finger never covers the result.
function SwatchGroup({
className,
...props
}: React.ComponentProps<"fieldset">) {
return (
<fieldset
data-slot="swatch-group"
className={cn("flex min-w-0 flex-wrap items-center gap-1", className)}
{...props}
/>
)
}
function Swatch({
color,
className,
style,
...props
}: Omit<React.ComponentProps<"input">, "type" | "color"> & {
/** Any CSS color; it is data, so it goes in an inline style. */
color: string
"aria-label": string
}) {
return (
<label
data-slot="swatch"
className="grid size-11 shrink-0 cursor-pointer place-items-center rounded-full has-disabled:cursor-not-allowed has-disabled:opacity-40"
>
<input
type="radio"
style={{ ...style, backgroundColor: color }}
className={cn(
"size-8 cursor-pointer appearance-none rounded-full outline -outline-offset-1 outline-black/10 dark:outline-white/10",
"ring-offset-2 ring-offset-card transition-[box-shadow] duration-(--duration-fast) ease-out checked:ring-2 checked:ring-foreground",
"focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-ring",
className
)}
{...props}
/>
</label>
)
}
export { Swatch, SwatchGroup }