Skip to content

ColorArea

A rectangular 2D area component for adjusting two color channels mapped to the horizontal and vertical axes.

Preview

Source code
tsx
import { ColorArea, useColor } from "@urcolor/react";

export default function ColorAreaHSL() {
  const { color, setColor } = useColor("hsl(210, 80%, 50%)");

  return (
    <ColorArea.Root
      value={color}
      onValueChange={setColor}
      colorSpace="hsl"
      xChannel="h"
      yChannel="s"
      className="
        relative block h-[200px] w-full cursor-crosshair touch-none
        overflow-clip rounded-lg
      "
      aria-label="HSL color area"
    >
      <ColorArea.Gradient className="absolute inset-0" />
      <ColorArea.Thumb
        className="
          absolute size-5 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)]
        "
      />
    </ColorArea.Root>
  );
}

Anatomy

tsx
<ColorArea.Root>
  <ColorArea.Gradient />
  <ColorArea.Thumb />
</ColorArea.Root>

The root is the interaction surface: pointer capture, the keyboard handler and the box that pointer coordinates are measured against all live on it, so the gradient and the thumb sit directly inside it.

Examples

HSL

HSL color area with Hue on X and Saturation on Y.

Source code
tsx
import { ColorArea, useColor } from "@urcolor/react";

export default function ColorAreaHSL() {
  const { color, setColor } = useColor("hsl(210, 80%, 50%)");

  return (
    <ColorArea.Root
      value={color}
      onValueChange={setColor}
      colorSpace="hsl"
      xChannel="h"
      yChannel="s"
      className="
        relative block h-[200px] w-full cursor-crosshair touch-none
        overflow-clip rounded-lg
      "
      aria-label="HSL color area"
    >
      <ColorArea.Gradient className="absolute inset-0" />
      <ColorArea.Thumb
        className="
          absolute size-5 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)]
        "
      />
    </ColorArea.Root>
  );
}

OKLCh

OKLCh color area with Chroma on X and Lightness on Y.

Source code
tsx
import { ColorArea, useColor } from "@urcolor/react";

export default function ColorAreaOKLCh() {
  const { color, setColor } = useColor("hsl(210, 80%, 50%)");

  return (
    <ColorArea.Root
      value={color}
      onValueChange={setColor}
      colorSpace="oklch"
      xChannel="c"
      yChannel="l"
      className="
        relative block h-[200px] w-full cursor-crosshair touch-none
        overflow-clip rounded-lg
      "
      aria-label="OKLCh color area"
    >
      <ColorArea.Gradient className="absolute inset-0" />
      <ColorArea.Thumb
        className="
          absolute size-5
          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)]
        "
      />
    </ColorArea.Root>
  );
}

API Reference

Every part is also exported unnamespaced, ColorAreaRoot, ColorAreaGradient, ColorAreaThumb, alongside the ColorArea.* namespace. The root's context is readable with useColorAreaContext().

ColorArea.Root

The root container that manages area state and color channel binding. Renders a <div> and owns the pointer and keyboard interaction for the whole family.

PropTypeDefaultDescription
valueColor | string | nullControlled color value.
defaultValueColor | string'hsl(0, 100%, 50%)'Initial color when uncontrolled.
colorSpaceSpaceId'hsl'Color space (e.g. 'hsl', 'oklch').
xChannelstringAutoChannel mapped to the X axis, or 'alpha'. Defaults to the color space's first channel.
yChannelstringAutoChannel mapped to the Y axis, or 'alpha'. Defaults to the color space's second channel.
disabledbooleanfalseDisables interaction.
dir'ltr' | 'rtl''ltr'Reading direction. 'rtl' mirrors the X axis.
xInvertedbooleanfalseRuns the X axis opposite to its natural direction.
yInvertedbooleanfalseRuns the Y axis opposite to its natural direction.
thumbAlignment'contain' | 'overflow''overflow'Whether the thumb straddles the edge ('overflow') or is pulled fully inside it.
onValueChange(color: Color) => voidCalled on every value change, including mid-drag.
onValueCommit(color: Color) => voidCalled when a change-producing interaction ends.
classNamestringClass applied to the rendered element.
styleReact.CSSPropertiesInline styles merged under the thumb-transform custom property.
childrenReact.ReactNodeThe area's parts.

TIP

WARNING

ColorAreaRootProps does not extend ComponentPropsWithoutRef<"div">. Only the props above are forwarded, arbitrary DOM attributes passed to the root are dropped, so put an aria-label on ColorArea.Thumb rather than on the root.

