npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

chromakit-react

v0.1.16

Published

A modern React color picker library with support for OKLCH, OKLAB, and traditional color spaces

Readme

ChromaKit

The Modern React Color Picker with Perceptually Uniform Colors

Build better design systems with OKLCH color space support

npm version npm downloads bundle size license TypeScript

Live DemoDocumentationMigration Guide


npm install chromakit-react

See it in action →

Table of Contents


Why Developers Choose ChromaKit

The only React color picker built for modern design systems. While other pickers struggle with consistent color scales and muddy gradients, ChromaKit uses perceptually uniform color spaces (OKLCH, OKLAB) to deliver what designers expect and users see.

// Get started in 30 seconds
import { ColorPicker } from 'chromakit-react';
import 'chromakit-react/chromakit.css';

<ColorPicker onChange={(color) => console.log(color.oklch)} />;

Perfect For

  • Design System Engineers - Generate consistent tonal scales with predictable lightness
  • Accessibility Teams - Built-in WCAG AA/AAA contrast checking
  • App Developers - Zero dependencies, 10KB bundle, works everywhere
  • UI Libraries - Composable primitives, full TypeScript support

What Makes ChromaKit Different

Perceptually Uniform Colors

The Problem: Traditional RGB/HSL create inconsistent color scales. A 10% lightness change in blue looks different than in yellow. Your carefully crafted design system falls apart.

ChromaKit's Solution: OKLCH color space ensures equal numerical changes produce equal visual differences. Generate accessible color palettes that actually look evenly spaced.

// Generate a perfectly uniform color scale
const scale = Array.from({ length: 9 }, (_, i) => {
  const lightness = 0.2 + i * 0.1; // 20% to 100%
  return `oklch(${lightness} 0.15 250)`; // Consistently spaced blues
});

Composable Architecture

Unlike monolithic pickers, ChromaKit gives you building blocks:

import {
  ColorArea,
  HueSlider,
  AlphaSlider,
  useColorState,
} from 'chromakit-react';

// Build your perfect picker layout
function CustomPicker() {
  const { hsva, colorValue, updateColor } = useColorState('#6366F1');
  return (
    <>
      <ColorArea hsva={hsva} onChange={updateColor} />
      <HueSlider hsva={hsva} onChange={updateColor} />
      <AlphaSlider hsva={hsva} onChange={updateColor} />
    </>
  );
}

Built for Performance

  • 10KB gzipped - Competitive with alternatives, but includes OKLCH + composability
  • Zero dependencies - No supply chain risk, no version conflicts
  • Tree-shakeable - Import only what you use
  • 60fps interactions - Smooth dragging on all devices

Accessibility Baked In

  • WCAG AA compliant - Full keyboard navigation, proper ARIA labels
  • Contrast checker included - getContrastRatio(), meetsContrastRatio()
  • Screen reader tested - Works with NVDA, JAWS, VoiceOver
  • Touch-friendly - 44px minimum touch targets

TypeScript Excellence

Every color format, every component prop, every utility function - fully typed:

import type { ColorValue, OKLCHColor, RGBColor } from 'chromakit-react';

const handleChange = (value: ColorValue) => {
  value.oklch; // { l: number, c: number, h: number }
  value.rgb; // { r: number, g: number, b: number }
  value.hex; // string
};

Comparison

| Feature | ChromaKit | react-colorful | react-color | | -------------- | ----------- | -------------- | ----------- | | Bundle Size | ~10KB | ~3KB | ~28KB | | OKLCH/OKLAB | ✅ | ❌ | ❌ | | Composable | ✅ | Limited | ❌ | | TypeScript | ✅ Native | ✅ | ⚠️ @types | | Dark Mode | ✅ Built-in | Manual | Manual | | Dependencies | 0 | 0 | Many |

Choose ChromaKit for: Design systems, OKLCH support, accessibility features, composability
Choose react-colorful for: Minimal bundle size (<5KB), traditional RGB/HSL only

Migration Guide available for switching from react-colorful or react-color.


Quick Start

Controlled Component (recommended)

import { useState } from 'react';
import { ColorPicker } from 'chromakit-react';
import 'chromakit-react/chromakit.css';

function App() {
  const [color, setColor] = useState('#6366F1');

  return (
    <ColorPicker
      value={color}
      onChange={(colorValue) => setColor(colorValue.hex)}
    />
  );
}

That's it! You now have a fully-featured color picker with OKLCH support, eyedropper, color history, and more.


Why OKLCH?

OKLCH is a perceptually uniform color space - equal numerical changes produce equal visual differences.

