Files
2026-05-27 23:29:53 +01:00

6.0 KiB

Capability Spec: colours

Package: @suaveplan/colours Status: STABLE

Every requirement below carries a **Verified by:** line at authoring time.

ADDED Requirements

Requirement: Stable error code registry via defineCodes

The package SHALL declare a single defineCodes("COLOUR", [...]) registry named COLOUR_CODES. No hand-typed string literals for codes SHALL appear in src/ or tests.

Scenario: Code values use COLOUR.* namespace

  • WHEN reading COLOUR_CODES.INVALID_INPUT
  • THEN the value is exactly "COLOUR.INVALID_INPUT"

Verified by: src/error-codes/error-codes.test.ts::"code values use COLOUR.* namespace"


Requirement: Typed error hierarchy extending SuaveplanError

The package SHALL export ColourError extending SuaveplanError<ColourCode> with typed factory functions for every failure mode.

Scenario: Factory functions produce correctly-coded errors

  • WHEN calling colourInvalid("bad hex")
  • THEN the result has code COLOUR.INVALID_INPUT and message context

Verified by: src/errors/errors.test.ts::"factory functions produce correctly-coded errors"


Requirement: Colour parsing from multiple formats

The package SHALL parse colour input from hex (3/4/6/8 digit), modern CSS rgb(), hsl(), lab(), lch(), oklab(), oklch(), named colours, and plain RGB/RGBA/HSL/HSLA/HSV/LAB/LCH/OKLAB/OKLCH objects.

Scenario: Parse 6-digit hex

  • WHEN parsing "#ff9900"
  • THEN the result is { r: 255, g: 153, b: 0, a: 1 }

Scenario: Parse modern space-separated rgb with alpha

  • WHEN parsing "rgb(255 128 0 / 0.5)"
  • THEN the result alpha is 0.5

Scenario: Parse named colour

  • WHEN parsing "transparent"
  • THEN the result is { r: 0, g: 0, b: 0, a: 0 }

Scenario: Reject invalid input

  • WHEN parsing "not-a-colour"
  • THEN it throws ColourError with code COLOUR.INVALID_INPUT

Verified by: src/parser/parser.test.ts::"parses hex" AND src/parser/parser.test.ts::"parses modern rgb with alpha" AND src/parser/parser.test.ts::"parses named colours" AND src/parser/parser.test.ts::"rejects invalid input"


Requirement: Colour space conversions

The package SHALL provide bidirectional conversions between RGB, HSL, HSV, XYZ, LAB, LCH, OKLAB, OKLCH, and CMYK.

Scenario: RGB to HSL round-trip

  • WHEN converting { r: 255, g: 128, b: 0 } to HSL and back
  • THEN the result is approximately the original RGB values

Scenario: LAB to LCH preserves chroma and hue

  • WHEN converting { l: 50, a: 20, b: -30 } to LCH
  • THEN c and h are positive finite numbers

Verified by: src/conversions/conversions.test.ts::"rgb to hsl round-trip" AND src/conversions/conversions.test.ts::"lab to lch preserves chroma and hue"


Requirement: Chainable Colour class manipulation

The package SHALL provide a Colour class with immutable chainable methods: lighten, darken, saturate, desaturate, rotate, alpha, fade, opaquer, negate, grayscale, and mix.

Scenario: Lighten and darken are inverse

  • WHEN colour.lighten(20).darken(20)
  • THEN the result approximately equals the original

Scenario: Mix in oklab space

  • WHEN mixing red and blue with weight 50 in oklab
  • THEN the result is a perceptually uniform mid-tone

Verified by: src/colour/colour.test.ts::"lighten and darken are inverse" AND src/colour/colour.test.ts::"mix in oklab space"


Requirement: Colour analysis and comparison

The package SHALL compute luminance (WCAG), perceived brightness, contrast ratio, isDark/isLight, bestContrast selection, and distance in euclidean/CIE76/CIE94/oklab spaces.

Scenario: Contrast ratio between black and white

  • WHEN computing contrast between black and white
  • THEN the result is 21

Scenario: bestContrast selects highest ratio

  • WHEN calling bestContrast with black and white against red
  • THEN the result is the colour with the highest contrast ratio

Verified by: src/colour/colour.test.ts::"contrast ratio between black and white" AND src/colour/colour.test.ts::"bestContrast selects highest ratio"


Requirement: Output formatters

The package SHALL format colours to hex (3/6/8), rgb, rgba, hsl, hsla, hsv, lab, lch, oklab, oklch, cmyk, and named.

Scenario: Hex round-trip

  • WHEN formatting a parsed hex colour back to hex
  • THEN the result matches the canonical 6-digit form

Verified by: src/colour/colour.test.ts::"hex round-trip"


Requirement: Colour factories

The package SHALL provide randomColour, colourFromTemperature, blendColours, colourSteps, and generatePalette.

Scenario: Steps produces correct count

  • WHEN calling colourSteps('#ff0000', '#0000ff', 5)
  • THEN the result has exactly 5 colours

Verified by: src/colour-factories/colour-factories.test.ts::"steps produces correct count"


Requirement: Zod schema validation on public boundaries

The package SHALL validate ColourInput via Zod schemas at public entrypoints. Invalid input SHALL throw COLOUR_CODES.INVALID_INPUT.

Scenario: Zod rejects non-finite channel values

  • WHEN constructing new Colour({ r: NaN, g: 0, b: 0 })
  • THEN it throws ColourError with code COLOUR.INVALID_INPUT

Verified by: src/parser/parser.test.ts::"rejects NaN channel values"


Requirement: Subpath exports and sideEffects false

The package SHALL declare sideEffects: false and provide subpath exports for ./types/colour-types, ./errors/errors, ./error-codes/error-codes, ./parser/parser, ./conversions/conversions, ./colour/colour, and ./colour-factories/colour-factories.

Scenario: Tree-shake test passes

  • WHEN bundling with only parseColour imported
  • THEN Colour class code is not included in the output

Verified by: tests/tree-shake.test.ts::"tree-shakes unused exports"