# `Colour`

A colour, written `#1e6fd9`, or `#1e6fd980` when it isn't fully opaque.

Use it for brand colours and themes. `colour.contrast` tells you whether two are readable together, and `colour.mix` blends them.

## Shape

| Field | Type | Required? |
|---|---|---|
| `red` | `Int` | Yes |
| `green` | `Int` | Yes |
| `blue` | `Int` | Yes |
| `alpha` | `Int` | Yes |

## Example

```jadpo
Colour { red: 30 green: 111 blue: 217 alpha: 255 }
```

`text.parse(raw, as: Colour)` reads one from text that comes from outside. Text that isn't one at all fails `malformed`, such as `1e6fd9`, `#1e6fd` or `#1e6fd9f`.

These operations give one: `colour.from_hsl`, `colour.from_oklch`, `colour.mix` and `colour.with_lightness`.

## Good to know

- Each part runs from 0 to 255. `alpha` of 255 is fully opaque.
- It is lower case with a `#`, in sRGB.
- `==` tells you whether two are the same. `<` and `list.sort_by` don't take it, so sort by one of its parts instead.

## In JSON

A string, in a request, a response or `json.write`:

```json
"#000000"
"#ffffff"
"#1e6fd9"
```

## Details

<details>
<summary>Text form, storage, how it is shown, and operations</summary>

`Colour` is in the group Media and visual, and has been in Jadpo since 0.1.0.

### Text form

`text.from` writes one standard text form, the same form `text.parse` reads back: `#000000`, `#ffffff` and `#1e6fd9`.

### How it is stored

It is stored in 4 columns, each named for the field, `_` and its suffix. This is `#1e6fd9`:

| Column | Type | Holds |
|---|---|---|
| `<field>_red` | `smallint` | `30` |
| `<field>_green` | `smallint` | `111` |
| `<field>_blue` | `smallint` | `217` |
| `<field>_alpha` | `smallint` | `255` |

### How it is shown to people

`text.format(value, locale:)` shows it to people in their locale.

With `locale: Locale.en_gb`:

| Value | Shown |
|---|---|
| `#000000` | `#000000` |
| `#ffffff` | `#ffffff` |
| `#1e6fd9` | `#1e6fd9` |
| `#1e6fd980` | `#1e6fd980` |
| `#00000000` | `#00000000` |

### Operations

- `colour.contrast(a: Colour, b: Colour) -> Decimal`: WCAG 2's contrast ratio between `a` and `b`, from 1.00 to 21.00, rounded down to 2 places, so `>= 4.5` never passes a ratio below it. Alpha isn't read: WCAG 2 defines contrast for opaque colours.
- `colour.from_hsl(hue: Angle, saturation: Percentage, lightness: Percentage) -> Colour`: The colour of a CSS Color 4 HSL hue, saturation and lightness, the percentages clamped to 0% to 100%: `colour.from_hsl(120deg, 100%, 25%)` is `#008000`.
- `colour.from_oklch(lightness: Percentage, chroma: Decimal, hue: Angle) -> Colour`: The colour of an OKLCH lightness, chroma and hue, clipped into sRGB, the lightness clamped to 0% to 100% and the chroma to 0 or more: `colour.from_oklch(62.8%, 0.2577, 29.23deg)` is `#ff0000`.
- `colour.mix(a: Colour, b: Colour, ratio: Percentage) -> Colour`: `a` and `b` mixed in OKLCH as CSS's `color-mix(in oklch, a, b ratio)` mixes them: `ratio` of `b`, the rest of `a`, the hue the shorter way round, with their alphas.
- `colour.with_lightness(value: Colour, lightness: Percentage) -> Colour`: `value` with its OKLCH lightness set to `lightness`, its chroma, hue and alpha kept, clipped into sRGB.

It also takes `text.parse`, `text.from`, `text.format`, `json.read`, `json.write` and `==`. [How every standard type behaves](/docs/every-standard-type-meets-one-contract.md) says how they work.

</details>