Key Benefits:

  • Predictable Lightness: oklch(0.5 ...) appears equally bright regardless of hue, unlike HSL
  • Better Gradients: Smooth transitions without muddy middle tones
  • Design Systems: Generate consistent tonal scales with uniform visual weight
  • Wide Gamut: Access more vibrant colors on modern displays

Parameters:

  • L (Lightness): 0 (black) to 1 (white)
  • C (Chroma): 0 (grayscale) to ~0.4 (vibrant)
  • H (Hue): 0-360° around the color wheel
// HSL: Same lightness value, different perceived brightness
hsl(240, 100%, 50%) // Blue - looks dark
hsl(60, 100%, 50%)  // Yellow - looks bright

// OKLCH: Same lightness = same perceived brightness
oklch(0.5 0.2 240)  // Blue at 50% brightness
oklch(0.5 0.2 60)   // Yellow at 50% brightness

Learn more about OKLCH →


Framework Setup

Next.js App Router

'use client';
import { ColorPicker } from 'chromakit-react';
import 'chromakit-react/chromakit.css';

export function MyColorPicker() {
  const [color, setColor] = useState('#6366F1');
  return <ColorPicker value={color} onChange={(c) => setColor(c.hex)} />;
}

Next.js Pages Router (SSR)

import dynamic from 'next/dynamic';

const ColorPicker = dynamic(
  () => import('chromakit-react').then((mod) => mod.ColorPicker),
  { ssr: false }
);

Vite / CRA

import { ColorPicker } from 'chromakit-react';
import 'chromakit-react/chromakit.css';
// Works out of the box

API Reference

ColorPicker

Full-featured color picker component.

interface ColorPickerProps {
  value?: string;
  defaultValue?: string;
  onChange?: (color: ColorValue) => void;
  onChangeComplete?: (color: ColorValue) => void;
  formats?: ColorFormat[];
  showAlpha?: boolean;
  showInputs?: boolean;
  showPreview?: boolean;
  showCopyButton?: boolean;
  showEyeDropper?: boolean;
  showPresets?: boolean;
  presets?: string[];
  presetGroups?: PresetGroup[] | Record<string, string[]>;
  enableHistory?: boolean;
  width?: number;
  height?: number;
  className?: string;
}

Props:

| Prop | Type | Default | Description | | ------------------ | --------------------------------------------- | -------------------------------- | --------------------------------------------------- | | value | string | - | Controlled color value (any supported format) | | defaultValue | string | '#6366F1' | Initial color for uncontrolled mode | | onChange | (color: ColorValue) => void | - | Fires during color changes (real-time) | | onChangeComplete | (color: ColorValue) => void | - | Fires when user completes a change (mouse up, blur) | | formats | ColorFormat[] | ['hex', 'rgb', 'hsl', 'oklch'] | Available color format options | | showAlpha | boolean | true | Enable alpha channel controlha channel control | | showInputs | boolean | true | Show color input fields | | showPreview | boolean | true | Show color preview swatch | | showCopyButton | boolean | true | Show copy button (enables Cmd/Ctrl+C) | | showEyeDropper | boolean | true | Show eyedropper button (if browser supports) | | showPresets | boolean | true | Show preset colors section | | presets | string[] | Built-in defaults | Custom preset colors array | | presetGroups | PresetGroup[] or Record<string, string[]> | Built-in groups | Preset color groups with dropdown | | enableHistory | boolean | true | Store recent colors in localStorage | | width | number | auto | Custom picker width in pixels | | height | number | 100 | Color area height in pixels | | className | string | - | Additional CSS classes |

ColorValue Object:

The onChange and onChangeComplete callbacks receive a ColorValue object:

interface ColorValue {
  hex: string; // "#ff6b6b"
  hex8: string; // "#ff6b6bff"
  rgb: RGBColor; // { r: 255, g: 107, b: 107 }
  rgba: RGBAColor; // { r: 255, g: 107, b: 107, a: 1 }
  hsl: HSLColor; // { h: 0, s: 100, l: 71 }
  hsla: HSLAColor; // { h: 0, s: 100, l: 71, a: 1 }
  hsv: HSVColor; // { h: 0, s: 58, v: 100 }
  hsva: HSVAColor; // { h: 0, s: 58, v: 100, a: 1 }
  oklch: OKLCHColor; // { l: 0.71, c: 0.19, h: 25 }
  oklcha: OKLCHAColor; // { l: 0.71, c: 0.19, h: 25, a: 1 }
  oklab: OKLABColor; // { l: 0.71, a: 0.17, b: 0.08 }
  oklaba: OKLABAColor; // { l: 0.71, a: 0.17, b: 0.08, alpha: 1 }
}

Composable Components

