add: display warning in profile settings if your text contrast is horrible

This commit is contained in:
trisua 2025-04-22 19:46:08 -04:00
parent 434f2ba00d
commit f867abcb34
5 changed files with 172 additions and 16 deletions

105
Cargo.lock generated
View file

@ -34,13 +34,13 @@ checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1"
[[package]] [[package]]
name = "ammonia" name = "ammonia"
version = "4.0.0" version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ab99eae5ee58501ab236beb6f20f6ca39be615267b014899c89b2f0bc18a459" checksum = "3ada2ee439075a3e70b6992fce18ac4e407cd05aea9ca3f75d2c0b0c20bbb364"
dependencies = [ dependencies = [
"cssparser",
"html5ever", "html5ever",
"maplit", "maplit",
"once_cell",
"tendril", "tendril",
"url", "url",
] ]
@ -646,6 +646,12 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
[[package]]
name = "contrasted"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40d36a6fb07044a3371abc364027b9489d770a840d72a04323389cfe32a41360"
[[package]] [[package]]
name = "cookie" name = "cookie"
version = "0.18.1" version = "0.18.1"
@ -742,6 +748,29 @@ dependencies = [
"typenum", "typenum",
] ]
[[package]]
name = "cssparser"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e901edd733a1472f944a45116df3f846f54d37e67e68640ac8bb69689aca2aa"
dependencies = [
"cssparser-macros",
"dtoa-short",
"itoa",
"phf",
"smallvec",
]
[[package]]
name = "cssparser-macros"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
dependencies = [
"quote",
"syn",
]
[[package]] [[package]]
name = "darling" name = "darling"
version = "0.20.11" version = "0.20.11"
@ -814,6 +843,21 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "dtoa"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6add3b8cff394282be81f3fc1a0605db594ed69890078ca6e2cab1c408bcf04"
[[package]]
name = "dtoa-short"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87"
dependencies = [
"dtoa",
]
[[package]] [[package]]
name = "either" name = "either"
version = "1.15.0" version = "1.15.0"
@ -1170,16 +1214,14 @@ dependencies = [
[[package]] [[package]]
name = "html5ever" name = "html5ever"
version = "0.27.0" version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4" checksum = "953cbbe631aae7fc0a112702ad5d3aaf09da38beaf45ea84610d6e1c358f569c"
dependencies = [ dependencies = [
"log", "log",
"mac", "mac",
"markup5ever", "markup5ever",
"proc-macro2", "match_token",
"quote",
"syn",
] ]
[[package]] [[package]]
@ -1714,16 +1756,24 @@ checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
[[package]] [[package]]
name = "markup5ever" name = "markup5ever"
version = "0.12.1" version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16ce3abbeba692c8b8441d036ef91aea6df8da2c6b6e21c7e14d3c18e526be45" checksum = "d0a8096766c229e8c88a3900c9b44b7e06aa7f7343cc229158c3e58ef8f9973a"
dependencies = [ dependencies = [
"log", "log",
"phf",
"phf_codegen",
"string_cache",
"string_cache_codegen",
"tendril", "tendril",
"web_atoms",
]
[[package]]
name = "match_token"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b"
dependencies = [
"proc-macro2",
"quote",
"syn",
] ]
[[package]] [[package]]
@ -2123,6 +2173,7 @@ version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
dependencies = [ dependencies = [
"phf_macros",
"phf_shared", "phf_shared",
] ]
@ -2146,6 +2197,19 @@ dependencies = [
"rand 0.8.5", "rand 0.8.5",
] ]
[[package]]
name = "phf_macros"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216"
dependencies = [
"phf_generator",
"phf_shared",
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "phf_shared" name = "phf_shared"
version = "0.11.3" version = "0.11.3"
@ -3160,6 +3224,7 @@ dependencies = [
"axum", "axum",
"axum-extra", "axum-extra",
"cf-turnstile", "cf-turnstile",
"contrasted",
"image", "image",
"mime_guess", "mime_guess",
"pathbufd", "pathbufd",
@ -3928,6 +3993,18 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "web_atoms"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "954c5a41f2bcb7314344079d0891505458cc2f4b422bdea1d5bfbe6d1a04903b"
dependencies = [
"phf",
"phf_codegen",
"string_cache",
"string_cache_codegen",
]
[[package]] [[package]]
name = "weezl" name = "weezl"
version = "0.1.8" version = "0.1.8"

View file

@ -19,7 +19,7 @@ tower-http = { version = "0.6.2", features = ["trace", "fs"] }
axum = { version = "0.8.3", features = ["macros"] } axum = { version = "0.8.3", features = ["macros"] }
tokio = { version = "1.44.2", features = ["macros", "rt-multi-thread"] } tokio = { version = "1.44.2", features = ["macros", "rt-multi-thread"] }
axum-extra = { version = "0.10.1", features = ["cookie", "multipart"] } axum-extra = { version = "0.10.1", features = ["cookie", "multipart"] }
ammonia = "4.0.0" ammonia = "4.1.0"
tetratto-shared = { path = "../shared" } tetratto-shared = { path = "../shared" }
tetratto-core = { path = "../core", features = [ tetratto-core = { path = "../core", features = [
"redis", "redis",
@ -32,3 +32,4 @@ regex = "1.11.1"
serde_json = "1.0.140" serde_json = "1.0.140"
mime_guess = "2.0.5" mime_guess = "2.0.5"
cf-turnstile = "0.2.0" cf-turnstile = "0.2.0"
contrasted = "0.1.0"

View file

@ -313,6 +313,25 @@
<div class="w-full hidden flex flex-col gap-2" data-tab="theme"> <div class="w-full hidden flex flex-col gap-2" data-tab="theme">
<div class="card tertiary flex flex-col gap-2" id="theme_settings"> <div class="card tertiary flex flex-col gap-2" id="theme_settings">
{% if failing_color_keys|length > 0 %}
<div
class="card flex flex-col gap-2"
style="background: white; color: black"
ui_ident="awful_contrast"
>
<div class="flex gap-2 items-center">
{{ icon "contrast" }}
<b>Some of your custom colors fail contrast checks:</b>
</div>
<ul>
{% for key in failing_color_keys %}
<li>{{ key[0] }} <b>{{ key[1] }} &lt; 4.5</b></li>
{% endfor %}
</ul>
</div>
{% endif %}
<div <div
class="card w-full flex flex-wrap gap-2" class="card w-full flex flex-wrap gap-2"
ui_ident="import_export" ui_ident="import_export"
@ -725,6 +744,7 @@
"change_banner", "change_banner",
]); ]);
ui.refresh_container(theme_settings, [ ui.refresh_container(theme_settings, [
"awful_contrast",
"import_export", "import_export",
"theme_preference", "theme_preference",
"profile_theme", "profile_theme",

View file

@ -10,6 +10,7 @@ use serde::Deserialize;
use tera::Context; use tera::Context;
use tetratto_core::model::{Error, auth::User, communities::Community, permissions::FinePermission}; use tetratto_core::model::{Error, auth::User, communities::Community, permissions::FinePermission};
use tetratto_shared::hash::hash; use tetratto_shared::hash::hash;
use contrasted::{Color, MINIMUM_CONTRAST_THRESHOLD};
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct SettingsProps { pub struct SettingsProps {
@ -59,6 +60,63 @@ pub async fn settings_request(
.replace("\"", "\\\""), .replace("\"", "\\\""),
); );
// check color contrasts
let mut failing_color_keys: Vec<(&str, f64)> = Vec::new();
let settings_map = serde_json::from_str::<serde_json::Map<String, serde_json::Value>>(
&serde_json::to_string(&profile.settings).unwrap(),
)
.unwrap();
let light = serde_json::Value::from("Light");
let mut profile_theme = settings_map
.get("profile_theme")
.unwrap_or(&light)
.as_str()
.unwrap();
if profile_theme.is_empty() | (profile_theme == "Auto") {
profile_theme = "Light";
}
let default_surface = serde_json::Value::from(if profile_theme == "Light" {
"#f3f2f1"
} else {
"#19171c"
});
let mut color_surface = settings_map
.get("theme_color_surface")
.unwrap_or(&default_surface)
.as_str()
.unwrap();
if color_surface.is_empty() {
color_surface = default_surface.as_str().unwrap();
}
for setting in &settings_map {
if !setting.0.starts_with("theme_color_text") {
continue;
}
let value = setting.1.as_str().unwrap();
if !value.starts_with("#") {
// we can only parse hex right now
continue;
}
let c1 = Color::from_hex(&color_surface);
let c2 = Color::from_hex(&value);
let contrast = c1.contrast(&c2);
if contrast < MINIMUM_CONTRAST_THRESHOLD {
failing_color_keys.push((setting.0, contrast));
}
}
context.insert("failing_color_keys", &failing_color_keys);
// return // return
Ok(Html( Ok(Html(
data.1.render("profile/settings.html", &context).unwrap(), data.1.render("profile/settings.html", &context).unwrap(),

View file

@ -7,7 +7,7 @@ repository.workspace = true
license.workspace = true license.workspace = true
[dependencies] [dependencies]
ammonia = "4.0.0" ammonia = "4.1.0"
chrono = "0.4.40" chrono = "0.4.40"
comrak = "0.38.0" comrak = "0.38.0"
hex_fmt = "0.3.0" hex_fmt = "0.3.0"