Add a color picker component - #42774
Open
mdo wants to merge 5 commits into
Open
Conversation
The color picker needs to move between the formats people type and the OKLCH model the v6 palette is built on, without taking on a dependency. Parsing, gamut clamping, and CSS custom property resolution live here so the component only has to deal with colors. contrastColor() mirrors the Sass color-contrast() logic, including the minimum contrast ratio, so JS and CSS pick the same foreground for a given swatch.
The range track hardcoded its fill gradient, so anything wanting a different ramp had to reimplement the thumb and track from scratch. --range-track-bg-image overrides just the image and falls back to the existing fill, which is what lets the color picker paint a hue wheel and an alpha gradient through .form-range.
Tokenized styles for the field chip, the panel, the chroma and lightness plane, the hue and alpha sliders, and the preset swatch grid. The inline chip is a background layer rather than generated content, because ::before and ::after do not render on form controls. It reuses the same recipe .form-control uses for the select caret, so the field is styled before JS runs. The swatch and preview backgrounds are composed on the elements that carry the per-swatch color. var() inside a custom property is substituted on the element that declares it, so composing them on an ancestor would resolve the color layer to transparent once and inherit it down already flattened. The panel reshaping lives in the components layer with a specificity bump over .menu. That layer outranks forms, and this file is emitted before _menu.scss within it, so the --menu-* overrides need both to take effect.
ColorPicker decorates a real input rather than replacing it, so name, value, required, disabled, readonly, labels, form.reset(), FormData, and constraint validation all come from the platform instead of being reimplemented. It matches how Datepicker, OtpInput, Strength, and Chips already work. Menu supplies the panel, which means Floating UI handles placement and the existing auto-close behavior applies. The panel is inserted next to the field by default so it keeps tab order and inherits the surrounding color mode, with container as the escape hatch for clipping ancestors. Native input and change events follow the range contract: input streams during a drag, change fires once the interaction settles. The commit guard covers the dispatches rather than just the assignment, otherwise the component's own input listener re-parses its rounded output and quantizes the color mid-drag. input[type="color"] is supported as a degraded mode. The alpha and colorspace attributes are still Safari-only, so it forces hex, disables alpha, and suppresses the native OS picker.
Adds the docs page and sidebar entry, the eyedropper icon to the symbol sprite, and the util/color description the auto-generated Dependencies table needs.
|
|
||
| // A transparent default lets us detect a rejected `fillStyle` assignment | ||
| context.clearRect(0, 0, 1, 1) | ||
| context.fillStyle = '#000' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a color picker that decorates a real
<input>instead of replacing it, so the platform keeps ownership of the form semantics.ColorPickerplugin: attaches to a text input, composesMenufor the panel, and paints a color chip inside the field.name,value,required,disabled,readonly, labels,form.reset(),FormData, and constraint validation all keep working as they normally dojs/src/util/color.ts: OKLCH-first parsing, conversion, formatting, gamut clamping, and CSS custom property resolution, with no new dependency.contrastColor()mirrors the Sasscolor-contrast()logic so JS and CSS agree on a foregroundscss/forms/_color-picker.scss: tokenized styles for the field chip, panel, chroma and lightness plane, hue and alpha sliders, and the preset swatch grid--range-track-bg-imagehook on therange-track()mixin so a consumer can replace the track fill with its own ramp instead of reimplementing the thumb and track. The color picker's hue and alpha sliders are plain.form-rangeelements through it.color-pickerwrapper with a focusable preview button, a custom trigger viadata-bs-toggle="color-picker"plusdata-bs-target, andinput[type="color"]as a degraded mode<datalist>, or a custom arrayOutput is switchable between hex, RGB, HSL, and OKLCH, and the picker infers the initial format from the value already in the field.
Native
inputandchangefollow the range contract:inputstreams during a drag,changefires once the interaction settles.Notes for reviewers:
body, which keeps tab order and inherits the surrounding color mode.containeris the escape hatch for a clipping ancestorinput[type="color"]forces hex and disables alpha, since thealphaandcolorspaceattributes are still Safari-onlyvar()inside a custom property is substituted on the element that declares it, so composing on an ancestor flattens the color layer totransparentbefore it inherits down--menu-*panel overrides live in thecomponentslayer with a specificity bump over.menu. That layer outranksforms, and this file is emitted before_menu.scsswithin it, so both are neededdist/is intentionally not includedVerified across light and dark modes: 122 new unit tests, plus area drag, hue and alpha sliders, format switching, swatch selection, keyboard navigation, Escape and outside-click dismissal, and the wrapper trigger.