Build custom pickers using primitive components for complete layout control:

import {
  ColorArea,
  HueSlider,
  AlphaSlider,
  ColorPreview,
  ColorInputs,
  useColorState,
} from 'chromakit-react';

function CustomPicker() {
  const { hsva, colorValue, updateColor } = useColorState('#ff0000');

  return (
    <div className="custom-picker">
      <ColorArea hsva={hsva} onChange={updateColor} width={200} height={150} />
      <HueSlider hsva={hsva} onChange={updateColor} />
      <AlphaSlider hsva={hsva} onChange={updateColor} />
      <ColorPreview colorValue={colorValue} size="lg" />
      <ColorInputs
        colorValue={colorValue}
        onChange={(hex) => updateColor(parseColor(hex))}
        format="hex"
      />
    </div>
  );
}

Component Props:

interface ColorAreaProps {
  hsva: HSVAColor;
  onChange: (hsva: HSVAColor) => void;
  onStart?: () => void;
  onEnd?: () => void;
  width?: number; // Default: 256
  height?: number; // Default: 200
  className?: string;
}
interface HueSliderProps {
  hsva: HSVAColor;
  onChange: (hsva: HSVAColor) => void;
  onStart?: () => void;
  onEnd?: () => void;
  vertical?: boolean; // Default: false
  className?: string;
}
interface AlphaSliderProps {
  hsva: HSVAColor;
  onChange: (hsva: HSVAColor) => void;
  onStart?: () => void;
  onEnd?: () => void;
  vertical?: boolean; // Default: false
  className?: string;
}
interface ColorPreviewProps {
  colorValue: ColorValue;
  showComparison?: boolean; // Show before/after
  originalColor?: string; // Original color for comparison
  size?: 'sm' | 'md' | 'lg'; // Default: 'md'
  className?: string;
}
interface ColorInputsProps {
  colorValue: ColorValue;
  onChange: (hexColor: string) => void;
  format: ColorFormat;
  onFormatChange?: (format: ColorFormat) => void;
  showAlpha?: boolean;
  availableFormats?: ColorFormat[];
  className?: string;
}
interface ColorSwatchProps {
  color: string;
  onClick?: () => void;
  active?: boolean;
  size?: 'sm' | 'md' | 'lg';
  className?: string;
}
interface RecentColorsProps {
  onColorSelect?: (color: string) => void;
  maxColors?: number; // Default: 10
  className?: string;
}

Hooks

useColorState - Main color state management hook

function useColorState(initialColor?: string) {
  return {
    hsva: HSVAColor;           // Internal HSVA representation
    colorValue: ColorValue;     // All format conversions
    updateColor: (hsva: HSVAColor) => void;
    setColorFromString: (color: string) => void;
  };
}

useDrag - Drag interaction handler (for custom components)

function useDrag(
  ref: RefObject<HTMLElement>,
  onDrag: (x: number, y: number) => void,
  onStart?: () => void,
  onEnd?: () => void
): void;

Examples

Basic Usage

Simple controlled picker

const [color, setColor] = useState('#6366F1');

<ColorPicker
  value={color}
  onChange={(colorValue) => setColor(colorValue.hex)}
/>;

Preset Colors

Custom preset colors

<ColorPicker
  value={color}
  onChange={(colorValue) => setColor(colorValue.hex)}
  presets={['#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A', '#98D8C8', '#F7DC6F']}
/>

Preset groups with dropdown (New in v0.1.10)

const presetGroups = [
  {
    name: 'Material',
    colors: ['#F44336', '#E91E63', '#9C27B0', '#673AB7', '#3F51B5'],
  },
  {
    name: 'Tailwind',
    colors: ['#EF4444', '#F97316', '#EAB308', '#22C55E', '#10B981'],
  },
];

<ColorPicker
  value={color}
  onChange={(colorValue) => setColor(colorValue.hex)}
  presetGroups={presetGroups}
/>;

Or use object format:

const presetGroups = {
  Material: ['#F44336', '#E91E63', '#9C27B0', '#673AB7', '#3F51B5'],
  Tailwind: ['#EF4444', '#F97316', '#EAB308', '#22C55E', '#10B981'],
};

<ColorPicker
  value={color}
  onChange={(colorValue) => setColor(colorValue.hex)}
  presetGroups={presetGroups}
/>;

Format Control

Limit available formats

<ColorPicker
  value={color}
  onChange={(colorValue) => setColor(colorValue.hex)}
  formats={['oklch', 'hex', 'rgb']}
/>

Advanced Features

Track color changes completion

