Skip to content

ColorTriangle

A triangular 2D area component for adjusting two (or three) color channels simultaneously.

Preview

#1980e6
Source code
vue
<script setup lang="ts">
import "internationalized-color/css";
import {
  ColorTriangleRoot,
  ColorTriangleGradient,
  ColorTriangleThumb,
  useColor,
} from "@urcolor/vue";

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

<template>
  <code>{{ hex }}</code>
  <ColorTriangleRoot
    v-model="color"
    color-space="hsv"
    channel-x="s"
    channel-y="v"
    class="relative block size-64"
  >
    <ColorTriangleGradient class="absolute inset-0 block" />
    <ColorTriangleThumb
      class="
        size-4 rounded-full border-2 border-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)]
      "
    />
  </ColorTriangleRoot>
</template>

Anatomy

vue
<template>
  <ColorTriangleRoot>
    <ColorTriangleCheckerboard />
    <ColorTriangleGradient />
    <ColorTriangleThumb>
      <ColorTriangleThumbX />
      <ColorTriangleThumbY />
    </ColorTriangleThumb>
  </ColorTriangleRoot>
</template>

Examples

HSV / Saturation x Value

HSV color triangle with Saturation and Value mapped to the triangle axes.

#1980e6
Source code
vue
<script setup lang="ts">
import "internationalized-color/css";
import {
  ColorTriangleRoot,
  ColorTriangleGradient,
  ColorTriangleThumb,
  useColor,
} from "@urcolor/vue";

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

<template>
  <code>{{ hex }}</code>
  <ColorTriangleRoot
    v-model="color"
    color-space="hsv"
    channel-x="s"
    channel-y="v"
    class="relative block size-64"
  >
    <ColorTriangleGradient class="absolute inset-0 block" />
    <ColorTriangleThumb
      class="
        size-4 rounded-full border-2 border-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)]
      "
    />
  </ColorTriangleRoot>
</template>

HSL / Saturation x Lightness

HSL color triangle with Saturation and Lightness mapped to the triangle axes.

#1980e6
Source code
vue
<script setup lang="ts">
import "internationalized-color/css";
import {
  ColorTriangleRoot,
  ColorTriangleGradient,
  ColorTriangleThumb,
  useColor,
} from "@urcolor/vue";

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

<template>
  <code>{{ hex }}</code>
  <ColorTriangleRoot
    v-model="color"
    color-space="hsl"
    channel-x="s"
    channel-y="l"
    class="relative block size-64"
  >
    <ColorTriangleGradient class="absolute inset-0 block" />
    <ColorTriangleThumb
      class="
        size-4 rounded-full border-2 border-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)]
      "
    />
  </ColorTriangleRoot>
</template>

Maxwell's RGB Triangle

Three-channel RGB triangle using barycentric coordinates.

#1980e6
Source code
vue
<script setup lang="ts">
import "internationalized-color/css";
import {
  ColorTriangleRoot,
  ColorTriangleGradient,
  ColorTriangleThumb,
  useColor,
} from "@urcolor/vue";

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

<template>
  <code>{{ hex }}</code>
  <ColorTriangleRoot
    v-model="color"
    color-space="rgb"
    channel-x="r"
    channel-y="g"
    channel-z="b"
    class="relative block size-64"
  >
    <ColorTriangleGradient class="absolute inset-0 block" />
    <ColorTriangleThumb
      class="
        size-4 rounded-full border-2 border-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)]
      "
    />
  </ColorTriangleRoot>
</template>

Three-Channel Mode

Pass channelZ to enable three-channel barycentric mode. Include ColorTriangleThumbZ inside the thumb.

vue
<script setup>
import {
  ColorTriangleRoot,
  ColorTriangleGradient,
  ColorTriangleThumb,
  ColorTriangleThumbX,
  ColorTriangleThumbY,
  ColorTriangleThumbZ,
} from "@urcolor/vue";
</script>

<template>
  <ColorTriangleRoot
    :model-value="color"
    color-space="srgb"
    channel-x="red"
    channel-y="green"
    channel-z="blue"
    @update:model-value="onColorUpdate"
  >
    <ColorTriangleGradient />
    <ColorTriangleThumb>
      <ColorTriangleThumbX />
      <ColorTriangleThumbY />
      <ColorTriangleThumbZ />
    </ColorTriangleThumb>
  </ColorTriangleRoot>
</template>

API Reference

ColorTriangleRoot

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

PropTypeDefaultDescription
modelValueColor | string | nullControlled color value (v-model).
defaultValueColor | string'hsl(0, 100%, 50%)'Initial color when uncontrolled.
colorSpacestring'hsv'Color space mode (e.g. 'hsv', 'hsl', 'srgb').
channelXstringAutoChannel for the X axis. Auto-derived from color space.
channelYstringAutoChannel for the Y axis. Auto-derived from color space.
channelZstringOptional third channel for barycentric three-channel mode.
rotationnumber0Triangle rotation in degrees.
orientation'vertical' | 'horizontal''vertical'Layout orientation.
disabledbooleanfalseDisables interaction.
dir'ltr' | 'rtl'Reading direction.
namestringHidden input name for form submission.
requiredbooleanfalseMarks as required for form submission.
EventPayloadDescription
update:modelValueColor | undefinedEmitted when color changes.
valueCommitColorEmitted when interaction ends.

ColorTriangleGradient

Renders a triangular gradient canvas. Automatically samples the gradient from the root's color space and channel configuration. Supports both 2-channel and 3-channel modes.

PropTypeDefaultDescription
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.

ColorTriangleCheckerboard

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

ColorTriangleThumb

Wrapper for the thumb indicator. Position is set automatically via CSS custom properties.

ColorTriangleThumbX / ColorTriangleThumbY

Individual axis thumb elements. Both are required inside ColorTriangleThumb for keyboard navigation.

ColorTriangleThumbZ

Optional third axis thumb for three-channel barycentric mode. Only include when channelZ is set on the root.

Accessibility

ColorTriangle provides a triangular 2D interface with independently focusable thumb elements for keyboard access. In three-channel mode, each channel has its own focusable thumb.

ARIA Labels

AttributeDescription
aria-labelLabels the root element with the color triangle's purpose.
role="slider"Applied to each thumb element for screen reader recognition.
aria-valuemin / aria-valuemaxDefines the range for each channel.
aria-valuenowCurrent value of the focused channel.

Keyboard Navigation

Two-Channel Mode

KeyAction
Arrow RightIncrease Y channel
Arrow LeftDecrease Y channel
Arrow UpIncrease X channel
Arrow DownDecrease X channel
Shift + ArrowMove by 10 steps
Home / Page UpJump to max
End / Page DownJump to min

Three-Channel Mode

KeyAction
Arrow Up / Arrow RightIncrease focused channel by 5%
Arrow Down / Arrow LeftDecrease focused channel by 5%
Shift + ArrowMove by 20%
Page UpIncrease by 20%
Page DownDecrease by 20%
HomeJump to focused channel vertex
EndJump to center (equal distribution)