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 @@ [![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) -# 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; };