6.0 KiB
Capability Spec Delta: colours (2026-05-20-colours)
Package: @suaveplan/colours
Status: PROPOSED
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_INPUTand 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
ColourErrorwith codeCOLOUR.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
candhare 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
bestContrastwith 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
ColourErrorwith codeCOLOUR.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
parseColourimported - THEN
Colourclass code is not included in the output
Verified by: tests/tree-shake.test.ts::"tree-shakes unused exports"