Pixel icons and the three-column table the page uses for facts. The icons are
site::config::Pic: shuqikhor's SVGs, vendored unchanged, drawn inline at about four times a
title-bar icon (page.css, .pic). The text of a file is trusted (it is a committed, tested
file, never anything a visitor sent); everything else goes through maud's escaping.
One icon, hidden from a screen reader: the words beside it say what it means.
10pub fn pic(p: Pic) -> Markup { 11 match p.art().2 { 12 Art::Svg(text) => html! { span .pic aria-hidden="true" { (PreEscaped(text)) } }, 13 // A PNG goes in as a data address (the page's policy allows `data:` images), drawn 14 // unsmoothed so its pixels stay square. 15 Art::Png(bytes) => html! { span .pic .png aria-hidden="true" { img src=(format!("data:image/png;base64,{}", base64(bytes))) alt="" width="32" height="32"; } }, 16 } 17}
Several icons side by side, for a download that has more than one.
24fn base64(bytes: &[u8]) -> String { 25 const T: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 26 let mut out = String::with_capacity(bytes.len().div_ceil(3) * 4); 27 for chunk in bytes.chunks(3) { 28 let n = (u32::from(chunk[0]) << 16) | (u32::from(*chunk.get(1).unwrap_or(&0)) << 8) | u32::from(*chunk.get(2).unwrap_or(&0)); 29 for i in 0..4 { 30 if i <= chunk.len() { out.push(T[(n >> (18 - 6 * i) & 63) as usize] as char) } else { out.push('=') } 31 } 32 } 33 out 34} 35 36#[cfg(test)] 37mod tests { 38 use super::*; 39 40 #[test] 41 fn base64_matches_the_standard_examples() { 42 for (plain, coded) in [("", ""), ("f", "Zg=="), ("fo", "Zm8="), ("foo", "Zm9v"), ("foob", "Zm9vYg=="), ("fooba", "Zm9vYmE="), ("foobar", "Zm9vYmFy")] { 43 assert_eq!(base64(plain.as_bytes()), coded); 44 } 45 } 46}
One row: an icon, a title and what it says.
An icon column, a title column (th scope="row") and a description column. The caption and
the header row are for a screen reader; at phone width each row stacks (page.css).
57pub fn facts(caption: &str, rows: Vec<Row>) -> Markup { 58 html! { 59 table .facts { 60 caption .vh { (caption) } 61 thead .vh { tr { th scope="col" { "Icon" } th scope="col" { "Item" } th scope="col" { "What it is" } } } 62 tbody { 63 @for r in rows { 64 tr { 65 td .icon-cell { (pic(r.icon)) } 66 th scope="row" { (r.title) } 67 td { (r.text) } 68 } 69 } 70 } 71 } 72 } 73}