Skip to content

Advanced Topics

The component uses Preact Signals for efficient, fine-grained reactivity:

  • Minimal Re-renders: Only affected parts of the UI update
  • Automatic Dependency Tracking: Signals track dependencies automatically
  • Lightweight: Small runtime overhead

The component uses Shadow DOM for style isolation:

  • No Style Conflicts: Component styles don’t leak to the page
  • CSS Parts API: Customize via ::part() selectors
  • Composability: Use multiple instances without conflicts

Color calculations are powered by colorjs.io:

  • Accurate Conversions: Between all supported color spaces
  • Gamut Mapping: Automatic handling of out-of-gamut colors
  • Standards Compliant: Follows CSS Color Module Level 4

The component is designed for minimal bundle impact:

  • Tree-Shakeable: ES modules allow unused code elimination
  • No Framework Dependencies: Only Preact Signals and colorjs.io
  • Modern Output: Targets ES2020+ for smaller bundles

Efficient Updates:

  • Signals prevent unnecessary re-renders
  • Color conversions are cached
  • Popover uses native Popover API (hardware accelerated)

Lazy Loading:

  • Component registers on import
  • Popover content rendered on-demand

The component uses the native Popover API with intelligent positioning:

The popover automatically positions relative to the trigger button:

  • Respects viewport boundaries
  • Handles safe areas (notches, system UI)
  • Flips when near edges
  • No manual positioning required

Use setAnchor() to position relative to a different element:

const picker = document.querySelector("color-input");
const customAnchor = document.querySelector("#my-anchor");
picker.setAnchor(customAnchor);
picker.show();

The Popover API is required:

  • Chrome: 114+
  • Safari: 17+
  • Firefox: 125+

The component automatically detects which color gamut contains the current color:

The read-only gamut property returns the smallest gamut:

console.log(picker.gamut); // "srgb" | "p3" | "rec2020" | "xyz"

A gamut badge appears in the picker UI showing the current gamut. This helps you understand:

  • srgb: Works on all displays
  • p3: Requires Display P3 or better
  • rec2020: Requires ultra-wide gamut
  • xyz: Beyond Rec2020

The change event includes gamut information:

picker.addEventListener("change", (e) => {
console.log(e.detail.gamut); // "srgb" | "p3" | "rec2020" | "xyz"
});

While the component doesn’t inherit from HTMLInputElement, you can integrate it with forms:

<form id="settings-form">
<label for="theme-color">Theme Color</label>
<color-input id="theme-color" value="oklch(70% 20% 240)"></color-input>
<input type="hidden" name="theme_color" id="theme-color-input" />
</form>
<script>
const picker = document.querySelector("#theme-color");
const hiddenInput = document.querySelector("#theme-color-input");
picker.addEventListener("change", (e) => {
hiddenInput.value = e.detail.value;
});
// Set initial value
hiddenInput.value = picker.value;
</script>

Use properties and methods to control the picker:

const picker = document.querySelector("color-input");
// Read state
console.log(picker.value);
console.log(picker.colorspace);
console.log(picker.gamut);
console.log(picker.contrastColor);
// Update state
picker.value = "oklch(80% 15% 180)";
picker.theme = "dark";
// Control popover
picker.show();
picker.close();