contrastrast
Version:
A lightweight tool that parses color strings and recommends text contrast based on WCAG Standards
503 lines (361 loc) • 13.3 kB
Markdown

[](https://jsr.io/@amuench/contrastrast)
[](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).