Media

Colour contrast

Colours in any CSS form, contrast checked against WCAG AA and AAA, readable text on any colour, and mixes.

Version 1.0.0 · 4 functions · 1 crate

#colours#accessibility#wcag#themes

Functions

What use color.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use contrast, text_on from color (Quillo 0.6).

contrast(fg: str, bg: str) → anyor an error

The contrast between two colours ("#1b1630", "rgb(255 255 255)", "hsl(40 90% 60%)", "teal"), 1 to 21, and whether it passes: { ratio, aa, aa_large, aaa }.

text_on(bg: str) → stror an error

Black or white, whichever reads better on bg: text on a user-picked label colour.

color_forms(c: str) → anyor an error

{ hex, rgb, hsl } of a colour written any CSS way.

mix(a: str, b: str, t: float) → stror an error

a mixed with b, t of the way (0 is a, 1 is b), as hex.

Get it

$ quillo install color               # color.qwer into this folder$ quillo install color --example     # and its example, color.qwe

Or with curl:

$ curl -O https://quillolang.org/plugins/files/color.qwer$ curl -O https://quillolang.org/plugins/files/color.qwe

Then, in your program, all of it or only what you call:

main.qwe
use color.qweruse contrast, text_on from color      // Quillo 0.6: only these

It uses this crate, which cargo fetches the first time the plugin is built:

[dependencies]
csscolorparser = "0.7"

Example

color.qwe
// Checks a theme's colours, and picks the text colour for each tag.////   quillo run color.qwe      then open http://localhost:8080/check?fg=%23f3b54a&bg=%230b0a12use "color.qwer"route GET /check ? { fg: str, bg: str } = contrast(fg, bg)?route GET /tags = ["#f3b54a", "navy", "hsl(140 60% 45%)"] | map((c) => { colour: color_forms(c)?.hex, text: text_on(c)? })route GET /mix = { halfway: mix("#000000", "#ffffff", 0.5)? }
$ quillo run color.qwe$ curl 'localhost:8080/check?fg=%23f3b54a&bg=%230b0a12'$ curl 'localhost:8080/tags'$ curl 'localhost:8080/mix'

Source

color.qwer
//! Colours read from any CSS form, contrast checked against WCAG (AA, AAA), and mixed: accessible themes and user-picked colours.//! version: 1.0.0//!//! [dependencies]//! csscolorparser = "0.7"use serde_json::{json, Value};fn parse(c: &str) -> Result<csscolorparser::Color, String> {    c.trim().parse::<csscolorparser::Color>().map_err(|e| format!("`{}`: {}", c, e))}fn luminance(c: &csscolorparser::Color) -> f64 {    let ch = |v: f32| { let v = v as f64; if v <= 0.04045 { v / 12.92 } else { ((v + 0.055) / 1.055).powf(2.4) } };    0.2126 * ch(c.r) + 0.7152 * ch(c.g) + 0.0722 * ch(c.b)}/// The contrast between two colours ("#1b1630", "rgb(255 255 255)", "hsl(40 90% 60%)", "teal"), 1 to 21, and whether it passes: `{ ratio, aa, aa_large, aaa }`.pub fn contrast(fg: &str, bg: &str) -> Result<Value, String> {    let (a, b) = (luminance(&parse(fg)?), luminance(&parse(bg)?));    let r = (a.max(b) + 0.05) / (a.min(b) + 0.05);    Ok(json!({ "ratio": (r * 100.0).round() / 100.0, "aa": r >= 4.5, "aa_large": r >= 3.0, "aaa": r >= 7.0 }))}/// Black or white, whichever reads better on `bg`: text on a user-picked label colour.pub fn text_on(bg: &str) -> Result<String, String> {    let l = luminance(&parse(bg)?);    Ok(if (1.05) / (l + 0.05) >= (l + 0.05) / 0.05 { "#ffffff".into() } else { "#000000".into() })}/// `{ hex, rgb, hsl }` of a colour written any CSS way.pub fn color_forms(c: &str) -> Result<Value, String> {    let c = parse(c)?;    Ok(json!({ "hex": c.to_css_hex(), "rgb": c.to_css_rgb(), "hsl": c.to_css_hsl() }))}/// `a` mixed with `b`, `t` of the way (0 is `a`, 1 is `b`), as hex.pub fn mix(a: &str, b: &str, t: f64) -> Result<String, String> {    Ok(parse(a)?.interpolate_rgb(&parse(b)?, t.clamp(0.0, 1.0) as f32).to_css_hex())}
Thread
66Useful? Like it, or add to its thread below.

Thread 0 posts

On Colour contrast: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.