Skip to content

ColorSlider

A 1D slider component for adjusting a single color channel, with a gradient track that reflects the current color.

Preview

Source code
vue
<script setup lang="ts">
import "internationalized-color/css";
import {
  ColorSliderRoot,
  ColorSliderTrack,
  ColorSliderGradient,
  ColorSliderThumb,
  useColor,
} from "@urcolor/vue";

const { color } = useColor("hsl(210, 80%, 50%)");
</script>

<template>
  <ColorSliderRoot
    v-model="color"
    color-space="hsl"
    channel="h"
    as="div"
    class="w-full"
  >
    <ColorSliderTrack
      as="div"
      class="relative h-5 overflow-hidden rounded-xl"
    >
      <ColorSliderGradient
        as="div"
        class="absolute inset-0 rounded-xl"
        :colors="['red', 'yellow', 'lime', 'cyan', 'blue', 'magenta', 'red']"
      />
      <ColorSliderThumb
        class="
          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"
      />
    </ColorSliderTrack>
  </ColorSliderRoot>
</template>

Anatomy

vue
<template>
  <ColorSliderRoot>
    <ColorSliderTrack>
      <ColorSliderCheckerboard />
      <ColorSliderGradient />
      <ColorSliderThumb />
    </ColorSliderTrack>
  </ColorSliderRoot>
</template>

Examples

Hue

Source code
vue
<script setup lang="ts">
import "internationalized-color/css";
import {
  ColorSliderRoot,
  ColorSliderTrack,
  ColorSliderGradient,
  ColorSliderThumb,
  useColor,
} from "@urcolor/vue";

const { color } = useColor("hsl(210, 80%, 50%)");
</script>

<template>
  <ColorSliderRoot
    v-model="color"
    color-space="hsl"
    channel="h"
    as="div"
    class="w-full"
  >
    <ColorSliderTrack
      as="div"
      class="relative h-5 overflow-hidden rounded-xl"
    >
      <ColorSliderGradient
        as="div"
        class="absolute inset-0 rounded-xl"
        :colors="['red', 'yellow', 'lime', 'cyan', 'blue', 'magenta', 'red']"
      />
      <ColorSliderThumb
        class="
          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"
      />
    </ColorSliderTrack>
  </ColorSliderRoot>
</template>

Saturation

Source code
vue
<script setup lang="ts">
import { computed } from "vue";
import "internationalized-color/css";
import { getChannelConfig } from "@urcolor/core";
import {
  ColorSliderRoot,
  ColorSliderTrack,
  ColorSliderGradient,
  ColorSliderThumb,
  useColor,
} from "@urcolor/vue";

const { color } = useColor("hsl(210, 80%, 50%)");

const gradientColors = computed(() => {
  if (!color.value) return ["gray", "blue"];
  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.value.set({ mode: "hsl", s: val })?.toString() ?? "black");
  }
  return colors;
});
</script>

<template>
  <ColorSliderRoot
    v-model="color"
    color-space="hsl"
    channel="s"
    as="div"
    class="w-full"
  >
    <ColorSliderTrack
      as="div"
      class="relative h-5 overflow-hidden rounded-xl"
    >
      <ColorSliderGradient
        as="div"
        class="absolute inset-0 rounded-xl"
        :colors="gradientColors"
      />
      <ColorSliderThumb
        class="
          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"
      />
    </ColorSliderTrack>
  </ColorSliderRoot>
</template>

Lightness

Source code
vue
<script setup lang="ts">
import { computed } from "vue";
import "internationalized-color/css";
import { getChannelConfig } from "@urcolor/core";
import {
  ColorSliderRoot,
  ColorSliderTrack,
  ColorSliderGradient,
  ColorSliderThumb,
  useColor,
} from "@urcolor/vue";

const { color } = useColor("hsl(210, 80%, 50%)");

const gradientColors = computed(() => {
  if (!color.value) return ["black", "white"];
  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.value.set({ mode: "hsl", l: val })?.toString() ?? "black");
  }
  return colors;
});
</script>

<template>
  <ColorSliderRoot
    v-model="color"
    color-space="hsl"
    channel="l"
    as="div"
    class="w-full"
  >
    <ColorSliderTrack
      as="div"
      class="relative h-5 overflow-hidden rounded-xl"
    >
      <ColorSliderGradient
        as="div"
        class="absolute inset-0 rounded-xl"
        :colors="gradientColors"
      />
      <ColorSliderThumb
        class="
          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"
      />
    </ColorSliderTrack>
  </ColorSliderRoot>
</template>

Vertical

