Chrome Extension Color Picker — Developer Guide

6 min read

Build a Color Picker Extension

What You’ll Build

Manifest


Step 1: EyeDropper API (Modern Browsers)

Chrome 95+ supports the native EyeDropper API:

async function pickColor() {
  const eyeDropper = new EyeDropper();
  const result = await eyeDropper.open();
  return result.sRGBHex; // "#ff0000"
}

Must be triggered from user gesture. For broader compatibility, use a content script:

document.addEventListener('mousemove', (e) => {
  const el = document.elementFromPoint(e.clientX, e.clientY);
  const color = getComputedStyle(el).backgroundColor;
});

Step 2: Magnifier Overlay

Inject a picker with crosshair:

const magnifier = document.createElement('div');
magnifier.innerHTML = '<div class="crosshair"></div><div class="preview"></div>';
document.body.appendChild(magnifier);

document.addEventListener('mousemove', (e) => {
  const el = document.elementFromPoint(e.clientX, e.clientY);
  magnifier.querySelector('.preview').style.background = getComputedStyle(el).backgroundColor;
});

Style: 10x zoom canvas, crosshair at center, position fixed, z-index 999999.


Step 3: Color Conversion

export function hexToRgb(hex) {
  return {
    r: parseInt(hex.slice(1, 3), 16),
    g: parseInt(hex.slice(3, 5), 16),
    b: parseInt(hex.slice(5, 7), 16)
  };
}

export function rgbToHsl(r, g, b) {
  r /= 255; g /= 255; b /= 255;
  const max = Math.max(r, g, b), min = Math.min(r, g, b);
  let h, s, l = (max + min) / 2;
  if (max === min) { h = s = 0; }
  else {
    const d = max - min;
    s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
    switch (max) {
      case r: h = ((g - b) / d + (g < b ? 6 : 0)) / 6; break;
      case g: h = ((b - r) / d + 2) / 6; break;
      case b: h = ((r - g) / d + 4) / 6; break;
    }
  }
  return { h: Math.round(h * 360), s: Math.round(s * 100), l: Math.round(l * 100) };
}

Step 4: Popup UI

<div class="picker-popup">
  <div class="current-color">
    <div id="color-swatch"></div>
    <div class="color-values">
      <button class="format-btn active" data-format="hex">HEX</button>
      <button class="format-btn" data-format="rgb">RGB</button>
      <button class="format-btn" data-format="hsl">HSL</button>
      <div id="color-value">#3498db</div>
    </div>
  </div>
  <button id="pick-btn">Pick Color</button>
  <button id="copy-btn">Copy to Clipboard</button>
  <div class="recent-colors"><h3>Recent</h3><div id="recent-grid"></div></div>
  <div class="palette-section"><h3>Palette</h3><button id="save-btn">Save</button></div>
</div>

Step 5: Palette Storage

Use @theluckystrike/webext-storage:

import { storage } from '@theluckystrike/webext-storage';

async function saveToPalette(name, color) {
  const palettes = await storage.get('colorPalettes') || {};
  (palettes[name] ||= []).push(color);
  await storage.set('colorPalettes', palettes);
}

function exportAsCssVars(name, colors) {
  return `:root {\n${colors.map((c, i) => `--${name}-${i + 1}: ${c};`).join('\n')}\n}`;
}

Step 6: Keyboard Shortcut

manifest.json:

{
  "commands": {
    "pick-color": {
      "suggested_key": { "default": "Alt+Shift+C", "mac": "Alt+Shift+C" },
      "description": "Pick a color from the page"
    }
  }
}

background.js:

chrome.commands.onCommand.addListener(async (command) => {
  if (command === 'pick-color') {
    const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
    chrome.tabs.sendMessage(tab.id, { action: 'start-picker' });
  }
});

Works via activeTab, must be user-triggered. Set at chrome://extensions/shortcuts.


Cross-References


Summary

You built a color picker extension with EyeDropper API + fallback content script, color conversion (HEX/RGB/HSL), popup UI with format switching, palette storage and export, and keyboard shortcut (Alt+Shift+C). Test at chrome://extensions/ with Developer mode enabled. -e —

Part of the Chrome Extension Guide by theluckystrike. Built at zovo.one.

No previous article
No next article