Skip to content

Color swatch

ColorSwatch is a focusable grid of color cells the user picks from with the arrow keys or the mouse. Each color is a 3-column block; the cell whose color equals the bound value is marked with a glyph. Every arrow key, click, and drag updates value immediately (arrow navigation wraps around the palette ends) and fires the optional onInput; Enter, Space, or a mouse-up over a cell fires the optional onChange — the discrete commit a hosting ColorPicker uses to close its dropdown. The swatch draws only its cells; a hosting popup or window supplies any border.

Usage

ts
import { ColorSwatch, signal } from '@jsvision/ui';
import type { Color } from '@jsvision/core';

const value = signal<Color>('cyan');
const swatch = new ColorSwatch({
  value,
  columns: 4,
  onInput: (c) => preview(c), // live: every arrow / click / drag
  onChange: (c) => commit(c), // commit: Enter / Space / mouse-up
});
swatch.setLayout({ position: 'absolute', rect: { x: 0, y: 0, width: 12, height: 4 } });

Live example

Compare live palette movement with an explicit committed color using keyboard and mouse.

Props

new ColorSwatch(options).

The public surface is ColorSwatch, ColorSwatchOptions, the core Color type, and the default ANSI16_ORDER palette.

PropTypeDefaultDescription
valueSignal<Color>Two-way selected color.
colorsreadonly Color[]ANSI16_ORDERPalette to display (the DOS-16 colors by default).
columnsnumber4Columns per row.
onInput(c: Color) => voidFired on every live change (arrow / click / drag).
onChange(c: Color) => voidFired on the discrete commit (Enter / Space / mouse-up).
nameFor(c: Color) => stringColor-name accessor a hosting ColorPicker uses to caption its chip.

Live selection and commit

InputResult
← / → / ↑ / ↓Move the selection (wraps around the palette ends).
Enter / SpaceCommit the current cell (fires onChange).
Click / dragSelect live (fires onInput); dragging outside reverts.
Mouse-up over a cellCommit that cell (fires onChange).

If value is an off-palette color (e.g. a custom hex from a hosting picker), no marker shows, but arrow navigation still works from an internal cursor. On a near-black cell the marker is drawn in a contrasting colour so it stays visible.

Palette geometry

Every palette entry occupies a three-cell block. columns controls wrapping and measure() reports the resulting width and row count, including a safe one-row minimum for an empty palette. An off-palette custom value keeps the grid usable: no marker is drawn, but the internal cursor provides a stable starting point for the next arrow movement.

Sizing & layout

Its measure() sizes it to columns × rows of 3-wide cells. The swatch has no frame — host it in a popup or window that supplies the border.

Best practices

  • Split live vs. committed. Use onInput for a live preview (recolour as the cursor moves) and onChange for the expensive commit — a drag fires many onInputs and one onChange on release.
  • Default to the DOS-16 palette. ANSI16_ORDER is the classic set; pass a custom colors array only when you need a specific palette.

Theming

Each cell draws its own color as the foreground over a black background. The marker uses the colorMarker role (black on lightGray) — forced onto a near-black cell so it stays visible.