<bmx-color-picker>
A colour field, or a colour panel in the page. Saturation and brightness on a square, hue and opacity on tracks, the value typed in hex, RGB, HSL or OKLCH, the page's brand colours and design tokens as swatches, recent colours, the screen's own eyedropper where the browser has one, and a live WCAG contrast check against a background - with a button that finds the nearest colour of the same hue that passes.
28 properties · 4 events · 5 methods · 21 parts
Example
Choose a colour.
Show markup
<div class="row" style="display: flex; gap: 24px; flex-wrap: wrap; align-items: flex-start">
<div style="display: grid; gap: 12px">
<bmx-color-picker id="ex-cp-brand" label="Brand colour" name="brand" value="#1a73e8" clearable description="Any CSS colour can be typed: a name, hex, rgb(), hsl() or oklch()."></bmx-color-picker>
<p class="note" id="ex-cp-out" role="status" style="margin: 0">Choose a colour.</p>
<div id="ex-cp-preview" style="display: grid; place-items: center; inline-size: 14rem; block-size: 4rem; border-radius: 12px; background: #1a73e8; color: #ffffff; font-weight: 600">Heading text</div>
</div>
<bmx-color-picker id="ex-cp-text" label="Heading text colour" mode="inline" alpha value="#8ab4f8" contrast-with="#1a73e8" description="Checked against the brand colour as text on it (WCAG AA, 4.5:1)."></bmx-color-picker>
</div>
<script type="module">
await customElements.whenDefined('bmx-color-picker');
const brand = document.getElementById('ex-cp-brand');
const text = document.getElementById('ex-cp-text');
const preview = document.getElementById('ex-cp-preview');
const out = document.getElementById('ex-cp-out');
const swatches = [
{ value: '#1a73e8', label: 'Blue 600', group: 'Brand' },
{ value: '#174ea6', label: 'Blue 800', group: 'Brand' },
{ value: '#e37400', label: 'Orange 700', group: 'Brand' },
'#202124', '#5f6368', '#dadce0', '#ffffff', '#d93025', '#f9ab00', '#188038', '#12b5cb', '#9334e6',
];
brand.swatches = swatches;
text.swatches = swatches;
brand.addEventListener('bmxChange', async e => {
const color = e.detail.color;
out.textContent = color ? `${color.hex} · ${color.rgb} · ${color.oklch} (${color.description})` : 'No colour.';
preview.style.background = e.detail.value || 'transparent';
text.contrastWith = e.detail.value || '#ffffff';
});
text.addEventListener('bmxInput', e => (preview.style.color = e.detail.value || 'inherit'));
preview.style.color = text.value;
// Recent colours are the page's to keep: here, for this visit only.
brand.addEventListener('bmxRecentChange', e => (text.recent = e.detail.recent));
</script>
<bmx-color-picker label="Brand colour" name="brand" value="#1a73e8"
swatches='["#1a73e8", "#188038", "#d93025", "var(--brand-accent)"]'
contrast-with="#ffffff"></bmx-color-picker>
value is always written in format (hex, rgb, hsl or oklch),
whatever was typed: "red", hsl(0 100% 50%) and #f00 all become
#ff0000 in hex. The form receives the same text. With alpha the value
carries opacity (#1a73e880, rgba(...)); without it every colour is
opaque.
A swatch may name a CSS custom property (var(--brand-500) or
--brand-500); it is read from the page, so the swatches follow the theme,
and bmxChange says which token was chosen.
Recent colours are the page's to keep: a colour is added when the panel
closes (or, inline, when focus leaves) on a new value, and
bmxRecentChange hands over the list. Nothing is stored.
Keyboard: on the square, arrows move saturation (left, right) and brightness (up, down), Shift ten at a time, Page Up and Page Down brightness by ten, Home and End saturation to its ends; the tracks are sliders. In the text box Enter takes what was typed, Alt+Down opens the panel, Escape puts the value back. Swatches are one tab stop, arrows between them. Escape closes the panel and returns focus to the field.
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
alpha |
alpha |
boolean |
false |
Allow opacity: an opacity track, and a value that carries it. |
clearable |
clearable |
boolean |
false |
Show a button that clears the colour. |
contrastLevel |
contrast-level |
BmxContrastLevel |
'AA' |
The WCAG level to meet. |
contrastRole |
contrast-role |
'foreground' | 'background' |
'foreground' |
Whether the chosen colour is the foreground against contrast-with, or the background behind it. |
contrastUse |
contrast-use |
BmxContrastUse |
'text' |
What the colour is used for, which sets the ratio it needs: text (4.5:1 / 7:1), large-text (3:1 / 4.5:1), graphics (3:1). |
contrastWith |
contrast-with |
string |
— | A colour to check contrast against: any colour, or a custom property. Leave empty for no check. |
description |
description |
string |
— | Help text below the field. |
disabled |
disabled |
boolean |
false |
Disable the picker. |
errorText |
error-text |
string |
— | An error supplied by the page. |
eyedropper |
eyedropper |
boolean |
true |
Offer the screen eyedropper, where the browser has one. |
format |
format |
BmxColorFormat |
'hex' |
How the value is written: hex, rgb, hsl or oklch. Mutable: the reader can switch it in the panel. |
formats |
formats |
BmxColorFormat[] | string |
['hex', 'rgb', 'hsl', 'oklch'] |
The formats offered in the panel, as an array, JSON or a comma-separated list. Empty hides the switcher. |
fullWidth |
full-width |
boolean |
false |
Stretch to the width of the container. |
hideInputs |
hide-inputs |
boolean |
false |
Leave out the format buttons and channel boxes. |
hideLabel |
hide-label |
boolean |
false |
Hide the label visually while keeping it as the accessible name. |
label |
label |
string |
— | The field's label. |
maxRecent |
max-recent |
number |
8 |
The longest the recent list grows. 0 keeps none and hides the list. |
mode |
mode |
'popup' | 'inline' |
'popup' |
popup: a field that opens a floating panel. inline: the panel drawn in place. |
name |
name |
string |
— | The field's name in the form it belongs to. |
placeholder |
placeholder |
string |
— | The text box's placeholder (popup mode). |
placement |
placement |
BmxPlacement |
'bottom-start' |
Where the floating panel opens against the field. |
readonly |
readonly |
boolean |
false |
Show the colour without allowing changes. |
recent |
recent |
string[] | string |
[] |
Recent colours, newest first. Mutable: a newly chosen colour is put first. |
required |
required |
boolean |
false |
A colour must be chosen. |
strings |
strings |
Partial<BmxColorPickerStrings> | string |
— | Words to show instead of the English ones, as an object or JSON. |
swatches |
swatches |
(BmxColorSwatch | string)[] | string |
[] |
Colours offered as swatches: strings or { value, label, group }, as an array, JSON or a comma-separated list. |
uppercase |
uppercase |
boolean |
false |
Hex digits in upper case. |
value |
value |
string |
'' |
The colour, written in format. Empty for none. Two-way. |
Events
| Event | Detail | Description |
|---|---|---|
bmxChange |
BmxColorChangeDetail |
Fired when a change is complete: a drag released, a key pressed, a swatch chosen, a value typed. |
bmxInput |
BmxColorChangeDetail |
Fired continuously while the colour changes - during a drag, on each step. |
bmxOpenChange |
boolean |
Fired when the floating panel opens or closes. |
bmxRecentChange |
BmxColorRecentDetail |
Fired when a colour is added to the recent list. |
Methods
| Method | Signature | Description |
|---|---|---|
checkContrastWith |
checkContrastWith(color?: string) => Promise<BmxContrastResult | null> |
The WCAG contrast of the current colour against another (default: contrast-with), or null. |
getColor |
getColor() => Promise<BmxColorInfo | null> |
The current colour written every way, or null when there is none. |
hide |
hide() => Promise<void> |
Close the floating panel. |
setFocus |
setFocus(options?: FocusOptions) => Promise<void> |
Focus the text box (popup mode) or the square (inline). |
show |
show() => Promise<void> |
Open the floating panel (popup mode). |
Slots
| Slot | Description |
|---|---|
description |
Rich help text, in place of the description property. |
label |
Rich label content, in place of the label property. |
CSS shadow parts
| Part | Description |
|---|---|
alpha |
The opacity track. |
area |
The saturation and brightness square. |
area-thumb |
Its handle. |
channel |
One channel box. |
channels |
The row of channel boxes. |
clear |
The clear button. |
contrast |
The contrast check. |
description |
The help text. |
eyedropper |
The eyedropper button. |
field |
The box around the swatch button and the text (popup mode). |
fix |
The button that fixes the contrast. |
formats |
The format buttons. |
hue |
The hue track. |
input |
The text box (popup mode). |
label |
The label element. |
panel |
The panel: floating in popup mode, in place inline. |
preview |
The before and after chips. |
swatch |
One swatch; also swatch-selected. |
swatches |
A list of swatches (also the recent list). |
thumb |
A track's handle. |
trigger |
The button that opens the panel, showing the colour. |
CSS custom properties
| Property | Description |
|---|---|
--bmx-color-picker-area-height |
The height of the saturation and brightness square. |
--bmx-color-picker-height |
The field's height. |
--bmx-color-picker-label-font-size |
The label's font size. |
--bmx-color-picker-panel-background |
The panel's background. |
--bmx-color-picker-panel-shadow |
The floating panel's shadow. |
--bmx-color-picker-panel-width |
The panel's width. |
--bmx-color-picker-radius |
Corner radius of the field and the panel. |
--bmx-color-picker-support-font-size |
Font size of the description and error message. |
--bmx-color-picker-swatch-size |
The size of a swatch. |