Media

Colour names

The nearest of the 148 CSS colour names to any colour, names back to hex, and the full list: for labels, filters and alt text.

Version 1.0.0 · 4 functions · 0 crates

#colors#search#accessibility

Functions

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

color_name(color: str) → anyor an error

The CSS colour name nearest to color ("#3b82f6", "#abc", "rgb(59, 130, 246)" or a name): { name, hex, distance, exact }. distance is how far apart they look (Oklab, 0 the same, under 0.02 hard to tell, over 0.1 clearly different); exact true when it is that colour.

color_name_nearest(color: str, count: int) → listor an error

The count nearest names (1 to 20), nearest first, each { name, hex, distance }: to offer a choice, or a search by colour.

color_name_hex(name: str) → stror an error

A CSS colour name ("rebeccapurple", "Light Sea Green" with spaces and capitals too) as "#rrggbb"; an error for a name CSS does not have.

color_name_list() → list

All 148 CSS colour names with their hex, as { name, hex } in alphabetical order (gray and grey spellings both).

Get it

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

Or with curl:

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

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

main.qwe
use color_names.qweruse color_name, color_name_nearest from color_names      // Quillo 0.6: only these

It uses these crates, which cargo fetches the first time the plugin is built:

[dependencies]

Example

color_names.qwe
// A shop's colour filter: each product colour gets a name people can search for and screen readers can say.////   quillo run color_names.qwe      then open http://localhost:8080/productsuse "color_names.qwer"let products = [{ name: "Mug", color: "#3b82f6" }, { name: "Scarf", color: "#b91c1c" }, { name: "Tote", color: "rgb(240, 230, 140)" }]route GET /products = products | map((p) => merge(p, { color_name: color_name(p.color)?.name }))route GET /name ? { c: str = "#663399" } = { nearest: color_name(c)?, others: color_name_nearest(c, 3)? | map((x) => x.name) }route GET /hex ? { name: str = "Light Sea Green" } = { name, hex: color_name_hex(name)?, names: len(color_name_list()) }
$ quillo run color_names.qwe$ curl 'localhost:8080/name?c=%23663399'$ curl 'localhost:8080/hex'$ curl 'localhost:8080/products'

Source

