1use crate::color::{linear_srgb_of, Color};
9
10pub fn relative_luminance(color: &Color) -> f64 {
12 let [r, g, b] = linear_srgb_of(color);
13 0.2126 * r + 0.7152 * g + 0.0722 * b
14}
15
16pub fn contrast_ratio(a: &Color, b: &Color) -> f64 {
19 let la = relative_luminance(a);
20 let lb = relative_luminance(b);
21 let (light, dark) = if la >= lb { (la, lb) } else { (lb, la) };
22 (light + 0.05) / (dark + 0.05)
23}
24
25pub const AA_TEXT: f64 = 4.5;
27pub const AA_LARGE_TEXT: f64 = 3.0;
29pub const AA_NON_TEXT: f64 = 3.0;
31
32pub const LIGHT_BG: &str = "#ffffff";
34pub const DARK_BG: &str = "#15161a";
37pub const TEXT_ON_LIGHT: &str = "#1a1a1a";
39pub const TEXT_ON_DARK: &str = "#f5f5f5";
41
42#[cfg(test)]
43mod tests {
44 use super::*;
45 use crate::color::Color;
46
47 fn c(hex: &str) -> Color {
48 Color::from_hex(hex).unwrap()
49 }
50
51 #[test]
52 fn black_on_white_is_twenty_one() {
53 let r = contrast_ratio(&c("#000000"), &c("#ffffff"));
54 assert!((r - 21.0).abs() < 0.01, "got {r}");
55 }
56
57 #[test]
58 fn identical_colors_yield_one() {
59 let r = contrast_ratio(&c("#3f6089"), &c("#3f6089"));
60 assert!((r - 1.0).abs() < 1e-9);
61 }
62
63 #[test]
64 fn ratio_is_symmetric() {
65 let a = c("#0d9488");
66 let b = c("#ffffff");
67 assert!((contrast_ratio(&a, &b) - contrast_ratio(&b, &a)).abs() < 1e-9);
68 }
69}