<ColorPicker
  value={color}
  onChange={(colorValue) => {
    // Fires during dragging (real-time updates)
    setColor(colorValue.hex);
  }}
  onChangeComplete={(colorValue) => {
    // Fires when user finishes selecting (mouse up, blur)
    // Perfect for API calls or expensive operations
    saveColorToDatabase(colorValue.hex);
  }}
/>

Color history with localStorage

<ColorPicker
  value={color}
  onChange={(colorValue) => setColor(colorValue.hex)}
  enableHistory={true} // Automatically saves recent colors
/>

Access all color formats

<ColorPicker
  value={color}
  onChange={(colorValue) => {
    console.log(colorValue.hex); // "#ff6b6b"
    console.log(colorValue.rgb); // { r: 255, g: 107, b: 107 }
    console.log(colorValue.oklch); // { l: 0.71, c: 0.19, h: 25 }
    setColor(colorValue.hex);
  }}
/>

Eyedropper & Copy Features

Built-in eyedropper and copy buttons

<ColorPicker
  value={color}
  onChange={(colorValue) => setColor(colorValue.hex)}
  showEyeDropper={true} // Shows eyedropper button (if browser supports)
  showCopyButton={true} // Shows copy button + enables Cmd/Ctrl+C
/>

Note: The EyeDropper API requires a secure context (HTTPS) and is currently supported in Chrome/Edge 95+, not yet in Firefox or Safari.

Form Integration

React Hook Form

import { Controller, useForm } from 'react-hook-form';
import { ColorPicker } from 'chromakit-react';

function MyForm() {
  const { control, handleSubmit } = useForm({
    defaultValues: { brandColor: '#6366F1' }
  });

  return (
    <form onSubmit={handleSubmit(data => console.log(data))}>
      <Controller
        name="brandColor"
        control={control}
        render={({ field }) => (
          <ColorPicker
            value={field.value}
            onChange={(c) => field.onChange(c.hex)}
          />
        )}
      />
    </form>
  );
}

Formik

import { useFormik } from 'formik';
import { ColorPicker } from 'chromakit-react';

function MyForm() {
  const formik = useFormik({
    initialValues: { brandColor: '#6366F1' },
    onSubmit: values => console.log(values)
  });

  return (
    <form onSubmit={formik.handleSubmit}>
      <ColorPicker
        value={formik.values.brandColor}
        onChange={(c) => formik.setFieldValue('brandColor', c.hex)}
      />
    </form>
  );
}

Color Utilities

Conversion Functions

Convert between any color format:

import {
  hexToRgb,
  rgbToHex,
  rgbToHsl,
  hslToRgb,
  rgbToOklch,
  oklchToRgb,
  parseColor,
  colorValueToString,
} from 'chromakit-react';

// Parse any format
const parsed = parseColor('#ff6b6b');
// Returns: { r: 255, g: 107, b: 107, a: 1 }

// Convert between formats
const rgb = hexToRgb('#ff0000'); // { r: 255, g: 0, b: 0 }
const hex = rgbToHex(rgb); // '#ff0000'
const hsl = rgbToHsl(rgb); // { h: 0, s: 100, l: 50 }
const oklch = rgbToOklch(rgb); // { l: 0.63, c: 0.26, h: 29 }

// Format color value to string
const color = { r: 255, g: 107, b: 107, a: 1 };
const hexString = colorValueToString(color, 'hex'); // "#ff6b6b"
const rgbString = colorValueToString(color, 'rgb'); // "rgb(255, 107, 107)"
const oklchString = colorValueToString(color, 'oklch'); // "oklch(0.71 0.19 25)"

Available conversion functions:

  • hexToRgba(hex) - Parse hex to RGBA
  • parseColor(color) - Parse any format to RGBA
  • rgbToHex(rgb) - RGB to 6-digit hex
  • rgbaToHex8(rgba) - RGBA to 8-digit hex
  • rgbToHsl(rgb) / hslToRgb(hsl)
  • rgbaToHsla(rgba) / hslaToRgba(hsla)
  • rgbToHsv(rgb) / hsvToRgb(hsv)
  • rgbaToHsva(rgba) / hsvaToRgba(hsva)
  • rgbToOklab(rgb) / oklabToRgb(oklab)
  • rgbaToOklaba(rgba) / oklabaToRgba(oklaba)
  • rgbToOklch(rgb) / oklchToRgb(oklch)
  • rgbaToOklcha(rgba) / oklchaToRgba(oklcha)
  • oklabToOklch(oklab) / oklchToOklab(oklch)
  • rgbaToColorValue(rgba) - Convert to all formats
  • colorValueToString(colorValue, format) - Format to string

Color Formats

ChromaKit supports the following color formats:

