Skip to content
ShineUI
Install

ColorEditor

The full color tool.

<sui-color-editor>Advanced inputsStable

About

Full-spectrum color editor: a saturation and brightness area, hue and alpha strips, HEX, RGB and HSL fields, an eyedropper, a compare chip that reverts on click, preset and recent swatches, and an optional contrast readout. The chrome is ShineUI; the color is user content. Use sui-color-picker where a color must stay on brand.

Usage

<sui-color-editor label="Fill color" name="fill" value="#2E86DE80" help="Applied to the chart series."></sui-color-editor> <script type="module"> import { defineCustomElements } from "@shineui/core/loader"; import "@shineui/core/shineui.css"; defineCustomElements(); const el = document.querySelector("sui-color-editor"); el.presets = []; /* string[] */ el.addEventListener("suiChange", (e) => console.log(e.detail)); </script>
import { SuiColorEditor } from "@shineui/react"; import "@shineui/core/shineui.css"; export function Example() { const presets = []; /* string[] */ return <SuiColorEditor label="Fill color" name="fill" value="#2E86DE80" help="Applied to the chart series." presets={presets} onSuiChange={(e) => console.log(e.detail)} />; }
<script setup> import { SuiColorEditor } from "@shineui/vue"; import "@shineui/core/shineui.css"; const presets = []; // string[] const onChange = (e) => console.log(e.detail); </script> <template> <SuiColorEditor label="Fill color" name="fill" value="#2E86DE80" help="Applied to the chart series." :presets="presets" @sui-change="onChange" /> </template>
import { Component } from "@angular/core"; import { SuiColorEditor } from "@shineui/angular"; @Component({ standalone: true, imports: [SuiColorEditor], template: `<sui-color-editor label="Fill color" name="fill" value="#2E86DE80" help="Applied to the chart series." [presets]="presets" (suiChange)="onChange($event)"></sui-color-editor>`, }) export class Example { presets = []; // string[] onChange(e: CustomEvent) { console.log(e.detail); } }

Basic

The saturation area and both strips are role="slider" with aria-valuenow. The HEX, RGB and HSL buttons switch the fields, and the eyedropper renders only where the browser has the EyeDropper API.

<sui-color-editor label="Fill color" name="fill" value="#2E86DE80" help="Applied to the chart series."></sui-color-editor>

Alpha off and contrast readout

no-alpha drops the alpha strip and the A% field, so the color is always opaque and posts as #RRGGBB. show-contrast adds the WCAG ratio against white and against black, graded AAA, AA, AA large or Fails.

<div style="display:flex;gap:20px;flex-wrap:wrap;align-items:flex-start"> <sui-color-editor label="Text color" value="#7A3FBF" no-alpha show-contrast recents-key="sui-docs-text"></sui-color-editor> <sui-color-editor label="Highlight" value="#FF9422" width="300" no-alpha show-contrast recents-key="sui-docs-highlight"></sui-color-editor> </div>

Custom presets and recents

presets replaces the brand six and is a property, not an attribute. recents-key is the localStorage key the Recent row is kept under; give two editors on one screen different keys, or an empty key to keep the row in memory and write nothing.

<div style="display:flex;gap:20px;flex-wrap:wrap;align-items:flex-start"> <sui-color-editor id="ce-presets" label="Ledger category" value="#7A3FBF" recents-key="sui-docs-ledger"></sui-color-editor> <sui-color-editor id="ce-memory" label="Scratch color" value="#FF9422" recents-key=""></sui-color-editor> </div>
// setup(root): properties and events setup(root) { root.querySelector('#ce-presets').presets = LEDGER_PRESETS; }

Disabled

disabled locks every control and the editor posts nothing, since a disabled control is barred from submission. A disabled fieldset locks it the same way without writing the prop.

<sui-color-editor id="ce-off" label="Managed by your administrator" value="#FF9422" disabled help="Theme colors are set by your administrator."></sui-color-editor>

Read-only

readonly shows the color and keeps its fields readable and copyable and its sliders focusable, read as read-only, but nothing changes it: no drag, no key, no swatch. The model buttons still switch the fields, and the value still posts.

<sui-color-editor label="Approved brand color" name="brand" value="#FF9422" readonly no-alpha help="Approved in the brand review."></sui-color-editor>

States

label, help, error, required and invalid, the same five sui-color-picker carries. The editor always holds a color, so error is the path that fails a form rather than an empty value, and the message is tied to the hex field so a screen reader reaches it from the control.

<div style="display:flex;gap:20px;flex-wrap:wrap"> <sui-color-editor label="Brand color" required help="Used for buttons and links." no-alpha></sui-color-editor> <sui-color-editor label="Accent color" required value="#7A3FBF" no-alpha error="That color is not in the approved brand palette."></sui-color-editor> </div>

In a form

The editor posts the CSS string: #RRGGBB, or rgba() once alpha drops below 100. value itself is written back as #RRGGBB or #RRGGBBAA.

Submit to see what posts.
<form id="ce-form" style="display:grid;gap:16px;justify-items:start"> <sui-color-editor label="Fill color" name="fill" value="#2E86DE80" recents-key="sui-docs-form"></sui-color-editor> <sui-button type="submit" variant="brand" label="Submit"></sui-button> <output id="ce-form-out" style="font:460 13px/1.6 var(--font-sans);color:var(--ink-muted)">Submit to see what posts.</output> </form>
// setup(root): properties and events setup(root) { const form = root.querySelector('#ce-form'); const out = root.querySelector('#ce-form-out'); form.addEventListener('submit', (e) => { e.preventDefault(); out.textContent = [...new FormData(form)].map(([k, v]) => `${k}=${v}`).join(' '); }); }

Events

suiInput fires on every frame of a drag and suiChange when the choice commits: a pointer release, a key, a swatch, the eyedropper or a field entry. Both carry hex, alpha, rgb, hsl and css; persist on suiChange, preview on suiInput.

suiInput: none yet. suiChange: none yet.
<div style="display:flex;gap:20px;align-items:flex-start;flex-wrap:wrap"> <sui-color-editor id="ce-events" label="Fill color" name="fill" value="#FF9422" recents-key="sui-docs-events"></sui-color-editor> <div style="display:grid;gap:6px;font:460 13px/1.6 var(--font-sans);color:var(--ink-muted)"> <span id="ce-log-input">suiInput: none yet.</span> <span id="ce-log-change">suiChange: none yet.</span> </div> </div>
// setup(root): properties and events setup(root) { const el = root.querySelector('#ce-events'); const inputLog = root.querySelector('#ce-log-input'); const changeLog = root.querySelector('#ce-log-change'); let frames = 0; el.addEventListener('suiInput', (e) => { frames += 1; inputLog.textContent = `suiInput ${frames}: ${e.detail.css}`; }); el.addEventListener('suiChange', (e) => { const { r, g, b } = e.detail.rgb; changeLog.textContent = `suiChange: ${e.detail.hex} rgb(${r} ${g} ${b}) alpha ${e.detail.alpha}`; }); }

Keyboard

KeyAction
ArrowRight ArrowLeftOn the saturation area, moves saturation one step. On the hue or alpha strip, moves it by one.
ArrowUp ArrowDownOn the saturation area, moves brightness one step. On a strip, moves it by one.
Shift+ArrowMoves ten steps.
PageUp PageDownMoves brightness, hue or alpha ten steps.
Home EndOn the saturation area, jumps to no saturation or full saturation. On a strip, jumps to either end.
Enter SpaceOn the compare chip, reverts to the original color. Enter also commits a typed hex or channel value.

API

<sui-color-editor>

Properties

PropertyAttributeTypeDefaultDescription
disableddisabledbooleanfalseLocks every control. A disabled fieldset or form locks it too, without writing this prop.
errorerrorstringstring | undefinedError message under the field. Setting it paints the danger state and fails form validation.
helphelpstringstring | undefinedHelp text under the editor.
invalidinvalidbooleanfalseDanger border with the message handled elsewhere.
labellabelstringstring | undefinedLabel above the editor.
namenamestringstring | undefinedForm control name. Posts the CSS string: #RRGGBB, or rgba() once alpha drops below 100; a disabled editor posts nothing. Reflected: entry construction reads the content attribute, and the three framework bindings set props, so an unreflected name would post nothing.
noAlphano-alphabooleanfalseHides the alpha strip and the A% field; the color is then always opaque and posts as #RRGGBB.
presetsproperty onlystring[]['#121C4D', '#FF9422', '#DCE3EC', '#8E9CAF', '#D86F00', '#00449E']Preset swatch row; defaults to the brand six (replaceable, not enforced). Property, not attribute.
readOnlyreadonlybooleanfalseDisplay only: the color can be read, its fields copied and the model switched, but nothing changes it; the sliders keep their tab stops and announce themselves read-only, and the value still posts. A read-only editor never fails a form on required.
recentsKeyrecents-keystring'sui-color-editor-recents'localStorage key the "Recent" row is kept under. Give two editors on one screen different keys to keep their recents apart, or "" to keep them in memory and write nothing.
requiredrequiredbooleanfalseMarks the field required: the asterisk on the label and aria-required on the hex input. The editor always holds a color, so this cannot raise valueMissing on its own, which is the same shape sui-color-picker has; error is the path that actually fails a form.
showContrastshow-contrastbooleanfalseWCAG contrast readout on white and on black.
valuevaluestring'#000000'Color as #RGB, #RRGGBB or #RRGGBBAA. Mutable; rewritten as #RRGGBB / #RRGGBBAA before suiChange fires. Black (#000000) by default, as on the native color input, and a value that does not parse reads as black too.
widthwidthnumber264Editor width in px.

Events

EventDetailReactVueDescription
suiChangeSuiColorEditorValueDetailonSuiChange@sui-changeFires when a color is committed: a drag released, a key, a swatch, the eyedropper or a field entry. Same detail as suiInput.
suiInputSuiColorEditorValueDetailonSuiInput@sui-inputFires on every color change, drag frames included; detail is { hex, alpha, rgb, hsl, css }.

Methods

MethodSignatureDescription
setFocus()setFocus() => Promise<void>Moves keyboard focus to the saturation and brightness field, the first control in the card. sui-form calls this to reach the first invalid control.

Types

SuiColorEditorValueDetail interfaceDetail of suiInput and suiChange: the color in every model plus a CSS string (hex, or rgba() when alpha < 100).
hexstring
alphanumber
rgb{ r: number; g: number; b: number; }
hsl{ h: number; s: number; l: number; }
cssstring

Theming

Every design token this element can render with, read from its own stylesheets and the ones it imports. Set a token on :root or on any ancestor and every use below follows it. The values shown are the ones in the theme you are viewing. The list is read per stylesheet, so an element that imports a shared drawing module lists that module's tokens too, whether or not it paints with all of them.

See also