
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
chromakit-react
Advanced tools
A modern React color picker library with support for OKLCH, OKLAB, and traditional color spaces
Build better design systems with OKLCH color space support
Live Demo • Documentation • Migration Guide
npm install chromakit-react
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)} />;
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
});
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} />
</>
);
}
getContrastRatio(), meetsContrastRatio()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
};
| 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.
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.
OKLCH is a perceptually uniform color space - equal numerical changes produce equal visual differences.
Key Benefits:
oklch(0.5 ...) appears equally bright regardless of hue, unlike HSLParameters:
// 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
'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)} />;
}
import dynamic from 'next/dynamic';
const ColorPicker = dynamic(
() => import('chromakit-react').then((mod) => mod.ColorPicker),
{ ssr: false }
);
import { ColorPicker } from 'chromakit-react';
import 'chromakit-react/chromakit.css';
// Works out of the box
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 }
}
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;
}
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;
Simple controlled picker
const [color, setColor] = useState('#6366F1');
<ColorPicker
value={color}
onChange={(colorValue) => setColor(colorValue.hex)}
/>;
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}
/>;
Limit available formats
<ColorPicker
value={color}
onChange={(colorValue) => setColor(colorValue.hex)}
formats={['oklch', 'hex', 'rgb']}
/>
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);
}}
/>
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.
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>
);
}
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 RGBAparseColor(color) - Parse any format to RGBArgbToHex(rgb) - RGB to 6-digit hexrgbaToHex8(rgba) - RGBA to 8-digit hexrgbToHsl(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 formatscolorValueToString(colorValue, format) - Format to stringChromaKit 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 |
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.
ChromaKit works in all modern browsers:
| 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.
ChromaKit is written in TypeScript and provides complete type definitions for all 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';
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
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:
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:
enableHistory prop is true (default)Clear history manually:
import { clearColorHistory } from 'chromakit-react';
clearColorHistory();
**I## Resources
MIT © Garrett Siegel
Built with:
Star on GitHub • Try Live Demo • Report Bug Color science based on:
Design inspired by:
FAQs
A modern React color picker library with support for OKLCH, OKLAB, and traditional color spaces
We found that chromakit-react demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.