| Format | Example | Description | | ------ | ------------------------- | -------------------------------- | | HEX | #ff0000 | Hexadecimal RGB | | HEX8 | #ff0000ff | Hex with alpha | | RGB | rgb(255, 0, 0) | Red, Green, Blue | | RGBA | rgba(255, 0, 0, 1) | RGB with alpha | | HSL | hsl(0, 100%, 50%) | Hue, Saturation, Lightness | | HSLA | hsla(0, 100%, 50%, 1) | HSL with alpha | | HSV | hsv(0, 100%, 100%) | Hue, Saturation, Value | | HSVA | hsva(0, 100%, 100%, 1) | HSV with alpha | | OKLCH | oklch(0.63 0.26 29) | Perceptually uniform cylindrical | | OKLCHA | oklch(0.63 0.26 29 / 1) | OKLCH with alpha | | OKLAB | oklab(0.63 0.22 0.13) | Perceptually uniform Cartesian |

Theming & Customization

Override CSS variables for custom themes:

.my-custom-picker {
  --ck-primary: #9333ea;
  --ck-bg: #0a0a0b;
  --ck-text: #ffffff;
  --ck-radius: 8px;
}
<ColorPicker className="my-custom-picker" />

See source CSS for all available variables.

Browser Support

ChromaKit works in all modern browsers:

  • Chrome/Edge: 88+
  • Firefox: 87+
  • Safari: 15+
  • Node.js: 20+ (for SSR/build tools)

Feature Support

| Feature | Browser Support | Fallback | | ------------------------ | --------------------------------------- | --------------------------- | | Color Picker | All modern browsers | - | | OKLCH/OKLAB calculations | All modern browsers | - | | CSS oklch() syntax | Chrome 111+, Firefox 113+, Safari 15.4+ | Use hex/rgb output | | EyeDropper API | Chrome/Edge 95+ | Button hidden automatically | | Clipboard API | All modern browsers (HTTPS required) | Manual copy |

For CSS OKLCH color space support, see Can I Use: OKLCH.

TypeScript

ChromaKit is written in TypeScript and provides complete type definitions for all exports.

Type Exports

import type {
  // Color format types
  RGBColor,
  RGBAColor,
  HSLColor,
  HSLAColor,
  HSVColor,
  HSVAColor,
  OKLCHColor,
  OKLCHAColor,
  OKLABColor,
  OKLABAColor,

  // Utility types
  ColorFormat,
  ColorValue,
  AnyColor,

  // Component props
  ColorPickerProps,
  ColorAreaProps,
  HueSliderProps,
  AlphaSliderProps,

  // Preset types
  PresetGroup,
  PresetGroupsInput,
} from 'chromakit-react';

Type-Safe Color Conversions

import {
  rgbToHsl,
  hslToRgb,
  type RGBColor,
  type HSLColor,
} from 'chromakit-react';

const rgb: RGBColor = { r: 255, g: 107, b: 107 };
const hsl: HSLColor = rgbToHsl(rgb);
// Type: { h: number; s: number; l: number }

const backToRgb: RGBColor = hslToRgb(hsl);
// Fully typed with autocomplete

Troubleshooting

Common Issues

Issue: Styles not loading

Make sure to import the CSS file:

import 'chromakit-react/chromakit.css';

Or if using automatic imports, ensure your bundler processes CSS side effects.


Issue: Eyedropper not showing

The EyeDropper API requires:

  • Secure context (HTTPS in production, localhost in development)
  • Browser support (Chrome/Edge 95+)
  • User permission

Check browser support:

if ('EyeDropper' in window) {
  // Eyedropper available
}

Issue: TypeScript errors with color formats

Ensure you're using the correct types:

import type { ColorValue } from 'chromakit-react';

const handleChange = (colorValue: ColorValue) => {
  // colorValue has all format properties
  console.log(colorValue.hex);
  console.log(colorValue.oklch);
};

Issue: Next.js "window is not defined" error

For server-side rendering, use dynamic imports:

import dynamic from 'next/dynamic';

const ColorPicker = dynamic(
  () => import('chromakit-react').then((mod) => mod.ColorPicker),
  { ssr: false }
);

Issue: Color history not persisting

History uses localStorage with key chromakit-color-history. Ensure:

  • localStorage is available (not in incognito/private mode)
  • enableHistory prop is true (default)
  • Browser has storage permissions

Clear history manually:

import { clearColorHistory } from 'chromakit-react';
clearColorHistory();

**I## Resources

Support

License

MIT © Garrett Siegel

Credits

Built with:

chromakit.site

Star on GitHubTry Live DemoReport Bug Color science based on:

Design inspired by:


chromakit.site