color_names.qwer
//! The nearest of the 148 CSS colour names to any colour ("#3b82f6" is dodgerblue), names back to hex, and the full list: for labels, filters and alt text.//! version: 1.0.0//!//! [dependencies]use serde_json::{json, Value};const NAMES: [(&str, u32); 148] = [    ("aliceblue", 0xf0f8ff),    ("antiquewhite", 0xfaebd7),    ("aqua", 0x00ffff),    ("aquamarine", 0x7fffd4),    ("azure", 0xf0ffff),    ("beige", 0xf5f5dc),    ("bisque", 0xffe4c4),    ("black", 0x000000),    ("blanchedalmond", 0xffebcd),    ("blue", 0x0000ff),    ("blueviolet", 0x8a2be2),    ("brown", 0xa52a2a),    ("burlywood", 0xdeb887),    ("cadetblue", 0x5f9ea0),    ("chartreuse", 0x7fff00),    ("chocolate", 0xd2691e),    ("coral", 0xff7f50),    ("cornflowerblue", 0x6495ed),    ("cornsilk", 0xfff8dc),    ("crimson", 0xdc143c),    ("cyan", 0x00ffff),    ("darkblue", 0x00008b),    ("darkcyan", 0x008b8b),    ("darkgoldenrod", 0xb8860b),    ("darkgray", 0xa9a9a9),    ("darkgreen", 0x006400),    ("darkgrey", 0xa9a9a9),    ("darkkhaki", 0xbdb76b),    ("darkmagenta", 0x8b008b),    ("darkolivegreen", 0x556b2f),    ("darkorange", 0xff8c00),    ("darkorchid", 0x9932cc),    ("darkred", 0x8b0000),    ("darksalmon", 0xe9967a),    ("darkseagreen", 0x8fbc8f),    ("darkslateblue", 0x483d8b),    ("darkslategray", 0x2f4f4f),    ("darkslategrey", 0x2f4f4f),    ("darkturquoise", 0x00ced1),    ("darkviolet", 0x9400d3),    ("deeppink", 0xff1493),    ("deepskyblue", 0x00bfff),    ("dimgray", 0x696969),    ("dimgrey", 0x696969),    ("dodgerblue", 0x1e90ff),    ("firebrick", 0xb22222),    ("floralwhite", 0xfffaf0),    ("forestgreen", 0x228b22),    ("fuchsia", 0xff00ff),    ("gainsboro", 0xdcdcdc),    ("ghostwhite", 0xf8f8ff),    ("gold", 0xffd700),    ("goldenrod", 0xdaa520),    ("gray", 0x808080),    ("green", 0x008000),    ("greenyellow", 0xadff2f),    ("grey", 0x808080),    ("honeydew", 0xf0fff0),    ("hotpink", 0xff69b4),    ("indianred", 0xcd5c5c),    ("indigo", 0x4b0082),    ("ivory", 0xfffff0),    ("khaki", 0xf0e68c),    ("lavender", 0xe6e6fa),    ("lavenderblush", 0xfff0f5),    ("lawngreen", 0x7cfc00),    ("lemonchiffon", 0xfffacd),    ("lightblue", 0xadd8e6),    ("lightcoral", 0xf08080),    ("lightcyan", 0xe0ffff),    ("lightgoldenrodyellow", 0xfafad2),    ("lightgray", 0xd3d3d3),    ("lightgreen", 0x90ee90),    ("lightgrey", 0xd3d3d3),    ("lightpink", 0xffb6c1),    ("lightsalmon", 0xffa07a),    ("lightseagreen", 0x20b2aa),    ("lightskyblue", 0x87cefa),    ("lightslategray", 0x778899),    ("lightslategrey", 0x778899),    ("lightsteelblue", 0xb0c4de),    ("lightyellow", 0xffffe0),    ("lime", 0x00ff00),    ("limegreen", 0x32cd32),    ("linen", 0xfaf0e6),    ("magenta", 0xff00ff),    ("maroon", 0x800000),    ("mediumaquamarine", 0x66cdaa),    ("mediumblue", 0x0000cd),    ("mediumorchid", 0xba55d3),    ("mediumpurple", 0x9370db),    ("mediumseagreen", 0x3cb371),    ("mediumslateblue", 0x7b68ee),    ("mediumspringgreen", 0x00fa9a),    ("mediumturquoise", 0x48d1cc),    ("mediumvioletred", 0xc71585),    ("midnightblue", 0x191970),    ("mintcream", 0xf5fffa),    ("mistyrose", 0xffe4e1),    ("moccasin", 0xffe4b5),    ("navajowhite", 0xffdead),    ("navy", 0x000080),    ("oldlace", 0xfdf5e6),    ("olive", 0x808000),    ("olivedrab", 0x6b8e23),    ("orange", 0xffa500),    ("orangered", 0xff4500),    ("orchid", 0xda70d6),    ("palegoldenrod", 0xeee8aa),    ("palegreen", 0x98fb98),    ("paleturquoise", 0xafeeee),    ("palevioletred", 0xdb7093),    ("papayawhip", 0xffefd5),    ("peachpuff", 0xffdab9),    ("peru", 0xcd853f),    ("pink", 0xffc0cb),    ("plum", 0xdda0dd),    ("powderblue", 0xb0e0e6),    ("purple", 0x800080),    ("rebeccapurple", 0x663399),    ("red", 0xff0000),    ("rosybrown", 0xbc8f8f),    ("royalblue", 0x4169e1),    ("saddlebrown", 0x8b4513),    ("salmon", 0xfa8072),    ("sandybrown", 0xf4a460),    ("seagreen", 0x2e8b57),    ("seashell", 0xfff5ee),    ("sienna", 0xa0522d),    ("silver", 0xc0c0c0),    ("skyblue", 0x87ceeb),    ("slateblue", 0x6a5acd),    ("slategray", 0x708090),    ("slategrey", 0x708090),    ("snow", 0xfffafa),    ("springgreen", 0x00ff7f),    ("steelblue", 0x4682b4),    ("tan", 0xd2b48c),    ("teal", 0x008080),    ("thistle", 0xd8bfd8),    ("tomato", 0xff6347),    ("turquoise", 0x40e0d0),    ("violet", 0xee82ee),    ("wheat", 0xf5deb3),    ("white", 0xffffff),    ("whitesmoke", 0xf5f5f5),    ("yellow", 0xffff00),    ("yellowgreen", 0x9acd32),];fn parse_hex(s: &str) -> Option<u32> {    let h = s.trim().trim_start_matches('#');    let full = match h.len() {        3 | 4 => h.chars().take(3).flat_map(|c| [c, c]).collect::<String>(),        6 | 8 => h[..6].to_string(),        _ => return None,    };    u32::from_str_radix(&full, 16).ok()}// "#rgb", "#rrggbb", "rgb(r, g, b)" or a CSS name, as 0xRRGGBB.fn parse(c: &str) -> Result<u32, String> {    let t = c.trim().to_lowercase();    if let Some(v) = NAMES.iter().find(|(n, _)| *n == t).map(|(_, v)| *v) {        return Ok(v);    }    if let Some(inner) = t.strip_prefix("rgb(").or_else(|| t.strip_prefix("rgba(")).and_then(|r| r.strip_suffix(')')) {        let parts: Vec<u32> = inner.split(|ch| ch == ',' || ch == ' ' || ch == '/').filter(|p| !p.is_empty()).take(3).filter_map(|p| p.trim().parse::<f64>().ok()).map(|x| x.round().clamp(0.0, 255.0) as u32).collect();        if parts.len() == 3 {            return Ok(parts[0] << 16 | parts[1] << 8 | parts[2]);        }    }    if t.starts_with('#') || t.chars().all(|ch| ch.is_ascii_hexdigit()) {        if let Some(v) = parse_hex(&t) {            return Ok(v);        }    }    Err(format!("`{}` is not a colour: give \"#rrggbb\", \"#rgb\", \"rgb(r, g, b)\" or a CSS name", c))}fn hex(v: u32) -> String {    format!("#{:06x}", v)}// Oklab: distances there follow how different colours look.fn oklab(v: u32) -> [f64; 3] {    let lin = |c: u32| {        let s = c as f64 / 255.0;        if s <= 0.04045 { s / 12.92 } else { ((s + 0.055) / 1.055).powf(2.4) }    };    let (r, g, b) = (lin(v >> 16 & 255), lin(v >> 8 & 255), lin(v & 255));    let l = (0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b).cbrt();    let m = (0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b).cbrt();    let s = (0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b).cbrt();    [0.2104542553 * l + 0.7936177850 * m - 0.0040720468 * s, 1.9779984951 * l - 2.4285922050 * m + 0.4505937099 * s, 0.0259040371 * l + 0.7827717662 * m - 0.8086757660 * s]}fn dist(a: u32, b: u32) -> f64 {    let (x, y) = (oklab(a), oklab(b));    ((x[0] - y[0]).powi(2) + (x[1] - y[1]).powi(2) + (x[2] - y[2]).powi(2)).sqrt()}fn ranked(v: u32) -> Vec<(&'static str, u32, f64)> {    let mut all: Vec<(&str, u32, f64)> = NAMES.iter().map(|(n, h)| (*n, *h, dist(v, *h))).collect();    // grey and gray name the same colours: keep the "gray" spellings once.    all.retain(|(n, _, _)| !n.contains("grey"));    all.sort_by(|a, b| a.2.partial_cmp(&b.2).unwrap_or(std::cmp::Ordering::Equal));    all}/// The CSS colour name nearest to `color` ("#3b82f6", "#abc", "rgb(59, 130, 246)" or a name): `{ name, hex, distance, exact }`./// `distance` is how far apart they look (Oklab, 0 the same, under 0.02 hard to tell, over 0.1 clearly different); `exact` true when it is that colour.pub fn color_name(color: &str) -> Result<Value, String> {    let v = parse(color)?;    let (n, h, d) = ranked(v)[0];    Ok(json!({ "name": n, "hex": hex(h), "distance": (d * 10000.0).round() / 10000.0, "exact": h == v }))}/// The `count` nearest names (1 to 20), nearest first, each `{ name, hex, distance }`: to offer a choice, or a search by colour.pub fn color_name_nearest(color: &str, count: i64) -> Result<Vec<Value>, String> {    let v = parse(color)?;    Ok(ranked(v).into_iter().take(count.clamp(1, 20) as usize).map(|(n, h, d)| json!({ "name": n, "hex": hex(h), "distance": (d * 10000.0).round() / 10000.0 })).collect())}/// A CSS colour name ("rebeccapurple", "Light Sea Green" with spaces and capitals too) as "#rrggbb"; an error for a name CSS does not have.pub fn color_name_hex(name: &str) -> Result<String, String> {    let n: String = name.trim().to_lowercase().chars().filter(|c| !c.is_whitespace() && *c != '-' && *c != '_').collect();    NAMES.iter().find(|(k, _)| *k == n).map(|(_, v)| hex(*v)).ok_or_else(|| format!("`{}` is not a CSS colour name", name))}/// All 148 CSS colour names with their hex, as `{ name, hex }` in alphabetical order (gray and grey spellings both).pub fn color_name_list() -> Vec<Value> {    NAMES.iter().map(|(n, h)| json!({ "name": n, "hex": hex(*h) })).collect()}
Thread
89Useful? Like it, or add to its thread below.

Thread 0 posts

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

No posts yet. The first one opens the thread.