From 1490d4e594093846c403bf3a6fdb5165b2db6823 Mon Sep 17 00:00:00 2001 From: Dmitrii Khlapov Date: Mon, 12 Oct 2026 06:51:53 +0200 Subject: [PATCH] Dark theme: readable text on the accent (#655) (#704) * #657: input borders reach 3:1 contrast in both themes Co-Authored-By: Claude Opus 5.5 * #655: dark-theme accent controls use dark on_accent text (6:1) Co-Authored-By: Claude Opus 5.5 --------- Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 Co-authored-by: Brandon Thomas --- crates/ui-egui/src/chrome.rs | 4 ++-- crates/ui-egui/src/ribbon.rs | 6 +++--- crates/ui-egui/src/theme.rs | 15 ++++++++++++++- docs/ui-parity.md | 3 ++- 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/crates/ui-egui/src/chrome.rs b/crates/ui-egui/src/chrome.rs index 69c4090cd..a93a22cb6 100644 --- a/crates/ui-egui/src/chrome.rs +++ b/crates/ui-egui/src/chrome.rs @@ -132,7 +132,7 @@ pub fn title_bar(app: &mut WordApp, ui: &mut Ui) { let (r, resp) = ui.allocate_exact_size(vec2(30.0, 16.0), Sense::click()); let (fill, stroke, knob_fill) = match (&block, on) { (Some(_), _) => (t.input.gamma_multiply(0.5), t.border.gamma_multiply(0.7), t.text_dim.gamma_multiply(0.45)), - (None, true) => (t.accent, t.accent, egui::Color32::WHITE), + (None, true) => (t.accent, t.accent, t.on_accent), (None, false) => (t.input, t.border_strong, t.text_dim), }; ui.painter().rect(r, 8.0, fill, Stroke::new(1.0, stroke), egui::StrokeKind::Inside); @@ -161,7 +161,7 @@ pub fn title_bar(app: &mut WordApp, ui: &mut Ui) { let (r, resp) = ui.allocate_exact_size(vec2(control_h, control_h), Sense::click()); ui.painter().circle_filled(r.center(), control_h / 2.0 - 1.0, t.accent); let initials: String = app.session.user_initials().chars().take(2).collect(); - ui.painter().text(r.center(), Align2::CENTER_CENTER, initials, semibold(10.5), egui::Color32::WHITE); + ui.painter().text(r.center(), Align2::CENTER_CENTER, initials, semibold(10.5), t.on_accent); resp.on_hover_text(format!("{} — set your name in File › Options", app.session.author)); ui.add_space(8.0); if app.ui.show_discord { diff --git a/crates/ui-egui/src/ribbon.rs b/crates/ui-egui/src/ribbon.rs index a6418e374..374ebf914 100644 --- a/crates/ui-egui/src/ribbon.rs +++ b/crates/ui-egui/src/ribbon.rs @@ -163,10 +163,10 @@ pub fn show(app: &mut WordApp, ui: &mut Ui) { ui.painter(), Rect::from_center_size(pos2(r.min.x + 14.0, r.center().y), vec2(14.0, 14.0)), "share", - egui::Color32::WHITE, - egui::Color32::WHITE, + t.on_accent, + t.on_accent, ); - ui.painter().text(pos2(r.min.x + 26.0, r.center().y), Align2::LEFT_CENTER, share, medium(12.0), egui::Color32::WHITE); + ui.painter().text(pos2(r.min.x + 26.0, r.center().y), Align2::LEFT_CENTER, share, medium(12.0), t.on_accent); if resp.on_hover_text(tl!("Export a copy to share (PDF, Word document)")).clicked() { let _ = app.run("ui.backstage", json!({"value": true, "page": "export"})); } diff --git a/crates/ui-egui/src/theme.rs b/crates/ui-egui/src/theme.rs index b4901f67e..27c18e399 100644 --- a/crates/ui-egui/src/theme.rs +++ b/crates/ui-egui/src/theme.rs @@ -232,7 +232,8 @@ impl Tokens { checked: Color32::from_rgb(0x2E, 0x3A, 0x66), accent: Color32::from_rgb(0x7A, 0x93, 0xF0), accent_text: Color32::from_rgb(0x9C, 0xB0, 0xF5), - on_accent: Color32::WHITE, + // Dark text on the light dark-theme accent: white on #7a93f0 is only 2.9:1 (#655). + on_accent: Color32::from_rgb(0x11, 0x16, 0x2E), input: Color32::from_rgb(0x1E, 0x1E, 0x1E), input_border: Color32::from_rgb(0x7A, 0x7A, 0x7A), group_label: Color32::from_rgb(0xA0, 0xA0, 0xA0), @@ -568,6 +569,18 @@ mod tests { (x.max(y) + 0.05) / (x.min(y) + 0.05) } + /// #655: text and knobs on filled accent controls (Share, avatar, AutoSave, keytips) reach 4.5:1 + /// on the accent and on its hover shade, in both themes. + #[test] + fn on_accent_reads_on_the_accent() { + for t in [super::Tokens::light(), super::Tokens::dark()] { + for (name, bg) in [("accent", t.accent), ("hover", t.accent_text)] { + let r = contrast(t.on_accent, bg); + assert!(r >= 4.5, "dark={}: on_accent on {name} is {r:.2}:1", t.dark); + } + } + } + /// #657: text field and combo outlines reach 3:1 against the field and everything it sits on. #[test] fn input_borders_reach_three_to_one() { diff --git a/docs/ui-parity.md b/docs/ui-parity.md index 7ef0126df..eda6b7ca2 100644 --- a/docs/ui-parity.md +++ b/docs/ui-parity.md @@ -1,6 +1,6 @@ # UI parity with Microsoft Word -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-11 · **Change:** trivial (input borders reach 3:1, #657; previously trivial: Outlining tab and outline keys, #493; previously trivial: Customize Ribbon and the Quick Access Toolbar, #379; previously: major, first version; ribbon, dialogs, shortcuts and on-canvas interaction measured from `crates/ui-egui` and the command registry) · **Target:** Microsoft Word (Microsoft 365) for Mac 16.113.4 +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-11 · **Change:** trivial (dark-theme accent controls readable, #655; previously trivial: input borders reach 3:1, #657; previously trivial: Outlining tab and outline keys, #493; previously trivial: Customize Ribbon and the Quick Access Toolbar, #379; previously: major, first version; ribbon, dialogs, shortcuts and on-canvas interaction measured from `crates/ui-egui` and the command registry) · **Target:** Microsoft Word (Microsoft 365) for Mac 16.113.4 How WordCraft looks and feels next to Word: ribbon, dialogs, panes, keyboard, mouse and touch. Typing behaviour has its own checklist ([`typing-parity.md`](typing-parity.md)). All assets are our @@ -53,6 +53,7 @@ own (`AGENTS.md`: no Microsoft iconography, ever). | 2026-10-11 | trivial | Line Numbers, Hyphenation (with Manual Hyphenation), Language, Envelopes and Labels dialogs (#407); the three duplicated modal-dialog rows merged into one | | 2026-10-11 | trivial | Accessibility pane (errors/warnings/tips, jump-to, fixes) and Alt Text pane, opened from Picture/Shape Format › Accessibility and right-click › View Alt Text… (#386) | | 2026-10-11 | trivial | References dialogs: Caption (New/Delete Label, Numbering with chapter numbers), Index and Mark Index Entry (Mark All), Custom Table of Contents (Options, TOC styles via Modify Style), Source Manager and Create/Edit Source; modal dialog count 34 (#402) | +| 2026-10-11 | trivial | Dark theme: Share, the avatar initials, the AutoSave knob and keytips use dark `on_accent` text on the light accent (6.2:1, 8.4:1 on hover; white was 2.9:1) (#655) | | 2026-10-11 | trivial | Text field and combo outlines reach 3:1 against their fill and surroundings in both themes (light #8A8A8A, dark #7A7A7A); a test pins the ratios (#657) | | 2026-10-11 | trivial | Font dialog: Asian text font and Latin text font boxes, each with its own list and type-to-find, shown for a Chinese/Japanese interface or East Asian text in the selection; OK sets only the fonts changed; `format.font {"eastAsia"}` (#505) | | 2026-10-11 | trivial | References › Table of Authorities: Insert Table of Authorities is labelled and joined by Update Table (`references.updateAuthorities`), as in the Index group (#661) |