Color Box
A color editor on the shared field chrome: the field shows a live swatch and the committed CSS color string; the popup composes a saturation/brightness surface, hue/alpha sliders, hex + channel inputs and an optional swatch palette. No WAI-ARIA APG color-picker pattern exists, so the popup is built from primitives — a role="dialog" that takes real DOM focus, APG sliders with mandatory aria-valuetext, a 2-axis surface with aria-roledescription, and a role="grid" palette.
For a plain form field with no format control, palette or alpha, the native <input type="color"> is smaller and fully accessible out of the box — reach for this editor when you need the string format contract, typed CSS color input, swatch governance or the draft/OK commit flow.
Getting started
The committed value is a CSS color string — bind it straight to styles. Opening moves DOM focus onto the gradient surface; Escape restores it to the input. In Chromium the panel also offers an eyedropper (the EyeDropper API — progressive enhancement, no polyfill). valueCommitted reports every change with previousValue.
Value: #3aa0ff
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { OgeColorBox } from '@oge-ui/inputs';
@Component({
selector: 'demo-root',
imports: [OgeColorBox],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<!-- A dropdown color editor: the field shows a swatch + the committed
string; the popup is a role="dialog" that takes real DOM focus (no APG
color-picker pattern exists — it is composed from dialog, sliders and
grid primitives). ArrowDown opens; Escape restores focus to the input. -->
<oge-color-box label="Brand color" [(value)]="brand" [showClearButton]="true" />
`,
})
export class Demo {
protected readonly brand = signal<string | null>('#3aa0ff');
}Formats and alpha
format: 'hex' | 'rgb' | 'rgba' | 'hsl' fixes the committed shape (hex default — the DevExtreme choice). editAlphaChannel adds the alpha slider and input; translucent colors widen the output to carry alpha, opaque ones stay compact. Without it alpha is coerced to 1 on commit.
Overlay: rgba(58, 160, 255, 0.5) — Accent: hsl(210, 100%, 61%)
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { OgeColorBox } from '@oge-ui/inputs';
@Component({
selector: 'demo-root',
imports: [OgeColorBox],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<!-- format controls the committed string shape ('hex' default —
the DevExtreme choice; Kendo defaults to rgba). editAlphaChannel adds the
alpha slider + input; a translucent color then WIDENS the output
(#rrggbbaa / rgba() / hsla()), while opaque colors stay compact. Without
it, alpha is coerced to 1 on commit — rgba() text still parses. -->
<oge-color-box label="Overlay" format="rgba" [editAlphaChannel]="true" [(value)]="overlay" />
<oge-color-box label="Accent (hsl)" format="hsl" [(value)]="accent" />
`,
})
export class Demo {
protected readonly overlay = signal<string | null>('rgba(58, 160, 255, 0.5)');
protected readonly accent = signal<string | null>('hsl(210, 100%, 61%)');
}Palette view
view: 'gradient' | 'palette' | 'both'. The palette is an APG grid — roving tabindex, arrow/Home/End/Ctrl+Home navigation, Enter/Space picks and closes. The selected cell's checkmark picks black or white by WCAG contrast against the swatch.
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { OgeColorBox } from '@oge-ui/inputs';
@Component({
selector: 'demo-root',
imports: [OgeColorBox],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<!-- view: 'gradient' | 'palette' | 'both'. The palette is an APG
grid: roving tabindex, arrows move by cell/row, Home/End row edges,
Ctrl+Home/End grid corners, Enter/Space picks (and closes — a swatch is a
final choice). Cells announce their color string; the selected checkmark
picks black or white by WCAG contrast. -->
<oge-color-box
label="Tag color"
view="palette"
[palette]="swatches"
[paletteColumns]="5"
[(value)]="tag"
/>
`,
})
export class Demo {
protected readonly tag = signal<string | null>('#16a34a');
protected readonly swatches: readonly string[] = [
'#dc2626', '#ea580c', '#d97706', '#16a34a', '#0d9488',
'#2563eb', '#7c3aed', '#c026d3', '#475569', '#111827',
];
}Apply with buttons
applyValueMode: 'useButtons' collects panel interactions in a draft and commits only on OK; Cancel, Escape or an outside click discards — the date box's exact contract, with Kendo's committed | draft preview pair in the footer. The default 'instantly' commits live while dragging ([debounce] throttles the stream).
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { OgeColorBox } from '@oge-ui/inputs';
@Component({
selector: 'demo-root',
imports: [OgeColorBox],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<!-- applyValueMode: 'instantly' (default) commits every panel
interaction live — dragging streams through valueCommitted and [debounce]
throttles it. 'useButtons' collects interactions in a draft, shows a
committed | draft preview pair in the footer and commits only on OK;
Cancel (or Escape / outside click) discards. The date box's exact
contract, applied to color. -->
<oge-color-box
label="Theme color"
view="both"
applyValueMode="useButtons"
[(value)]="theme"
/>
`,
})
export class Demo {
protected readonly theme = signal<string | null>('#7c3aed');
}Typed colors
Typed text parses any CSS color — hex in all four lengths, rgb()/rgba() in comma and space/slash syntax, hsl(), the named colors, transparent. Commits normalize to format; unparseable text shows the invalid state while typing and reverts on blur. acceptCustomValue=false makes the text read-only.
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { OgeColorBox } from '@oge-ui/inputs';
@Component({
selector: 'demo-root',
imports: [OgeColorBox],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<!-- Typed text parses ANY CSS color — #rgb/#rrggbb/#rrggbbaa,
rgb()/rgba() (comma and space/slash syntax), hsl(), the 148 named colors,
'transparent'. Commits normalize to format; unparseable text shows the
invalid state while typing and REVERTS on blur — a wrong color is never
committed. acceptCustomValue=false makes the text read-only. -->
<oge-color-box label="Any CSS color" [(value)]="typed" />
<oge-color-box label="Picker only" [acceptCustomValue]="false" [(value)]="typed" />
`,
})
export class Demo {
protected readonly typed = signal<string | null>('rebeccapurple');
}Inside a form
A chrome'd editor: editorType: 'colorBox' with colorFormat, editAlphaChannel, view and palette under editorOptions. Works in all three form bindings.
Model: #3aa0ff
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { OgeForm } from '@oge-ui/forms';
import type { OgeFormItemData } from '@oge-ui/forms';
@Component({
selector: 'demo-root',
imports: [OgeForm],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<oge-form [(formData)]="branding" [items]="items" />
`,
})
export class Demo {
protected readonly branding = signal({ primary: '#3aa0ff' });
protected readonly items: OgeFormItemData[] = [
{
field: 'primary',
label: 'Primary color',
editorType: 'colorBox',
editorOptions: { colorFormat: 'hex', showClearButton: true },
},
];
}