sdxc

Type to search, or start from one of these:

ColorSwatchPicker

A set of mutually exclusive color options built from native <input type="radio"> controls sharing one grouping name, rendered as a role="radiogroup" host wrapping ColorSwatchPicker.Swatch instances styled from each input's own :checked state.

Shown on every issue carrying this label.

needs-triage · Blue
View code
let picked = { value: "#3b82f6", name: "Blue" };

<div mix={[vstack({ gap: 2, align: "start" }), maxIs("22rem")]}>
	<Label id="preview-label-color">Label color</Label>

	<ColorSwatchPicker
		aria-labelledby="preview-label-color"
		name="labelColor"
		mix={[
			flexWrap(),
			on<HTMLDivElement, "change">("change", (event) => {
				let input = event.target;
				if (!(input instanceof HTMLInputElement)) return;

				picked = LABEL_COLORS.find((color) => color.value === input.value) ?? picked;
				void handle.update();
			}),
		]}
	>
		{LABEL_COLORS.map((color) => (
			<ColorSwatchPicker.Swatch
				key={color.value}
				value={color.value}
				aria-label={color.name}
				shape="rounded"
				checked={color.value === picked.value}
				parts={{ input: [ariaChecked()] }}
			/>
		))}
	</ColorSwatchPicker>

	<Description id="preview-label-color-hint">
		Shown on every issue carrying this label.
	</Description>

	<div mix={[hstack({ gap: 2, align: "center" })]}>
		<ColorSwatch value={picked.value} shape="rounded" size="sm" />
		<span mix={[text("sm")]}>
			<span mix={[weight("medium")]}>needs-triage</span>{" "}
			<span mix={[fg("neutral")]}>· {picked.name}</span>
		</span>
	</div>
</div>

Installation

An ordinary dependency: install it, import the theme once, and import the component where it is used.

npm add @sdxc/ui
import "@sdxc/ui/theme.css";

Usage

import { ColorSwatchPicker } from "@sdxc/ui";

Renders the group host: a role="radiogroup" <div> laying its ColorSwatchPicker.Swatch options out in a wrapping row, sharing its name through context so each option defaults to the group's own identifier.

Composition

The parts the component publishes, each a static property of the host.

  • ColorSwatchPicker
  • ColorSwatchPicker.SwatchRenders a single option: a hidden <input type="radio"> paired with a ColorSwatch indicator, styled through precededBy() since a bare element-first selector here would serialize as a declaration, not a match.

Examples

<ColorSwatchPicker aria-label={t("form.brandColor.label")}>
	<ColorSwatchPicker.Swatch value="#f97316" aria-label={t("color.orange")} shape="circle" size="lg" />
	<ColorSwatchPicker.Swatch value="#a855f7" aria-label={t("color.purple")} shape="circle" size="lg" />
</ColorSwatchPicker>

Props

Read from the component's own types, so every prop, every default and every allowed value is listed.

PropTypeDescription
name?stringNative grouping name shared by every ColorSwatchPicker.Swatch nested inside. Defaults to the group's own stable instance id when omitted.

Also accepts everything in TagProps<"div">.

ColorSwatchPicker.Swatch

PropTypeDescription
valuestringThe color this option submits and previews, already resolved to a literal CSS color value — the same value ColorSwatch paints its indicator with and submits with the enclosing form.
"aria-label"stringAccessible name announced for this option in place of visible text — a color's name, for instance. Required, since the indicator identifies the option to assistive technology through this text alone.
name?stringNative grouping name for this option's underlying input. Defaults to the name provided by the nearest ancestor ColorSwatchPicker — set this only to opt a single option out of its group's shared name.
shape?ColorSwatch.ShapeThe indicator's shape variant, forwarded to ColorSwatch.
size?ColorSwatch.SizeThe indicator's size variant, forwarded to ColorSwatch.
defaultChecked?booleanWhether this option starts selected, for a form that never tracks selection itself.
checked?booleanWhether this option is selected, for a form that tracks selection itself.
disabled?booleanWhether this option is inert and excluded from the group's tab order.
required?booleanMarks the enclosing native radio group as requiring one option selected.
parts?{ /** Additional mixin(s) applied to the hidden native `<input type="radio">`. */ input?: TagProps<"input">["mix"]; /** Additional mixin(s) applied to the {@link ColorSwatch} indicator. */ indicator?: TagProps<"span">["mix"]; }Per-part styling for the option's hidden input and its ColorSwatch indicator, layered after each part's own built-in styling. The mix prop styles the option's outer <label> host.

Also accepts everything in Omit<TagProps<"label">, "children" | "aria-label">.