diff --git a/.github/workflows/pr-validation.yml b/.github/workflows/pr-validation.yml
new file mode 100644
index 0000000..0568a5b
--- /dev/null
+++ b/.github/workflows/pr-validation.yml
@@ -0,0 +1,41 @@
+name: PR Validation
+
+on:
+ pull_request:
+ branches: [main]
+ workflow_dispatch:
+
+jobs:
+ build-validation:
+ name: Build Validation
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Deno
+ uses: denoland/setup-deno@v1
+ with:
+ deno-version: v2.x
+
+ - name: Build NPM package
+ run: deno run -A scripts/build_npm.ts
+
+ - name: Verify NPM package structure
+ run: |
+ test -f npm/package.json
+ test -d npm/esm
+ test -d npm/script
+
+ - name: Test NPM package can be imported
+ working-directory: npm
+ run: |
+ npm install
+ node -e "const contrastrast = require('./script/mod.js'); console.log('NPM package import successful')"
+
+ - name: Upload build artifacts
+ uses: actions/upload-artifact@v3
+ with:
+ name: npm-package
+ path: npm/
+ retention-days: 7
diff --git a/.github/workflows/publish-jsr.yml b/.github/workflows/publish-jsr.yml
new file mode 100644
index 0000000..e81c44b
--- /dev/null
+++ b/.github/workflows/publish-jsr.yml
@@ -0,0 +1,84 @@
+name: Publish to JSR
+
+on:
+ push:
+ tags: ["v*"]
+ workflow_dispatch:
+ inputs:
+ tag:
+ description: "Tag to publish (e.g., v1.0.0)"
+ required: true
+ type: string
+
+jobs:
+ validate:
+ name: Pre-publish Validation
+ runs-on: ubuntu-latest
+ outputs:
+ version: ${{ steps.version.outputs.version }}
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Deno
+ uses: denoland/setup-deno@v1
+ with:
+ deno-version: v2.x
+
+ - name: Extract version from tag
+ id: version
+ run: |
+ if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
+ VERSION="${{ github.event.inputs.tag }}"
+ else
+ VERSION="${GITHUB_REF#refs/tags/}"
+ fi
+ echo "version=${VERSION}" >> $GITHUB_OUTPUT
+ echo "Publishing version: ${VERSION}"
+
+ - name: Verify version in deno.json matches tag
+ run: |
+ DENO_VERSION=$(jq -r '.version' deno.json)
+ TAG_VERSION="${{ steps.version.outputs.version }}"
+ TAG_VERSION_CLEAN="${TAG_VERSION#v}"
+
+ if [ "$DENO_VERSION" != "$TAG_VERSION_CLEAN" ]; then
+ echo "Error: Version mismatch!"
+ echo "deno.json version: $DENO_VERSION"
+ echo "Tag version: $TAG_VERSION_CLEAN"
+ exit 1
+ fi
+
+ - name: Run quality checks
+ run: |
+ deno fmt --check
+ deno lint
+ deno test
+
+ publish:
+ name: Publish to JSR
+ runs-on: ubuntu-latest
+ needs: validate
+ environment: publishing
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Deno
+ uses: denoland/setup-deno@v1
+ with:
+ deno-version: v2.x
+
+ - name: Publish to JSR
+ env:
+ JSR_TOKEN: ${{ secrets.JSR_TOKEN }}
+ run: deno publish
+
+ - name: Create GitHub Release
+ uses: softprops/action-gh-release@v1
+ with:
+ tag_name: ${{ needs.validate.outputs.version }}
+ name: Release ${{ needs.validate.outputs.version }}
+ generate_release_notes: true
+ draft: false
+ prerelease: ${{ contains(needs.validate.outputs.version, '-') }}
diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml
new file mode 100644
index 0000000..334d549
--- /dev/null
+++ b/.github/workflows/publish-npm.yml
@@ -0,0 +1,122 @@
+name: Publish to NPM
+
+on:
+ push:
+ tags: ["v*"]
+ workflow_dispatch:
+ inputs:
+ tag:
+ description: "Tag to publish (e.g., v1.0.0)"
+ required: true
+ type: string
+
+jobs:
+ validate:
+ name: Pre-publish Validation
+ runs-on: ubuntu-latest
+ outputs:
+ version: ${{ steps.version.outputs.version }}
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Deno
+ uses: denoland/setup-deno@v1
+ with:
+ deno-version: v2.x
+
+ - name: Extract version from tag
+ id: version
+ run: |
+ if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
+ VERSION="${{ github.event.inputs.tag }}"
+ else
+ VERSION="${GITHUB_REF#refs/tags/}"
+ fi
+ echo "version=${VERSION}" >> $GITHUB_OUTPUT
+ echo "Publishing version: ${VERSION}"
+
+ - name: Verify version in deno.json matches tag
+ run: |
+ DENO_VERSION=$(jq -r '.version' deno.json)
+ TAG_VERSION="${{ steps.version.outputs.version }}"
+ TAG_VERSION_CLEAN="${TAG_VERSION#v}"
+
+ if [ "$DENO_VERSION" != "$TAG_VERSION_CLEAN" ]; then
+ echo "Error: Version mismatch!"
+ echo "deno.json version: $DENO_VERSION"
+ echo "Tag version: $TAG_VERSION_CLEAN"
+ exit 1
+ fi
+
+ - name: Run quality checks
+ run: |
+ deno fmt --check
+ deno lint
+ deno test
+
+ build-and-publish:
+ name: Build and Publish to NPM
+ runs-on: ubuntu-latest
+ needs: validate
+ environment: publishing
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Deno
+ uses: denoland/setup-deno@v1
+ with:
+ deno-version: v2.x
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v3
+ with:
+ node-version: "18"
+ registry-url: "https://registry.npmjs.org"
+
+ - name: Build NPM package with dnt
+ run: deno run -A scripts/build_npm.ts
+
+ - name: Verify NPM package version matches tag
+ run: |
+ NPM_VERSION=$(jq -r '.version' npm/package.json)
+ TAG_VERSION="${{ needs.validate.outputs.version }}"
+ TAG_VERSION_CLEAN="${TAG_VERSION#v}"
+
+ if [ "$NPM_VERSION" != "$TAG_VERSION_CLEAN" ]; then
+ echo "Error: NPM package version mismatch!"
+ echo "npm/package.json version: $NPM_VERSION"
+ echo "Tag version: $TAG_VERSION_CLEAN"
+ exit 1
+ fi
+
+ - name: Verify package structure
+ run: |
+ test -f npm/package.json
+ test -d npm/esm
+ test -d npm/script
+ test -f npm/LICENSE
+ test -f npm/README.md
+
+ - name: Test NPM package imports
+ working-directory: npm
+ run: |
+ # Test CommonJS import
+ node -e "const contrastrast = require('./script/mod.js'); console.log('CommonJS import successful:', typeof contrastrast);"
+
+ # Test ESM import
+ node -e "import('./esm/mod.js').then(m => console.log('ESM import successful:', typeof m.default || typeof m));"
+
+ - name: Publish to NPM
+ working-directory: npm
+ env:
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
+ run: |
+ if [[ "${{ needs.validate.outputs.version }}" == *"-"* ]]; then
+ echo "Publishing non-stable release with beta tag"
+ npm publish --tag beta
+ else
+ echo "Publishing stable release"
+ npm publish
+ fi
diff --git a/.github/workflows/quality-checks.yml b/.github/workflows/quality-checks.yml
new file mode 100644
index 0000000..449bb5d
--- /dev/null
+++ b/.github/workflows/quality-checks.yml
@@ -0,0 +1,52 @@
+name: Quality Checks
+
+on:
+ pull_request:
+ workflow_dispatch:
+
+jobs:
+ format:
+ name: Format Check
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Deno
+ uses: denoland/setup-deno@v1
+ with:
+ deno-version: v2.x
+
+ - name: Check formatting
+ run: deno fmt --check
+
+ lint:
+ name: Lint
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Deno
+ uses: denoland/setup-deno@v1
+ with:
+ deno-version: v2.x
+
+ - name: Run linter
+ run: deno lint
+
+ test:
+ name: Test w/ Deno
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Deno
+ uses: denoland/setup-deno@v1
+ with:
+ deno-version: v2.x
+
+ - name: Run tests
+ run: deno test
diff --git a/README.md b/README.md
index 2798894..307ccdc 100644
--- a/README.md
+++ b/README.md
@@ -3,115 +3,500 @@
[](https://jsr.io/@amuench/contrastrast)
[](https://badge.fury.io/js/contrastrast)
-# constrastrast
+# contrastrast
-A lightweight tool that parses color strings and recommends text contrast based
-on [WCAG Standards](http://www.w3.org/TR/AERT#color-contrast)
+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 `constrastrast` by running one of the following commands:
+Install `contrastrast` by running one of the following commands:
```bash
-npm install --save constrastrast
+npm install --save contrastrast
-yarn add constrastrast
+yarn add contrastrast
-pnpm install --save constrastrast
+pnpm install --save contrastrast
-deno add contrastrast
+deno add jsr:@amuench/contrastrast
```
-## How it works
+## Quick Start
-`constrastrast` takes a given background color as a string in either HEX, HSL,
-or RGB format, and (by default) returns `"dark"` or `"light"` as a recommended
-text variant for that given background color
+```typescript
+import { Contrastrast } from "contrastrast";
-For example, you may use it like this:
+// Parse any color format, by default will throw an error if the color string is invalid
+const color = new Contrastrast("#1a73e8");
-```tsx
-import { textContrastForBGColor } from "contrastrast";
+// Check if the color is light or dark
+console.log(color.isLight()); // false
+console.log(color.isDark()); // true
-const MyColorChangingComponent = (backgroundColor: string) => {
- return
- This text is readable no matter what the background color is!
-
-}
+// 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%)"
```
-## Supported Color Formats
+## API Reference
-`constrastrast` supports the following color string formats:
+### Types
-### HEX
+The library exports the following TypeScript types:
-HEX Notation in either 3 or 6 length format
+#### Color Value Types
-**examples**
+```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)
+};
```
-#ad1232
-ad1232
+#### Configuration Types
-#ada
+```typescript
+type ParseOptions = {
+ throwOnError: boolean; // Whether to throw on invalid colors
+ fallbackColor?: string; // Fallback color when throwOnError is false
+};
-ada
+type ContrastOptions = {
+ returnDetails?: boolean; // Return detailed WCAG analysis instead of just ratio
+};
```
-### RGB
+#### 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)
+ };
+};
+```
-Standard RGB notation
+#### WCAG Types
-**examples**
+```typescript
+type WCAGContrastLevel = "AA" | "AAA";
+type WCAGTextSize = "normal" | "large";
+```
+
+### Constructor and Factory Methods
+
+#### `new Contrastrast(colorString: string, parseOpts?: Partial)`
+
+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",
+});
```
-rgb(100,200, 230)
-rgb(5, 30, 40)
+#### `Contrastrast.parse(colorString: string, parseOpts?: Partial): 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",
+});
```
-### HSL
+#### `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`
-HSL Notation with or without the symbol markers
+Determine if color is light or dark based on
+[AERT brightness](https://www.w3.org/TR/AERT#color-contrast) threshold (124).
-**examples**
+```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
```
-hsl(217°, 90%, 61%)
-hsl(72°, 90%, 61%)
+#### `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:
-hsl(121deg, 90%, 61%)
+```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",
+});
-hsl(298, 90, 61)
+// Will throw on invalid color (default behavior)
+const strictColor = new Contrastrast("invalid-color"); // throws Error
```
-### Alpha Formats
+## Supported Color Formats
+
+### HEX
-Currently `contrastrast` doesn't support alpha formats and will log an error and
-return the default value
+- `#ff0000` or `ff0000`
+- `#f00` or `f00` (short format)
-### Unhandled Formats
+### RGB
+
+- `rgb(255, 0, 0)`
+- `rgb(100, 200, 230)`
+
+### HSL
-If an unhandled string is passed, by default `contrastrast` will log an error
-and return the default value (`"dark"`)
+- `hsl(0, 100%, 50%)`
+- `hsl(217, 90%, 61%)`
-## Options
+## Real-World Examples
-`textContrastForBGColor` takes an `ContrastrastOptions` object as an optional
-second parameter, it currently has the following configuration options:
+### React Component with Dynamic Text Color
-```ts
-type ContrastrastOptions = {
- fallbackOption?: "dark" | "light"; // Defaults to "dark" if not specified
- throwErrorOnUnhandled?: boolean; // Throws an error instead of returning the `fallbackOption`. Defaults to `false` if not specific
+```tsx
+import { Contrastrast } from "contrastrast";
+
+interface ColorCardProps {
+ backgroundColor: string;
+ children: React.ReactNode;
+}
+
+const ColorCard: React.FC = ({ backgroundColor, children }) => {
+ const bgColor = new Contrastrast(backgroundColor);
+ const textColor = bgColor.isLight() ? "#000000" : "#ffffff";
+
+ return (
+
+ {children}
+
+ );
};
```
+### 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): "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. Please note the project uses `pnpm` and I
-prefer to have git commits formatted with
-[`gitmoji-cli`](https://github.com/carloscuesta/gitmoji-cli)
+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).
diff --git a/types/Colors.types.ts b/types/Colors.types.ts
index f1edb50..f527d00 100644
--- a/types/Colors.types.ts
+++ b/types/Colors.types.ts
@@ -1,11 +1,23 @@
+/**
+ * RGB color values
+ */
export type RGBValues = {
+ /** Red component (0-255) */
r: number;
+ /** Green component (0-255) */
g: number;
+ /** Blue component (0-255) */
b: number;
};
+/**
+ * HSL color values
+ */
export type HSLValues = {
+ /** Hue in degrees (0-360) */
h: number;
+ /** Saturation percentage (0-100) */
s: number;
+ /** Lightness percentage (0-100) */
l: number;
};
diff --git a/types/ParseOptions.types.ts b/types/ParseOptions.types.ts
index ec72e55..2fb6154 100644
--- a/types/ParseOptions.types.ts
+++ b/types/ParseOptions.types.ts
@@ -1,4 +1,9 @@
+/**
+ * Configuration options for parsing color strings
+ */
export type ParseOptions = {
+ /** Whether to throw an error on invalid color strings (default: true) */
throwOnError: boolean;
+ /** Fallback color to use when throwOnError is false (default: "#000000") */
fallbackColor?: string;
};
diff --git a/types/WCAG.types.ts b/types/WCAG.types.ts
index ff934e6..8321f80 100644
--- a/types/WCAG.types.ts
+++ b/types/WCAG.types.ts
@@ -1,3 +1,9 @@
+/**
+ * WCAG contrast compliance levels
+ */
export type WCAGContrastLevel = "AA" | "AAA";
+/**
+ * WCAG text size categories for contrast requirements
+ */
export type WCAGTextSize = "normal" | "large";
diff --git a/utils/textContrast.ts b/utils/textContrast.ts
index 5bfc860..eece02e 100644
--- a/utils/textContrast.ts
+++ b/utils/textContrast.ts
@@ -2,17 +2,30 @@ import type { Contrastrast } from "../contrastrast.ts";
import { WCAG_LEVELS } from "../constants.ts";
import { contrastRatio } from "./contrastRatio.ts";
+/**
+ * Detailed contrast analysis result with WCAG compliance information
+ */
export type ContrastResult = {
+ /** Contrast ratio (1:1 to 21:1) */
ratio: number;
+ /** WCAG compliance test results */
passes: {
+ /** WCAG AA compliance for normal text (4.5:1 threshold) */
AA_NORMAL: boolean;
+ /** WCAG AA compliance for large text (3:1 threshold) */
AA_LARGE: boolean;
+ /** WCAG AAA compliance for normal text (7:1 threshold) */
AAA_NORMAL: boolean;
+ /** WCAG AAA compliance for large text (4.5:1 threshold) */
AAA_LARGE: boolean;
};
};
+/**
+ * Options for contrast analysis functions
+ */
export type ContrastOptions = {
+ /** Return detailed WCAG analysis instead of just the ratio (default: false) */
returnDetails?: boolean;
};