UNPKG

contrastrast

Version:

A lightweight tool that parses color strings and recommends text contrast based on WCAG Standards

503 lines (361 loc) 13.3 kB
![contrastrast](https://github.com/ammuench/contrastrast/assets/2099658/8b7a90b0-3874-4650-a575-1170063d3462) [![JSR](https://jsr.io/badges/@amuench/contrastrast)](https://jsr.io/@amuench/contrastrast) [![npm version](https://badge.fury.io/js/contrastrast.svg)](https://badge.fury.io/js/contrastrast) # contrastrast A comprehensive TypeScript/Deno library for color manipulation, parsing, conversion, and accessibility analysis. Built with WCAG standards in mind, contrastrast helps you create accessible color combinations and analyze contrast ratios. **Features:** - 🎨 **Multi-format parsing**: Supports HEX, RGB, and HSL color formats - ♿ **WCAG compliance**: Built-in WCAG 2.1 contrast ratio calculations and compliance checking - 📊 **Color analysis**: Luminance, brightness, and accessibility calculations - 🔄 **Format conversion**: Convert between HEX, RGB, and HSL formats - ⚡ **TypeScript**: Full TypeScript support with comprehensive type definitions ## Installation Install `contrastrast` by running one of the following commands: ```bash npm install --save contrastrast yarn add contrastrast pnpm install --save contrastrast deno add jsr:@amuench/contrastrast ``` ## Quick Start ```typescript import { Contrastrast } from "contrastrast"; // Parse any color format, by default will throw an error if the color string is invalid const color = new Contrastrast("#1a73e8"); // Check if the color is light or dark console.log(color.isLight()); // false console.log(color.isDark()); // true // Get contrast ratio with another color const ratio = color.contrastRatio("#ffffff"); // 4.5 // Check WCAG compliance const meetsAA = color.meetsWCAG("#ffffff", "background", "AA"); // true // Convert between formats console.log(color.toHex()); // "#1a73e8" console.log(color.toRgbString()); // "rgb(26, 115, 232)" console.log(color.toHslString()); // "hsl(218, 80%, 51%)" ``` ## API Reference ### Types The library exports the following TypeScript types: #### Color Value Types ```typescript type RGBValues = { r: number; // Red (0-255) g: number; // Green (0-255) b: number; // Blue (0-255) }; type HSLValues = { h: number; // Hue (0-360) s: number; // Saturation (0-100) l: number; // Lightness (0-100) }; ``` #### Configuration Types ```typescript type ParseOptions = { throwOnError: boolean; // Whether to throw on invalid colors fallbackColor?: string; // Fallback color when throwOnError is false }; type ContrastOptions = { returnDetails?: boolean; // Return detailed WCAG analysis instead of just ratio }; ``` #### Result Types ```typescript type ContrastResult = { ratio: number; passes: { AA_NORMAL: boolean; // WCAG AA normal text (4.5:1) AA_LARGE: boolean; // WCAG AA large text (3:1) AAA_NORMAL: boolean; // WCAG AAA normal text (7:1) AAA_LARGE: boolean; // WCAG AAA large text (4.5:1) }; }; ``` #### WCAG Types ```typescript type WCAGContrastLevel = "AA" | "AAA"; type WCAGTextSize = "normal" | "large"; ``` ### Constructor and Factory Methods #### `new Contrastrast(colorString: string, parseOpts?: Partial<ParseOptions>)` Create a new Contrastrast instance from any supported color string. ```typescript const color1 = new Contrastrast("#ff0000"); const color2 = new Contrastrast("rgb(255, 0, 0)"); const color3 = new Contrastrast("hsl(0, 100%, 50%)"); // With error handling const safeColor = new Contrastrast("invalid-color", { throwOnError: false, fallbackColor: "#000000", }); ``` #### `Contrastrast.parse(colorString: string, parseOpts?: Partial<ParseOptions>): Contrastrast` Static method alias for the constructor. Also accepts the same `parseOpts` configuration object. ```typescript const color = Contrastrast.parse("#1a73e8"); // With error handling const safeColor = Contrastrast.parse("invalid-color", { throwOnError: false, fallbackColor: "#ffffff", }); ``` #### `Contrastrast.fromHex(hex: string): Contrastrast` Create from hex color (with or without #). Supports 3 and 6 digit codes. ```typescript const red1 = Contrastrast.fromHex("#ff0000"); const red2 = Contrastrast.fromHex("ff0000"); const shortRed = Contrastrast.fromHex("#f00"); ``` #### `Contrastrast.fromRgb(r: number, g: number, b: number): Contrastrast` / `Contrastrast.fromRgb(rgb: RGBValues): Contrastrast` Create from RGB values. ```typescript const red1 = Contrastrast.fromRgb(255, 0, 0); const red2 = Contrastrast.fromRgb({ r: 255, g: 0, b: 0 }); ``` #### `Contrastrast.fromHsl(h: number, s: number, l: number): Contrastrast` / `Contrastrast.fromHsl(hsl: HSLValues): Contrastrast` Create from HSL values. ```typescript const red1 = Contrastrast.fromHsl(0, 100, 50); const red2 = Contrastrast.fromHsl({ h: 0, s: 100, l: 50 }); ``` ### Color Format Conversion #### `toHex(includeHash?: boolean): string` Convert to hex format. ```typescript const color = new Contrastrast("rgb(255, 0, 0)"); console.log(color.toHex()); // "#ff0000" console.log(color.toHex(false)); // "ff0000" ``` #### `toRgb(): RGBValues` Get RGB values as an object. ```typescript const rgb = color.toRgb(); // { r: 255, g: 0, b: 0 } ``` #### `toRgbString(): string` Convert to RGB string format. ```typescript const rgbString = color.toRgbString(); // "rgb(255, 0, 0)" ``` #### `toHsl(): HSLValues` Get HSL values as an object. ```typescript const hsl = color.toHsl(); // { h: 0, s: 100, l: 50 } ``` #### `toHslString(): string` Convert to HSL string format. ```typescript const hslString = color.toHslString(); // "hsl(0, 100%, 50%)" ``` ### Color Analysis #### `luminance(): number` Calculate [WCAG 2.1 relative luminance](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html#dfn-relative-luminance) (0-1). ```typescript const black = new Contrastrast("#000000"); const white = new Contrastrast("#ffffff"); console.log(black.luminance()); // 0 console.log(white.luminance()); // 1 ``` #### `brightness(): number` Calculate perceived brightness using [AERT formula](https://www.w3.org/TR/AERT#color-contrast) (0-255). ```typescript const color = new Contrastrast("#1a73e8"); const brightness = color.brightness(); // ~102.4 ``` #### `isLight(): boolean` / `isDark(): boolean` Determine if color is light or dark based on [AERT brightness](https://www.w3.org/TR/AERT#color-contrast) threshold (124). ```typescript const lightColor = new Contrastrast("#ffffff"); const darkColor = new Contrastrast("#000000"); console.log(lightColor.isLight()); // true console.log(darkColor.isDark()); // true ``` ### Accessibility & Contrast #### `contrastRatio(color: Contrastrast | string): number` Calculate [WCAG 2.1 contrast ratio](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html#dfn-contrast-ratio) between colors. ```typescript const bgColor = new Contrastrast("#1a73e8"); const ratio = bgColor.contrastRatio("#ffffff"); // 4.5 ``` #### `textContrast(comparisonColor: Contrastrast | string, role?: "foreground" | "background", options?: ContrastOptions): number | ContrastResult` Calculate contrast with detailed [WCAG compliance analysis](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html) options. ```typescript // Simple ratio const ratio = bgColor.textContrast("#ffffff"); // 4.5 // Detailed analysis const result = bgColor.textContrast("#ffffff", "background", { returnDetails: true, }); // { // ratio: 4.5, // passes: { // AA_NORMAL: true, // AA_LARGE: true, // AAA_NORMAL: false, // AAA_LARGE: true // } // } ``` #### `meetsWCAG(comparisonColor: Contrastrast | string, role: "foreground" | "background", level: "AA" | "AAA", textSize?: "normal" | "large"): boolean` Check [WCAG compliance](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html) for specific requirements. ```typescript const bgColor = new Contrastrast("#1a73e8"); // Check different WCAG levels const meetsAA = bgColor.meetsWCAG("#ffffff", "background", "AA"); // true const meetsAAA = bgColor.meetsWCAG("#ffffff", "background", "AAA"); // false const meetsAALarge = bgColor.meetsWCAG("#ffffff", "background", "AA", "large"); // true ``` ### Utility Methods #### `equals(color: Contrastrast | string): boolean` Compare colors for equality. ```typescript const color1 = new Contrastrast("#ff0000"); const color2 = new Contrastrast("rgb(255, 0, 0)"); console.log(color1.equals(color2)); // true ``` ## ParseOptions Configuration Both the constructor and `parse` method accept optional configuration for error handling: ```typescript interface ParseOptions { throwOnError: boolean; // Whether to throw on invalid colors (default: true) fallbackColor?: string; // Fallback color when throwOnError is false (default: "#000000") } // Safe parsing with fallback const safeColor = Contrastrast.parse("invalid-color", { throwOnError: false, fallbackColor: "#333333", }); // Will throw on invalid color (default behavior) const strictColor = new Contrastrast("invalid-color"); // throws Error ``` ## Supported Color Formats ### HEX - `#ff0000` or `ff0000` - `#f00` or `f00` (short format) ### RGB - `rgb(255, 0, 0)` - `rgb(100, 200, 230)` ### HSL - `hsl(0, 100%, 50%)` - `hsl(217, 90%, 61%)` ## Real-World Examples ### React Component with Dynamic Text Color ```tsx import { Contrastrast } from "contrastrast"; interface ColorCardProps { backgroundColor: string; children: React.ReactNode; } const ColorCard: React.FC<ColorCardProps> = ({ backgroundColor, children }) => { const bgColor = new Contrastrast(backgroundColor); const textColor = bgColor.isLight() ? "#000000" : "#ffffff"; return ( <div style={{ backgroundColor, color: textColor }}> {children} </div> ); }; ``` ### WCAG Compliant Color Picker ```typescript import { Contrastrast } from "contrastrast"; function validateColorCombination(background: string, foreground: string): { isValid: boolean; level: string; ratio: number; } { const bgColor = new Contrastrast(background); const ratio = bgColor.contrastRatio(foreground); const meetsAAA = bgColor.meetsWCAG(foreground, "background", "AAA"); const meetsAA = bgColor.meetsWCAG(foreground, "background", "AA"); return { isValid: meetsAA, level: meetsAAA ? "AAA" : meetsAA ? "AA" : "FAIL", ratio, }; } const result = validateColorCombination("#1a73e8", "#ffffff"); console.log(result); // { isValid: true, level: "AA", ratio: 4.5 } ``` ## Standalone Utility Functions In addition to the Contrastrast class methods, contrastrast exports standalone utility functions for when you need to work with colors without creating class instances. ### `textContrast(foreground: Contrastrast | string, background: Contrastrast | string, options?: ContrastOptions): number | ContrastResult` Calculate contrast ratio between two colors with optional detailed [WCAG analysis](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html). ```typescript import { textContrast } from "contrastrast"; // Simple ratio calculation const ratio = textContrast("#000000", "#ffffff"); // 21 // Detailed WCAG analysis const analysis = textContrast("#1a73e8", "#ffffff", { returnDetails: true }); // { // ratio: 4.5, // passes: { // AA_NORMAL: true, // AA_LARGE: true, // AAA_NORMAL: false, // AAA_LARGE: true // } // } ``` ### `contrastRatio(color1: Contrastrast | string, color2: Contrastrast | string): number` Calculate the [WCAG 2.1 contrast ratio](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html#dfn-contrast-ratio) between any two colors. ```typescript import { contrastRatio } from "contrastrast"; const ratio1 = contrastRatio("#1a73e8", "#ffffff"); // 4.5 const ratio2 = contrastRatio("rgb(255, 0, 0)", "hsl(0, 0%, 100%)"); // 3.998 // Works with mixed formats const ratio3 = contrastRatio("#000", "rgb(255, 255, 255)"); // 21 ``` Both functions accept color strings in any supported format (HEX, RGB, HSL) or Contrastrast instances. ## Legacy API Support (v0.3.x) For backward compatibility, contrastrast still exports the legacy v0.3.x API, but these methods are deprecated and will be removed in v2.0. ### `textContrastForBGColor(bgColorString: string, options?: Partial<ContrastrastOptions>): "dark" | "light"` **⚠️ Deprecated** - Use `new Contrastrast(bgColor).isLight() ? "dark" : "light"` or the new class methods instead. ```typescript import { textContrastForBGColor } from "contrastrast"; // Legacy usage (deprecated) const textColor = textContrastForBGColor("#1a73e8"); // "light" // Recommended v1.0+ approach const bgColor = new Contrastrast("#1a73e8"); const textColor = bgColor.isLight() ? "dark" : "light"; // "light" ``` **Migration Guide:** - Replace `textContrastForBGColor(color)` with `new Contrastrast(color).isLight() ? "dark" : "light"` - For more sophisticated analysis, use the new WCAG-compliant methods like `meetsWCAG()` or `textContrast()` - The legacy `ContrastrastOptions` are replaced by `ParseOptions` for error handling ## Contributing Happy for any and all contributions. This project uses Deno for development with the following commands: - `deno test` - Run tests - `deno lint` - Lint code - `deno fmt` - Format code - `deno task build:npm` - Test building the NPM distribution Please note I prefer git commits formatted with [`gitmoji-cli`](https://github.com/carloscuesta/gitmoji-cli).