# `Color.Contrast`

Contrast and luminance computations.

* `relative_luminance/1` — the `Y` component of an sRGB color as
  defined by WCAG 2.x (applies the sRGB inverse companding then takes
  `0.2126·R + 0.7152·G + 0.0722·B`).

* `wcag_ratio/2` — WCAG 2.x contrast ratio,
  `(L1 + 0.05) / (L2 + 0.05)` where `L1 ≥ L2`. Result range
  `[1.0, 21.0]`.

* `wcag_level/2` — classifies a pair as `:aaa`, `:aaa_large`, `:aa`,
  `:aa_large`, or `:fail` for the common normal-text / large-text
  thresholds.

* `apca/2` — the Accessible Perceptual Contrast Algorithm (APCA
  W3 0.1.9), the successor to WCAG 2 proposed for CSS Color 5.
  Returns a signed `L_c` value roughly in `[-108, 106]`.

* `pick_contrasting/3` — given a background and two candidate
  foregrounds, pick the one with the higher contrast against the
  background.

All inputs may be anything accepted by `Color.new/1`.

# `apca`

```elixir
@spec apca(Color.input(), Color.input()) :: float()
```

Returns the APCA `L_c` contrast value between a text colour and a
background colour.

APCA is directional: swapping the arguments can change the
magnitude and sign of the result. Positive values mean the text is
darker than the background (normal polarity); negative values mean
lighter text on a darker background (reverse polarity).

Values are roughly in the range `[-108, 106]`. The rough thresholds
suggested by the APCA spec are:

* `|Lc| ≥ 90` — body text under the WCAG 3 "fluent" rating.

* `|Lc| ≥ 75` — body text, content.

* `|Lc| ≥ 60` — content, large headlines.

* `|Lc| ≥ 45` — large / bold text.

* `|Lc| ≥ 30` — non-text ornamental.

Reference: https://github.com/Myndex/apca-w3 (APCA-W3 0.1.9).

### Arguments

* `text` is the foreground colour — anything accepted by `Color.new/1`.

* `background` is the background colour.

### Returns

* A signed float.

### Examples

    iex> Float.round(Color.Contrast.apca("black", "white"), 1)
    106.0

    iex> Float.round(Color.Contrast.apca("white", "black"), 1)
    -107.9

# `pick_contrasting`

```elixir
@spec pick_contrasting(Color.input(), [Color.input()]) ::
  {:ok, Color.SRGB.t()} | {:error, Exception.t()}
```

Picks the candidate that gives the higher WCAG contrast against the
background.

### Arguments

* `background` is any color accepted by `Color.new/1`.

* `candidates` is a list of colors accepted by `Color.new/1`.
  Defaults to `["white", "black"]`.

### Returns

* `{:ok, color_struct}` where `color_struct` is the chosen candidate
  as a `Color.SRGB` struct.

### Examples

    iex> {:ok, chosen} = Color.Contrast.pick_contrasting("white")
    iex> Color.SRGB.to_hex(chosen)
    "#000000"

    iex> {:ok, chosen} = Color.Contrast.pick_contrasting("#333")
    iex> Color.SRGB.to_hex(chosen)
    "#ffffff"

    iex> {:ok, chosen} = Color.Contrast.pick_contrasting("red", ["yellow", "darkblue"])
    iex> Color.SRGB.to_hex(chosen)
    "#00008b"

# `relative_luminance`

```elixir
@spec relative_luminance(Color.input()) :: float()
```

Returns the WCAG 2.x relative luminance of a color — the `Y`
component of its linear sRGB, on the `[0, 1]` scale.

### Arguments

* `color` is anything accepted by `Color.new/1`.

### Returns

* A float in `[0, 1]`.

### Examples

    iex> Color.Contrast.relative_luminance("white")
    1.0

    iex> Color.Contrast.relative_luminance("black")
    0.0

    iex> Float.round(Color.Contrast.relative_luminance("red"), 4)
    0.2126

# `wcag_level`

```elixir
@spec wcag_level(Color.input(), Color.input()) ::
  :aaa | :aaa_large | :aa_large | :fail
```

Classifies a WCAG contrast ratio into the accessibility grade it
meets for normal and large text.

The thresholds are:

* **AAA** — `7.0` normal text, `4.5` large text.

* **AA** — `4.5` normal text, `3.0` large text.

### Arguments

* `a` is any color accepted by `Color.new/1`.

* `b` is any color accepted by `Color.new/1`.

### Returns

* One of `:aaa`, `:aaa_large`, `:aa`, `:aa_large`, `:fail`.

### Examples

    iex> Color.Contrast.wcag_level("black", "white")
    :aaa

    iex> Color.Contrast.wcag_level("#777", "white")
    :aa_large

# `wcag_ratio`

```elixir
@spec wcag_ratio(Color.input(), Color.input()) :: float()
```

Returns the WCAG 2.x contrast ratio between two colors.

### Arguments

* `a` is any color accepted by `Color.new/1`.

* `b` is any color accepted by `Color.new/1`.

### Returns

* A float in `[1.0, 21.0]`. `1.0` means no contrast; `21.0` is the
  maximum (black on white or white on black).

### Examples

    iex> Float.round(Color.Contrast.wcag_ratio("white", "black"), 2)
    21.0

    iex> Float.round(Color.Contrast.wcag_ratio("#777", "white"), 2)
    4.48

---

*Consult [api-reference.md](api-reference.md) for complete listing*
