diff --git a/ui/public/vendor/css/components/settings.css b/ui/public/vendor/css/components/settings.css index 9023ad9..17f30cf 100644 --- a/ui/public/vendor/css/components/settings.css +++ b/ui/public/vendor/css/components/settings.css @@ -460,7 +460,7 @@ padding: 2px 6px; border-radius: 3px; border: 1px solid; } .fm-contact-trust.known { color: var(--trust); border-color: rgba(51, 153, 102, 0.3); background: var(--trust-bg); } - .fm-contact-trust.unknown { color: #a6731f; border-color: rgba(217, 119, 6, 0.3); background: rgba(217, 119, 6, 0.06); } + .fm-contact-trust.unknown { color: var(--caution); border-color: rgba(217, 119, 6, 0.3); background: rgba(217, 119, 6, 0.06); } .fm-contact-trust.unsigned { color: #a32f2f; border-color: rgba(163, 47, 47, 0.3); background: #fee2e2; } .fm-contact-tier { font-family: "Geist Mono", monospace; font-size: 10.5px; letter-spacing: 0.04em; @@ -746,7 +746,7 @@ background: var(--trust); border-color: var(--trust); } .verify-box .tick { - color: #fff; font-size: 13px; line-height: 1; opacity: 0; transform: scale(0.7); + color: var(--on-trust); font-size: 13px; line-height: 1; opacity: 0; transform: scale(0.7); transition: opacity .15s ease, transform .15s ease; } .verify-check.checked .verify-box .tick { opacity: 1; transform: scale(1); } diff --git a/ui/public/vendor/css/components/tokens.css b/ui/public/vendor/css/components/tokens.css index 62ea937..5da1038 100644 --- a/ui/public/vendor/css/components/tokens.css +++ b/ui/public/vendor/css/components/tokens.css @@ -64,6 +64,15 @@ --caution: #a6731f; --warn: #a32f2f; + /* Error/danger text on a card surface (e.g. the import-contact + unverified warning, #340). Dark theme remaps to a light red so it + stays readable on the dark card. */ + --danger-ink: #991b1b; + + /* Ink on a solid --trust surface (verify-box tick). White works on the + light-theme green but not on the brighter dark-theme green (#340). */ + --on-trust: #fff; + position: fixed; inset: 0; display: flex; @@ -124,6 +133,8 @@ --amber-ink: #fbbf24; --caution: #d6a44e; --warn: #e06c6c; + --danger-ink: #f87171; + --on-trust: #052e16; } @media (prefers-color-scheme: dark) { @@ -156,6 +167,8 @@ --amber-ink: #fbbf24; --caution: #d6a44e; --warn: #e06c6c; + --danger-ink: #f87171; + --on-trust: #052e16; } } diff --git a/ui/src/app/login.rs b/ui/src/app/login.rs index 76c794d..d377f81 100644 --- a/ui/src/app/login.rs +++ b/ui/src/app/login.rs @@ -2376,7 +2376,7 @@ pub(super) fn ImportContactForm() -> Element { } if has_verify_phrase && !*verified.read() { span { class: "verify-sub", - style: "color:#991b1b;", + style: "color: var(--danger-ink, #991b1b);", "The sender included a verify phrase. Importing unverified is allowed, but verified-only inboxes will reject your messages until you tick this once you've matched the six words out-of-band." } } diff --git a/ui/src/app/settings.rs b/ui/src/app/settings.rs index e277069..5e006b1 100644 --- a/ui/src/app/settings.rs +++ b/ui/src/app/settings.rs @@ -129,7 +129,7 @@ pub(crate) fn SettingsShell() -> Element { div { class: "fm-set-ident-pop", role: "listbox", - style: "border: 1px solid var(--line); border-radius: 10px; background: #fff; box-shadow: 0 8px 24px rgba(0,0,0,0.08); margin: 4px 0 12px; overflow: hidden;", + style: "border: 1px solid var(--line); border-radius: 10px; background: var(--bg-card); box-shadow: 0 8px 24px rgba(0,0,0,0.08); margin: 4px 0 12px; overflow: hidden;", for (uid, row_alias, fp) in switcher_rows.iter() { { let uid = *uid; @@ -141,7 +141,7 @@ pub(crate) fn SettingsShell() -> Element { .map(|c| c.to_string()) .unwrap_or_else(|| "·".into()); let row_fp = fp.clone(); - let bg = if is_active { "var(--bg-soft)" } else { "#fff" }; + let bg = if is_active { "var(--bg-soft)" } else { "var(--bg-card)" }; let style = format!( "all: unset; display: flex; gap: 10px; align-items: center; width: 100%; padding: 8px 12px; cursor: pointer; background: {bg};", ); @@ -962,7 +962,7 @@ fn ScrAft() -> Element { div { key: "{tier_name}", class: "fm-tier", - style: "display: flex; flex-direction: column; gap: 4px; border: 1px solid var(--line); border-radius: 11px; padding: 12px 12px 10px; background: #fff; {extra_style}", + style: "display: flex; flex-direction: column; gap: 4px; border: 1px solid var(--line); border-radius: 11px; padding: 12px 12px 10px; background: var(--bg-card); {extra_style}", "data-testid": "fm-aft-usage-tier", "data-tier": "{tier_name}", "data-used": "{used}", @@ -1007,7 +1007,7 @@ fn ScrAft() -> Element { key: "{id}", class: "{cls}", r#type: "button", - style: "all: unset; display: flex; flex-direction: column; gap: 4px; cursor: pointer; border: 1px solid var(--line); border-radius: 11px; padding: 12px 12px 10px; background: #fff;", + style: "all: unset; display: flex; flex-direction: column; gap: 4px; cursor: pointer; border: 1px solid var(--line); border-radius: 11px; padding: 12px 12px 10px; background: var(--bg-card);", "data-testid": "fm-aft-tier", "data-tier": "{id}", "data-active": "{active}", @@ -1612,6 +1612,31 @@ mod tests { const FP: &str = "abandon-ability-able-about-above-absent"; + /// #340: every Settings screen renders inside `.fm-app`, which has a dark + /// theme. A hard-coded white inline background ignores the theme and puts + /// the light dark-mode ink on a white surface (AFT tier cards, identity + /// switcher rows). Use `var(--bg-card)` instead. + #[test] + fn no_hardcoded_white_backgrounds_340() { + let src = include_str!("settings.rs"); + let body = src.split("#[cfg(test)]").next().unwrap(); + let offenders: Vec = body + .match_indices('#') + .filter(|(i, _)| { + let hex: String = body[i + 1..] + .chars() + .take_while(|c| c.is_ascii_hexdigit()) + .collect(); + matches!(hex.to_ascii_lowercase().as_str(), "fff" | "ffffff") + }) + .map(|(i, _)| body[..i].matches('\n').count() + 1) + .collect(); + assert!( + offenders.is_empty(), + "hard-coded white in settings.rs at line(s) {offenders:?}; use var(--bg-card)" + ); + } + /// No saved decision + auto-accept OFF → `None` (show modal). #[test] fn no_policy_returns_none() {