History

fix(web): keep custom palettes readable

t3-code[bot] committed 3 months ago

Commit f0e4ec41 parent d3c46444 files · +10 −39Browse files

Unified · Split

4 files
  1. apps/web/index.html +2 −12
  2. apps/web/src/hooks/useThemeColors.test.ts +0 −9
  3. apps/web/src/hooks/useThemeColors.ts +2 −13
  4. apps/web/src/index.css +6 −5
apps/web/index.html+2−12
@@ -22,19 +22,9 @@ · 21 unmodified lines
2222 const isHexColor = (value) =>
2323 typeof value === "string" && /^#[0-9a-f]{6}$/i.test(value.trim());
2424 if (isHexColor(colors?.accentColor)) {
25− const accentColor = colors.accentColor.trim().toLowerCase();
26− const channels = [1, 3, 5].map((offset) =>
27− Number.parseInt(accentColor.slice(offset, offset + 2), 16),
28− );
29− const [red, green, blue] = channels.map((channel) => {
30− const value = channel / 255;
31− return value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;
32− });
33− const luminance = 0.2126 * red + 0.7152 * green + 0.0722 * blue;
34− document.documentElement.style.setProperty("--theme-accent-seed", accentColor);
3525 document.documentElement.style.setProperty(
36− "--theme-accent-foreground",
37− luminance >= 0.179 ? "#000000" : "#ffffff",
26+ "--theme-accent-seed",
27+ colors.accentColor.trim().toLowerCase(),
3828 );
3929 }
4030 if (isHexColor(colors?.neutralColor)) {
apps/web/src/hooks/useThemeColors.test.ts+0−9
@@ -86,17 +86,8 @@ describe("theme colors", () => { · 85 unmodified lines
8686 applyThemeColors({ accentColor: "#16a34a", neutralColor: "#78716c", contrast: 50 });
8787
8888 expect(setProperty).toHaveBeenCalledWith("--theme-accent-seed", "#16a34a");
89− expect(setProperty).toHaveBeenCalledWith("--theme-accent-foreground", "#000000");
9089 expect(setProperty).toHaveBeenCalledWith("--theme-neutral-seed", "#78716c");
9190 expect(setProperty).toHaveBeenCalledWith("--theme-accent-strength", "16%");
9291 expect(setProperty).toHaveBeenCalledWith("--theme-border-strength", "16%");
9392 });
94−
95− it("chooses a readable foreground for light and dark accents", async () => {
96− vi.stubGlobal("window", createWindow());
97− const { getThemeAccentForeground } = await import("./useThemeColors");
98−
99− expect(getThemeAccentForeground("#facc15")).toBe("#000000");
100− expect(getThemeAccentForeground("#1d4ed8")).toBe("#ffffff");
101− });
10293 });
apps/web/src/hooks/useThemeColors.ts+2−13
@@ -1,4 +1,5 @@
11 import { useCallback, useSyncExternalStore } from "react";
2+import { syncBrowserChromeTheme } from "./useTheme";
23
34 export type ThemeColors = {
45 accentColor: string;
@@ -36,18 +37,6 @@ export function normalizeThemeColors(value: unknown): ThemeColors { · 31 unmodified lines
3637 };
3738 }
3839
39−export function getThemeAccentForeground(accentColor: string): "#000000" | "#ffffff" {
40− const channels = [1, 3, 5].map((offset) =>
41− Number.parseInt(accentColor.slice(offset, offset + 2), 16),
42− );
43− const [red = 0, green = 0, blue = 0] = channels.map((channel) => {
44− const value = channel / 255;
45− return value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;
46− });
47− const luminance = 0.2126 * red + 0.7152 * green + 0.0722 * blue;
48− return luminance >= 0.179 ? "#000000" : "#ffffff";
49−}
50−
5140 function getThemeContrastProperties(contrast: number): Record<string, string> {
5241 return {
5342 "--theme-background-strength": `${4 + contrast * 0.12}%`,
@@ -72,11 +61,11 @@ export function applyThemeColors(colors: ThemeColors): void { · 18 unmodified lines
7261 if (typeof document === "undefined") return;
7362 const rootStyle = document.documentElement.style;
7463 rootStyle.setProperty("--theme-accent-seed", colors.accentColor);
75− rootStyle.setProperty("--theme-accent-foreground", getThemeAccentForeground(colors.accentColor));
7664 rootStyle.setProperty("--theme-neutral-seed", colors.neutralColor);
7765 for (const [property, value] of Object.entries(getThemeContrastProperties(colors.contrast))) {
7866 rootStyle.setProperty(property, value);
7967 }
68+ syncBrowserChromeTheme();
8069 }
8170
8271 function emitChange(): void {
apps/web/src/index.css+6−5
@@ -854,7 +854,6 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil · 853 unmodified lines
854854 color-scheme: light;
855855 --radius: 0.625rem;
856856 --theme-accent-seed: #3b5bdb;
857− --theme-accent-foreground: #ffffff;
858857 --theme-neutral-seed: #737373;
859858 --theme-background-strength: 10%;
860859 --theme-surface-strength: 14%;
@@ -873,8 +872,10 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil · 12 unmodified lines
873872 --card-foreground: var(--foreground);
874873 --popover: var(--card);
875874 --popover-foreground: var(--foreground);
876− --primary: var(--theme-accent-seed);
877− --primary-foreground: var(--theme-accent-foreground);
875+ /* Keep text, rings, and translucent primary controls readable for any seed.
876+ Raw seed color still drives the more expressive accent surfaces below. */
877+ --primary: color-mix(in oklch, var(--theme-accent-seed) 46%, var(--color-black));
878+ --primary-foreground: var(--color-white);
878879 --secondary: color-mix(
879880 in srgb,
880881 var(--theme-neutral-seed) var(--theme-control-strength),
@@ -932,8 +933,8 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil · 51 unmodified lines
932933 var(--background)
933934 );
934935 --popover-foreground: var(--foreground);
935− --primary: var(--theme-accent-seed);
936− --primary-foreground: var(--theme-accent-foreground);
936+ --primary: color-mix(in oklch, var(--theme-accent-seed) 40%, var(--color-white));
937+ --primary-foreground: var(--color-black);
937938 --secondary: color-mix(
938939 in srgb,
939940 var(--theme-neutral-seed) var(--theme-surface-strength),