Levels

4 parts

A headless, accessible Photoshop-style levels control. The root owns a LevelsValue — input black/white clipping points (0..255), a gamma midtone factor (0.1..9.99), and an outputBlack/outputWhite range (0..255) — controlled via v-model or uncontrolled via defaultValue. It is a constrained multi-thumb slider: black is kept strictly below white (by minStepsBetweenHandles * step) and the output handles keep their order, with a value pushed past its neighbour pinning rather than swapping. The root handles pointer drags and keyboard for the thumbs, exposes the 0..255 output LUT via getOutputCurve, and can derive auto black/white from a histogram via autoLevels. Provides context to LevelsTrack, LevelsThumb, and LevelsHandleValue. Pair it with HistogramRoot for a full levels editor.

Demo

Loading demo…

Anatomy

Import the parts and compose them. Each part forwards attributes to its underlying element.

vue
import {
  LevelsRoot,
  LevelsTrack,
  LevelsThumb,
  LevelsHandleValue,
} from '@robonen/primitives/canvas/levels';

<LevelsRoot>
  <LevelsTrack />
  <LevelsThumb />
  <LevelsHandleValue />
</LevelsRoot>

API Reference

LevelsRoot

Root

A headless, accessible Photoshop-style levels control. The root owns a `LevelsValue` — input `black`/`white` clipping points (`0..255`), a `gamma` midtone factor (`0.1..9.99`), and an `outputBlack`/`outputWhite` range (`0..255`) — controlled via `v-model` or uncontrolled via `defaultValue`. It is a constrained multi-thumb slider: `black` is kept strictly below `white` (by `minStepsBetweenHandles * step`) and the output handles keep their order, with a value pushed past its neighbour pinning rather than swapping. The root handles pointer drags and keyboard for the thumbs, exposes the `0..255` output LUT via `getOutputCurve`, and can derive auto black/white from a histogram via `autoLevels`. Provides context to `LevelsTrack`, `LevelsThumb`, and `LevelsHandleValue`. Pair it with `HistogramRoot` for a full levels editor.

PropTypeDescription
defaultValue?LevelsValueUncontrolled initial value.
step?numberStep for the 0..255 handles (black/white/output).
gammaStep?numberStep for the gamma handle.
largeStep?numberLarge-step multiplier (Page keys / Shift+Arrow).
minStepsBetweenHandles?numberMinimum gap (in steps) the black handle keeps below white.
disabled?booleanDisable all interaction.
orientation?LevelsOrientationOrientation.
dir?LevelsDirectionWriting direction (inherited from ConfigProvider when omitted).
inverted?booleanInvert the direction of interaction.
modelValue?LevelsValue | nullTwo-way bound value (v-model).
Emits
EventPayload
update:modelValue[value: LevelsValue | null]

LevelsTrack

Track

The `0..255` rail the levels thumbs travel along, rendered inside `LevelsRoot`. It registers itself as the geometry reference for the root's pointer math. Use it as the container for the histogram backdrop and the `LevelsThumb`s. (Drag is initiated on each thumb, so pressing a thumb does not also start a track-position drag.)

No props or events — renders its element and forwards attributes.

LevelsThumb

Thumb

A draggable levels handle rendered as `role="slider"`, one per `kind` (`'black'` / `'gamma'` / `'white'` / `'outputBlack'` / `'outputWhite'`), placed inside `LevelsTrack`. It positions itself along the `0..255` track, exposes full per-kind ARIA (`aria-label`, `aria-valuemin/max/now`), and handles pointer drags and the keyboard (Arrow / Page / Home / End, neighbour-clamped). The gamma thumb is special: it sits at its effective midtone input level, its `aria-valuenow` carries the gamma FACTOR (`0.1..9.99`), and its `aria-valuetext` states the effective level (e.g. "Gamma 1.00, midtone at 128"). Keyboard nudges use `step` for the `0..255` handles and `gammaStep` for gamma.

PropTypeDescription
kindLevelsHandleKindWhich handle this thumb controls.

LevelsHandleValue

HandleValue

A visible, editable numeric entry box bound to one levels `kind` — the precise and screen-reader-friendly path for entering a handle value. It composes the `NumberField` family (`NumberFieldRoot` / `NumberFieldInput` / `NumberFieldIncrement` / `NumberFieldDecrement`) wired to the enclosing `LevelsRoot`: typing or stepping writes back through the same neighbour-clamped setter the thumb uses, so input black can never cross input white. The field's `min`/`max`/`step` track the handle's current legal range (gamma uses `gammaStep` and the `0.1..9.99` bounds). The default slot exposes the `NumberField` parts for full layout control; omit it for a stepper-free input.

PropTypeDescription
kindLevelsHandleKindWhich handle this entry box edits.