From 4176c70a20f2ad906c526a1656c8a12933317d73 Mon Sep 17 00:00:00 2001 From: Kostya Farber Date: Wed, 30 Sep 2026 14:43:48 +0100 Subject: [PATCH 1/3] feat: draw the launcher ghost in each theme's colours Shift Light keeps the channel-coloured lid; other themes fill the ghost with a tint of their text colour. Each colour is one path rendered with crisp edges, removing hairline seams between pixel blocks. Release now wears the forward cap and Nightly the backward one. --- apps/desktop/src/renderer/index.css | 6 +++ .../src/assets/launcher-lockup-nightly.svg | 45 +++++-------------- .../renderer/src/assets/launcher-lockup.svg | 45 +++++-------------- 3 files changed, 30 insertions(+), 66 deletions(-) diff --git a/apps/desktop/src/renderer/index.css b/apps/desktop/src/renderer/index.css index c6b8ecbb..74dc36f0 100644 --- a/apps/desktop/src/renderer/index.css +++ b/apps/desktop/src/renderer/index.css @@ -114,6 +114,12 @@ --editor-text-component-overlay-hover-b: rgba(255, 151, 186, 0.4); } +/* Only Shift Light shows the coloured lockup; other themes fill it with a tint of their text colour. */ +:root[data-color-theme]:not([data-color-theme="shift-light"]) { + --color-logo-lid: color-mix(in srgb, currentColor 22%, transparent); + --color-logo-body: color-mix(in srgb, currentColor 22%, transparent); +} + /* Lifts a bordered card off the page with theme-aware shadow colours. */ @utility card-shadow { box-shadow: diff --git a/apps/desktop/src/renderer/src/assets/launcher-lockup-nightly.svg b/apps/desktop/src/renderer/src/assets/launcher-lockup-nightly.svg index d69498b3..71868757 100644 --- a/apps/desktop/src/renderer/src/assets/launcher-lockup-nightly.svg +++ b/apps/desktop/src/renderer/src/assets/launcher-lockup-nightly.svg @@ -1,34 +1,13 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/apps/desktop/src/renderer/src/assets/launcher-lockup.svg b/apps/desktop/src/renderer/src/assets/launcher-lockup.svg index ac06fb0b..3636260d 100644 --- a/apps/desktop/src/renderer/src/assets/launcher-lockup.svg +++ b/apps/desktop/src/renderer/src/assets/launcher-lockup.svg @@ -1,34 +1,13 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + From 8aacf5e4c007f1ac5a0bf966c45f956f49056818 Mon Sep 17 00:00:00 2001 From: Kostya Farber Date: Wed, 30 Sep 2026 14:43:50 +0100 Subject: [PATCH 2/3] fix: use palette colours for text on theme accents Text on accent buttons now picks the palette's lightest colour instead of pure white, and prefers it within 20% of the best WCAG contrast. Solarized's blue gets its light base and Gruvbox Light's teal its cream. --- .../src/renderer/src/lib/themes/index.test.ts | 8 +++++++ .../src/renderer/src/lib/themes/index.ts | 23 +++++++++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/apps/desktop/src/renderer/src/lib/themes/index.test.ts b/apps/desktop/src/renderer/src/lib/themes/index.test.ts index 57c77ded..8e0b88db 100644 --- a/apps/desktop/src/renderer/src/lib/themes/index.test.ts +++ b/apps/desktop/src/renderer/src/lib/themes/index.test.ts @@ -59,6 +59,14 @@ describe("color themes", () => { // Dracula's accent is a pale cyan, so text on it must be dark, not white. expect(properties.get("--color-on-accent")).toBe(properties.get("--color-background")); + // Solarized's mid-tone blue scores slightly higher with dark text, but its light base reads better. + applyResolvedTheme(resolveThemeSelection("solarized-dark", "light"), root); + expect(properties.get("--color-on-accent")).toBe("#fdf6e3"); + + // Text on Gruvbox Light's teal uses the palette's cream rather than pure white. + applyResolvedTheme(resolveThemeSelection("gruvbox-light", "dark"), root); + expect(properties.get("--color-on-accent")).toBe("#fbf1c7"); + applyResolvedTheme(resolveThemeSelection("shift-light", "dark"), root); expect(root.dataset.theme).toBe("light"); expect(properties.has("--color-background")).toBe(false); diff --git a/apps/desktop/src/renderer/src/lib/themes/index.ts b/apps/desktop/src/renderer/src/lib/themes/index.ts index d20af88c..3b7637fa 100644 --- a/apps/desktop/src/renderer/src/lib/themes/index.ts +++ b/apps/desktop/src/renderer/src/lib/themes/index.ts @@ -306,7 +306,7 @@ function colorThemeVariables(theme: ColorTheme): Record { "--color-sidebar-icon": secondary, "--color-accent": palette.base0D, "--color-on-accent": readableOn(palette.base0D, [ - "#ffffff", + lightest(palette.base00, palette.base07), darkest(palette.base00, palette.base07), ]), "--color-error": palette.base08, @@ -363,14 +363,29 @@ function colorThemeVariables(theme: ColorTheme): Record { }; } -/** Returns the candidate text colour with the highest WCAG contrast against `background`. */ -function readableOn(background: string, candidates: readonly string[]): string { +/** How close a preferred candidate's contrast must come to the best one to still win. */ +const PREFERRED_CONTRAST_MARGIN = 0.8; + +/** + * Picks the candidate with the highest WCAG contrast on `background`, except that the + * first candidate wins whenever it comes within 20% of the best. WCAG contrast + * over-favours dark text on saturated mid-tone accents, where light text reads better. + */ +function readableOn(background: string, [preferred, ...others]: readonly string[]): string { const backgroundLuminance = relativeLuminance(background); const contrast = (color: string) => { const [lighter, darker] = [relativeLuminance(color), backgroundLuminance].sort((a, b) => b - a); return (lighter + 0.05) / (darker + 0.05); }; - return candidates.reduce((best, color) => (contrast(color) > contrast(best) ? color : best)); + const best = others.reduce( + (best, color) => (contrast(color) > contrast(best) ? color : best), + preferred, + ); + return contrast(preferred) >= contrast(best) * PREFERRED_CONTRAST_MARGIN ? preferred : best; +} + +function lightest(first: string, second: string): string { + return relativeLuminance(first) >= relativeLuminance(second) ? first : second; } function darkest(first: string, second: string): string { From de5e03f12f4f7c93d92cf1213c312a21178b0535 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 30 Sep 2026 14:05:04 +0000 Subject: [PATCH 3/3] test(e2e): update visual snapshots --- .../landing.spec.ts/landing-default.png | Bin 11552 -> 11033 bytes .../tools.spec.ts/tool-select.png | Bin 1337 -> 1345 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/apps/desktop/e2e/__screenshots__/landing.spec.ts/landing-default.png b/apps/desktop/e2e/__screenshots__/landing.spec.ts/landing-default.png index bf13d92f646b516921494a73a6280c201326b737..a1774676a1d6793bbb71cf312852786958d48c72 100644 GIT binary patch literal 11033 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 A&;S4c literal 11552 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 delta 1318 zcmV+>1=;$+3b_i9B!BKnL_t(|oa~%EOe04W$DcpoEC>k_vOpr}M0pf<1ri1;r<2fC zDI&!c#Z^?-L_$#n7*XdMI3Zm@N{SRzj+EkP0wNe85vof;$_NuABV>sj*z#ffxWE1I zu>LrE7tgF&dwvC8kJqzi=eKX(%zk)du^1KU?Ck9B?)Le7UVpEbH0?n)n@y+F$z<~C z>Pi&eHh*t#Z%T3|R0YpQ!9Re*Gu=%e&0qXCJTW?zo5^``s^}bi8<^GZn`G8B(c~3q1<@6;b-{ z|JOhM`QeYF9Dm8<`9sg^XAg$Ia*@UfTwY$fGMS8V-0}VIX}L}?FypuHe57##7@N3_ zPN45l;RHansGKxT07|%x?~zZCGL%-XkxNN3h#ZrYp|o<1TuQWPWZY2~rIp)2C=}Y; z+xw{iDPU`B>+I~z(pUU`|K#K(Dd7GN9p~rg*Vfj!4Sy81gcmX=T+ zEI^Wg6YJ~iBO@c0zO}iziN7k$xW8J!iQ(blB!-H6h*>SZ8FYUeN%y=-Vi|rw4%L2O*KcfWEH5w5&d%=c?sBKzEI+Ek z9SQ;owH#)(5d6G<1wW$y<)=541`j?Q5h`HD+}vC=8s+k;eo!;uu+dyK6=6Y(r-L4` z96iaw4;z99j>YluahMSfhlLr8@PkuAvYjR-!hfo<94*S=;K9q%gNNAfY{tUE!p_c4 z)zw<^kZmGnwRp9kdHS{IbDu&EWAGq(i1rQy0x)B7adCBZwJfXP?nYw3Kvl>a&^@(e z-e3j`(E?d#-E|-b$)haW9~&DR9Ua}?-sTabvCZTL3{({UlaG(PVyC;$-xs7uMM@L88v!KLSF1@L^@*4F!F|iN_Pu(AHVLA(D zpzZP3SSW0uLPt~fY2)yJf@9S9mYooVOqnPRZnR8`N*o$9OxkRr6R8XXNHCFbzy@TU z`BCeM{FCzB*v7XU#>yqZ!azRDbOC z6*)o0+!poMn5f`$?k%Z>>-zfkvMHXXtPSGCot?n!I7xy}U8-&sp6;x?A`9glCuA9U zx65!24-coNrc_*pV_Azvkf?>7@LYSDf6|S-R~PM-Xz}Hnq`E6ET}1DlsYM%7l5#%w zH+n*~21+Z}$fcyIUOwp{Whkv&BYzinHhX)s*NaEq=MO!k0931QP<9IWn+?T^n(l>%IWE;D;A6C$0KP3{HUaS%88d$ zDn**M2O99>vu?tBB9X}Dayn+TGgTg`M#Y$+AHdjdRDA&BCjbBd|NoD&Umtz|000I_ cL_t&o0ExhkGp$U}OaK4?07*qoM6N<$g7Ff7-T(jq