Skip to content

ammuench/contrastrast

Repository files navigation

contrastrast

JSR npm version

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:

npm install --save contrastrast

yarn add contrastrast

pnpm install --save contrastrast

deno add jsr:@amuench/contrastrast

Quick Start

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

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

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

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

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.

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.

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.

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.

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.

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.

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.

const rgb = color.toRgb(); // { r: 255, g: 0, b: 0 }

toRgbString(): string

Convert to RGB string format.

const rgbString = color.toRgbString(); // "rgb(255, 0, 0)"

toHsl(): HSLValues

Get HSL values as an object.

const hsl = color.toHsl(); // { h: 0, s: 100, l: 50 }

toHslString(): string

Convert to HSL string format.

const hslString = color.toHslString(); // "hsl(0, 100%, 50%)"

Color Analysis

luminance(): number

Calculate WCAG 2.1 relative luminance (0-1).

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 (0-255).

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 threshold (124).

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 between colors.

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 options.

// 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 for specific requirements.

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.

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:

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

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

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.

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 between any two colors.

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.

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.

About

Comprehensive color manipulation library with WCAG compliance, parsing, and format conversion

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published