ColorSlider
A 1D slider component for adjusting a single color channel, with a gradient track that reflects the current color.
Preview
Source code
tsx
import "internationalized-color/css";
import { ColorSlider, useColor } from "@urcolor/react";
export default function ColorSliderHue() {
const { color, setColor } = useColor("hsl(210, 80%, 50%)");
return (
<ColorSlider.Root
value={color}
onValueChange={setColor}
colorSpace="hsl"
channel="h"
className="w-full"
>
<ColorSlider.Control>
<ColorSlider.Track className="relative h-5 overflow-hidden rounded-xl">
<ColorSlider.Gradient
className="absolute inset-0 rounded-xl"
colors={["red", "yellow", "lime", "cyan", "blue", "magenta", "red"]}
/>
<ColorSlider.Thumb
className="
block size-5 rounded-full border-[2.5px] border-white bg-white
shadow-[0_0_0_1px_rgba(0,0,0,0.3),0_2px_4px_rgba(0,0,0,0.3)]
focus-visible:shadow-[0_0_0_1px_rgba(0,0,0,0.3),0_0_0_3px_rgba(66,153,225,0.6)]
"
aria-label="Hue"
/>
</ColorSlider.Track>
</ColorSlider.Control>
</ColorSlider.Root>
);
}Anatomy
tsx
<ColorSlider.Root>
<ColorSlider.Control>
<ColorSlider.Track>
<ColorSlider.Checkerboard />
<ColorSlider.Gradient />
<ColorSlider.Thumb />
</ColorSlider.Track>
</ColorSlider.Control>
</ColorSlider.Root>Examples
Hue
Source code
tsx
import "internationalized-color/css";
import { ColorSlider, useColor } from "@urcolor/react";
export default function ColorSliderHue() {
const { color, setColor } = useColor("hsl(210, 80%, 50%)");
return (
<ColorSlider.Root
value={color}
onValueChange={setColor}
colorSpace="hsl"
channel="h"
className="w-full"
>
<ColorSlider.Control>
<ColorSlider.Track className="relative h-5 overflow-hidden rounded-xl">
<ColorSlider.Gradient
className="absolute inset-0 rounded-xl"
colors={["red", "yellow", "lime", "cyan", "blue", "magenta", "red"]}
/>
<ColorSlider.Thumb
className="
block size-5 rounded-full border-[2.5px] border-white bg-white
shadow-[0_0_0_1px_rgba(0,0,0,0.3),0_2px_4px_rgba(0,0,0,0.3)]
focus-visible:shadow-[0_0_0_1px_rgba(0,0,0,0.3),0_0_0_3px_rgba(66,153,225,0.6)]
"
aria-label="Hue"
/>
</ColorSlider.Track>
</ColorSlider.Control>
</ColorSlider.Root>
);
}Saturation
Source code
tsx
import { useMemo } from "react";
import "internationalized-color/css";
import { getChannelConfig } from "@urcolor/core";
import { ColorSlider, useColor } from "@urcolor/react";
export default function ColorSliderSaturation() {
const { color, setColor } = useColor("hsl(210, 80%, 50%)");
const gradientColors = useMemo(() => {
const cfg = getChannelConfig("hsl", "s");
if (!cfg) return ["gray", "blue"];
const steps = 7;
const colors: string[] = [];
const cMin = cfg.culoriMin ?? cfg.min;
const cMax = cfg.culoriMax ?? cfg.max;
for (let i = 0; i < steps; i++) {
const t = i / (steps - 1);
const val = cMin + t * (cMax - cMin);
colors.push(color.set({ mode: "hsl", s: val })?.toString() ?? "black");
}
return colors;
}, [color]);
return (
<ColorSlider.Root
value={color}
onValueChange={setColor}
colorSpace="hsl"
channel="s"
className="w-full"
>
<ColorSlider.Control>
<ColorSlider.Track className="relative h-5 overflow-hidden rounded-xl">
<ColorSlider.Gradient
className="absolute inset-0 rounded-xl"
colors={gradientColors}
/>
<ColorSlider.Thumb
className="
block size-5 rounded-full border-[2.5px] border-white bg-white
shadow-[0_0_0_1px_rgba(0,0,0,0.3),0_2px_4px_rgba(0,0,0,0.3)]
focus-visible:shadow-[0_0_0_1px_rgba(0,0,0,0.3),0_0_0_3px_rgba(66,153,225,0.6)]
"
aria-label="Saturation"
/>
</ColorSlider.Track>
</ColorSlider.Control>
</ColorSlider.Root>
);
}Lightness
Source code
tsx
import { useMemo } from "react";
import "internationalized-color/css";
import { getChannelConfig } from "@urcolor/core";
import { ColorSlider, useColor } from "@urcolor/react";
export default function ColorSliderLightness() {
const { color, setColor } = useColor("hsl(210, 80%, 50%)");
const gradientColors = useMemo(() => {
const cfg = getChannelConfig("hsl", "l");
if (!cfg) return ["black", "white"];
const steps = 7;
const colors: string[] = [];
const cMin = cfg.culoriMin ?? cfg.min;
const cMax = cfg.culoriMax ?? cfg.max;
for (let i = 0; i < steps; i++) {
const t = i / (steps - 1);
const val = cMin + t * (cMax - cMin);
colors.push(color.set({ mode: "hsl", l: val })?.toString() ?? "black");
}
return colors;
}, [color]);
return (
<ColorSlider.Root
value={color}
onValueChange={setColor}
colorSpace="hsl"
channel="l"
className="w-full"
>
<ColorSlider.Control>
<ColorSlider.Track className="relative h-5 overflow-hidden rounded-xl">
<ColorSlider.Gradient
className="absolute inset-0 rounded-xl"
colors={gradientColors}
/>
<ColorSlider.Thumb
className="
block size-5 rounded-full border-[2.5px] border-white bg-white
shadow-[0_0_0_1px_rgba(0,0,0,0.3),0_2px_4px_rgba(0,0,0,0.3)]
focus-visible:shadow-[0_0_0_1px_rgba(0,0,0,0.3),0_0_0_3px_rgba(66,153,225,0.6)]
"
aria-label="Lightness"
/>
</ColorSlider.Track>
</ColorSlider.Control>
</ColorSlider.Root>
);
}Vertical
Source code
tsx
import "internationalized-color/css";
import { ColorSlider, useColor } from "@urcolor/react";
export default function ColorSliderVertical() {
const { color, setColor } = useColor("hsl(210, 80%, 50%)");
return (
<ColorSlider.Root
value={color}
onValueChange={setColor}
colorSpace="hsl"
channel="h"
orientation="vertical"
className="h-[150px] w-auto"
>
<ColorSlider.Control>
<ColorSlider.Track className="relative h-full w-5 overflow-hidden rounded-xl">
<ColorSlider.Gradient
className="absolute inset-0 rounded-xl"
colors={["red", "yellow", "lime", "cyan", "blue", "magenta", "red"]}
angle={180}
/>
<ColorSlider.Thumb
className="
block size-5 rounded-full border-[2.5px] border-white bg-white
shadow-[0_0_0_1px_rgba(0,0,0,0.3),0_2px_4px_rgba(0,0,0,0.3)]
focus-visible:shadow-[0_0_0_1px_rgba(0,0,0,0.3),0_0_0_3px_rgba(66,153,225,0.6)]
"
aria-label="Hue (vertical)"
/>
</ColorSlider.Track>
</ColorSlider.Control>
</ColorSlider.Root>
);
}API Reference
ColorSlider.Root
The root container that manages slider state and color channel binding.
| Prop | Type | Default | Description |
|---|---|---|---|
value | Color | string | null | — | Controlled color value. |
defaultValue | Color | string | null | — | Initial color when uncontrolled. |
colorSpace | string | 'hsl' | Color space mode (e.g. 'hsl', 'oklch'). |
channel | string | 'h' | Channel to control (e.g. 'h', 's', 'l'). |
disabled | boolean | false | Disables interaction. |
dir | 'ltr' | 'rtl' | — | Reading direction. |
inverted | boolean | false | Visually invert the slider. |
orientation | 'horizontal' | 'vertical' | 'horizontal' | Slider orientation. |
onValueChange | (color: Color) => void | — | Called when color changes. |
onValueCommit | (color: Color) => void | — | Called when interaction ends. |
ColorSlider.Control
The clickable, interactive area that handles pointer events for dragging.
ColorSlider.Track
The track area that contains the gradient and thumb.
ColorSlider.Gradient
Renders a gradient canvas background for the slider track.
| Prop | Type | Default | Description |
|---|---|---|---|
colors | string[] | — | Array of color stops (minimum 2). |
angle | number | — | Gradient angle in degrees. |
interpolationSpace | string | — | Color space for perceptual interpolation (e.g. 'oklch'). |
channelOverrides | Record<string, number> | false | { alpha: 1 } | Lock specific channels to fixed values in the gradient. Set to false to reflect all channels from current color including alpha. |
ColorSlider.Checkerboard
Renders a checkerboard pattern behind the gradient to visualize alpha transparency.
ColorSlider.Thumb
The draggable thumb element.
ColorSlider.Range
The filled range portion of the track.
Accessibility
ColorSlider provides a standard slider interface with robust screen reader support.
ARIA Labels
| Attribute | Description |
|---|---|
aria-label | Labels the slider with the controlled channel name. |
role="slider" | Applied to the thumb element for screen reader recognition. |
aria-valuemin / aria-valuemax | Defines the channel's value range. |
aria-valuenow | Current value of the channel. |
aria-orientation | Reflects horizontal or vertical orientation. |
Keyboard Navigation
| Key | Action |
|---|---|
| Arrow Left / Arrow Down | Decrease by one step |
| Arrow Right / Arrow Up | Increase by one step |
| Shift + Arrow | Move by 10 steps |
| Home | Move to minimum |
| End | Move to maximum |
| Page Up | Increase by large step |
| Page Down | Decrease by large step |