Source code
vue
<script setup lang="ts">
import "internationalized-color/css";
import {
  ColorSliderRoot,
  ColorSliderTrack,
  ColorSliderGradient,
  ColorSliderThumb,
  useColor,
} from "@urcolor/vue";

const { color } = useColor("hsl(210, 80%, 50%)");
</script>

<template>
  <ColorSliderRoot
    v-model="color"
    color-space="hsl"
    channel="h"
    orientation="vertical"
    as="div"
    class="h-[150px] w-auto"
  >
    <ColorSliderTrack
      as="div"
      class="relative h-full w-5 overflow-hidden rounded-xl"
    >
      <ColorSliderGradient
        as="div"
        class="absolute inset-0 rounded-xl"
        :colors="['red', 'yellow', 'lime', 'cyan', 'blue', 'magenta', 'red']"
        :vertical="true"
      />
      <ColorSliderThumb
        class="
          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)"
      />
    </ColorSliderTrack>
  </ColorSliderRoot>
</template>

With Alpha

Pass :channel-overrides="false" on ColorSliderGradient to reflect the color's alpha as opacity on the gradient. Add ColorSliderCheckerboard to show a checkerboard behind transparent areas.

vue
<template>
  <ColorSliderRoot
    :model-value="color"
    color-space="hsl"
    channel="h"
    @update:model-value="onColorUpdate"
  >
    <ColorSliderTrack>
      <ColorSliderCheckerboard />
      <ColorSliderGradient
        :colors="['red', 'yellow', 'lime', 'cyan', 'blue', 'magenta', 'red']"
        :channel-overrides="false"
      />
      <ColorSliderThumb />
    </ColorSliderTrack>
  </ColorSliderRoot>
</template>

Alpha Channel

Set channel="alpha" to create an opacity slider. The gradient automatically renders with transparency.

vue
<template>
  <ColorSliderRoot
    :model-value="color"
    color-space="hsl"
    channel="alpha"
    @update:model-value="onColorUpdate"
  >
    <ColorSliderTrack>
      <ColorSliderCheckerboard />
      <ColorSliderGradient :colors="['hsla(210, 80%, 50%, 0)', 'hsl(210, 80%, 50%)']" />
      <ColorSliderThumb />
    </ColorSliderTrack>
  </ColorSliderRoot>
</template>

API Reference

ColorSliderRoot

The root container that manages slider state and color channel binding.

PropTypeDefaultDescription
modelValueColor | string | nullControlled color value (v-model).
colorSpacestring'hsl'Color space mode (e.g. 'hsl', 'oklch').
channelstring'h'Channel to control (e.g. 'h', 's', 'l').
disabledbooleanfalseDisables interaction.
dir'ltr' | 'rtl'Reading direction.
invertedbooleanfalseVisually invert the slider.
orientation'horizontal' | 'vertical''horizontal'Slider orientation.
EventPayloadDescription
-----------------------------
update:modelValueColor | undefinedEmitted when color changes.
valueCommitColorEmitted when interaction ends.

ColorSliderTrack

The track area that contains the gradient and thumb. Extends Reka UI's SliderTrackProps.

ColorSliderGradient

Renders a gradient canvas background for the slider track.

PropTypeDefaultDescription
colorsstring[]Array of color stops (minimum 2).
verticalbooleanfalseRender gradient top-to-bottom instead of left-to-right.
interpolationSpacestringColor space for perceptual interpolation (e.g. 'oklch').
channelOverridesRecord<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. E.g. { s: 1, v: 1, alpha: 1 } for an immutable hue gradient in HSV.

ColorSliderCheckerboard

Renders a checkerboard pattern behind the gradient to visualize alpha transparency. Place it inside ColorSliderTrack before ColorSliderGradient.

ColorSliderThumb

The draggable thumb element. Extends Reka UI's SliderThumbProps.

ColorSliderRange

The filled range portion of the track. Extends Reka UI's SliderRangeProps.

Accessibility

ColorSlider provides a standard slider interface built on top of Reka UI's slider primitives, ensuring robust screen reader support.

ARIA Labels

AttributeDescription
aria-labelLabels the slider with the controlled channel name.
role="slider"Applied to the thumb element for screen reader recognition.
aria-valuemin / aria-valuemaxDefines the channel's value range.
aria-valuenowCurrent value of the channel.
aria-orientationReflects horizontal or vertical orientation.

Keyboard Navigation

KeyAction
Arrow Left / Arrow DownDecrease by one step
Arrow Right / Arrow UpIncrease by one step
Shift + ArrowMove by 10 steps
HomeMove to minimum
EndMove to maximum
Page UpIncrease by large step
Page DownDecrease by large step