The root publishes --reka-slider-area-thumb-transform in its own style, which is the centring transform the thumb consumes. dir and xInverted each mirror the X axis, so setting both cancels out.

ColorArea.Gradient

Renders the area's two-dimensional color surface, sampled from the root's color space and channel configuration. Renders a <span> wrapper with an inner <canvas>. The transparency checkerboard is the wrapper's own CSS background, which the canvas bitmap composites over, so no separate part is needed for it.

Extends ComponentPropsWithoutRef<"span">.

PropTypeDefaultDescription
topLeftstringExplicit top-left corner color. Supplying any corner switches the component to corner mode and ignores the channel sampling.
topRightstringExplicit top-right corner color.
bottomLeftstringExplicit bottom-left corner color.
bottomRightstringExplicit bottom-right corner color.
interpolationSpaceSpaceIdInterpolate the corner surface in this space for perceptual accuracy. Switches the corner path from WebGL to a CPU sampler.
channelOverridesRecord<string, number> | false{ alpha: 1 }Lock specific channels to fixed values in the gradient. Set to false to reflect all channels from the current color, including alpha.
classNamestringClass applied to the wrapper element.
styleReact.CSSPropertiesInline styles merged over the wrapper's checkerboard background.

ColorArea.Checkerboard deprecated

Deprecated

ColorArea.Gradient now paints the checkerboard itself, so this component is no longer needed and is kept only for backwards compatibility. It emits a one-time console warning in development. To render a checkerboard elsewhere, apply a CSS repeating-conic-gradient background to your own element.

Renders a checkerboard pattern behind the gradient to visualize alpha transparency. Renders a <div> and extends ComponentPropsWithoutRef<"div">.

ColorArea.Thumb

The single combined handle, and the area's only focusable element. One thumb drives both axes: it renders role="slider", takes tabIndex={0} unless the root is disabled, and is positioned from the X and Y channel values. There is no separate thumb per axis.

The thumb is only a focus target and an ARIA surface; every value change is owned by the root, whose onKeyDown handler sees the events that bubble up from here.

Extends ComponentPropsWithoutRef<"span">.

PropTypeDefaultDescription
indexnumber0Which entry of the root's value list this thumb reads. The area drives a single thumb, so the default is the only useful value.
aria-labelstringLabels the handle. Not generated. Pass one, or the control is announced without a name.
classNamestringClass applied to the rendered element.
styleReact.CSSPropertiesInline styles merged over the computed absolute position.

A mirrored axis is anchored from the opposite edge, so the thumb sets right/bottom instead of left/top and the percentage stays positive.

Data Attributes

AttributePartPresent when
data-slider-area-implRootAlways.
data-disabledRoot, Gradient, ThumbThe root is disabled.

CSS Variables

The transparency grid reads three custom properties and no component writes them, so a rule anywhere above the element wins:

VariableDefaultDescription
--urcolor-checkerboard-darkrgb(230, 230, 230)The darker of the two checks.
--urcolor-checkerboard-lightwhiteThe lighter of the two checks.
--urcolor-checkerboard-size16pxThe tile size, applied to both axes.

The grid is a single background shorthand, so an invalid value invalidates the whole declaration rather than its own layer. Keep overrides to a <color> and a <length>.

Accessibility

ColorArea exposes a single focusable thumb that drives both the X and the Y channel. Keyboard events are handled on the root, which sees them bubble up from the focused thumb.

ARIA Labels

AttributeDescription
role="slider"Applied to ColorArea.Thumb, with aria-roledescription="2D slider".
aria-valuemin / aria-valuemaxThe X channel's range.
aria-valuenowThe current X channel value. Only one number can be carried here, so the X axis owns it.
aria-disabledAlways present on the root; "true" when disabled is set.

WARNING

Unlike the Vue, Svelte and Angular packages, the React thumb does not generate an aria-label or an aria-valuetext. Pass your own aria-label to ColorArea.Thumb naming both channels, and an aria-valuetext if the raw X-axis number alone would be misleading.

Keyboard Navigation

KeyAction
Arrow Right / Arrow LeftMove one step along the X axis
Arrow Down / Arrow UpMove one step along the Y axis
Shift + ArrowMove by 10 steps
Home / EndJump to the left / right edge of the X axis
Page Up / Page DownJump to the top / bottom edge of the Y axis

Keys address the visual axes: xInverted, yInverted and RTL flip the direction of travel so the thumb still moves the way the key points. Each key press that changes the value fires onValueCommit as well as onValueChange.