authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-09-17 18:55:54-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-17 18:55:54-07:00
log53510d44ea7b4a44c10c122dd9245194f2f14d09
tree72ef847f8f5295593849bb05b88df499f07d9737
parent3fd5d6439d8fd49d173503ecda96500463a39bd2
signature Signed by PGP key B5690EEEBB952194

fix(mobile): define Clerk colors in every Uniwind theme (#12344)


4 files changed, 115 insertions(+), 27 deletions(-)

apps/mobile/generated-uniwind-themes.css+60
......@@ -59,6 +59,11 @@
5959 --color-adaptive-zinc-500-a12-a16: oklch(55.2% 0.016 285.938 / 12%);
6060 --color-adaptive-zinc-500-400: oklch(55.2% 0.016 285.938);
6161 --color-adaptive-zinc-600-300: oklch(44.2% 0.017 285.786);
62 --color-clerk-page: #f2f2f7;
63 --color-clerk-foreground: #262626;
64 --color-clerk-foreground-muted: #737373;
65 --color-clerk-border: rgba(229, 229, 234, 0.06);
66 --color-clerk-danger: #dc2626;
6267 }
6368
6469 @variant dark {
......@@ -119,6 +124,11 @@
119124 --color-adaptive-zinc-500-a12-a16: oklch(55.2% 0.016 285.938 / 16%);
120125 --color-adaptive-zinc-500-400: oklch(70.5% 0.015 286.067);
121126 --color-adaptive-zinc-600-300: oklch(87.1% 0.006 286.286);
127 --color-clerk-page: #0e0e0e;
128 --color-clerk-foreground: #f5f5f5;
129 --color-clerk-foreground-muted: #a3a3a3;
130 --color-clerk-border: rgba(42, 42, 42, 0.06);
131 --color-clerk-danger: #fca5a5;
122132 }
123133
124134 @variant t3-chat-light {
......@@ -254,6 +264,11 @@
254264 --color-adaptive-zinc-500-a12-a16: oklch(55.2% 0.016 285.938 / 12%);
255265 --color-adaptive-zinc-500-400: oklch(55.2% 0.016 285.938);
256266 --color-adaptive-zinc-600-300: oklch(44.2% 0.017 285.786);
267 --color-clerk-page: #f2f2f7;
268 --color-clerk-foreground: #262626;
269 --color-clerk-foreground-muted: #737373;
270 --color-clerk-border: rgba(229, 229, 234, 0.06);
271 --color-clerk-danger: #dc2626;
257272 }
258273
259274 @variant t3-chat-dark {
......@@ -389,6 +404,11 @@
389404 --color-adaptive-zinc-500-a12-a16: oklch(55.2% 0.016 285.938 / 16%);
390405 --color-adaptive-zinc-500-400: oklch(70.5% 0.015 286.067);
391406 --color-adaptive-zinc-600-300: oklch(87.1% 0.006 286.286);
407 --color-clerk-page: #0e0e0e;
408 --color-clerk-foreground: #f5f5f5;
409 --color-clerk-foreground-muted: #a3a3a3;
410 --color-clerk-border: rgba(42, 42, 42, 0.06);
411 --color-clerk-danger: #fca5a5;
392412 }
393413
394414 @variant grove-light {
......@@ -524,6 +544,11 @@
524544 --color-adaptive-zinc-500-a12-a16: oklch(55.2% 0.016 285.938 / 12%);
525545 --color-adaptive-zinc-500-400: oklch(55.2% 0.016 285.938);
526546 --color-adaptive-zinc-600-300: oklch(44.2% 0.017 285.786);
547 --color-clerk-page: #f2f2f7;
548 --color-clerk-foreground: #262626;
549 --color-clerk-foreground-muted: #737373;
550 --color-clerk-border: rgba(229, 229, 234, 0.06);
551 --color-clerk-danger: #dc2626;
527552 }
528553
529554 @variant grove-dark {
......@@ -659,6 +684,11 @@
659684 --color-adaptive-zinc-500-a12-a16: oklch(55.2% 0.016 285.938 / 16%);
660685 --color-adaptive-zinc-500-400: oklch(70.5% 0.015 286.067);
661686 --color-adaptive-zinc-600-300: oklch(87.1% 0.006 286.286);
687 --color-clerk-page: #0e0e0e;
688 --color-clerk-foreground: #f5f5f5;
689 --color-clerk-foreground-muted: #a3a3a3;
690 --color-clerk-border: rgba(42, 42, 42, 0.06);
691 --color-clerk-danger: #fca5a5;
662692 }
663693
664694 @variant ocean-light {
......@@ -794,6 +824,11 @@
794824 --color-adaptive-zinc-500-a12-a16: oklch(55.2% 0.016 285.938 / 12%);
795825 --color-adaptive-zinc-500-400: oklch(55.2% 0.016 285.938);
796826 --color-adaptive-zinc-600-300: oklch(44.2% 0.017 285.786);
827 --color-clerk-page: #f2f2f7;
828 --color-clerk-foreground: #262626;
829 --color-clerk-foreground-muted: #737373;
830 --color-clerk-border: rgba(229, 229, 234, 0.06);
831 --color-clerk-danger: #dc2626;
797832 }
798833
799834 @variant ocean-dark {
......@@ -929,6 +964,11 @@
929964 --color-adaptive-zinc-500-a12-a16: oklch(55.2% 0.016 285.938 / 16%);
930965 --color-adaptive-zinc-500-400: oklch(70.5% 0.015 286.067);
931966 --color-adaptive-zinc-600-300: oklch(87.1% 0.006 286.286);
967 --color-clerk-page: #0e0e0e;
968 --color-clerk-foreground: #f5f5f5;
969 --color-clerk-foreground-muted: #a3a3a3;
970 --color-clerk-border: rgba(42, 42, 42, 0.06);
971 --color-clerk-danger: #fca5a5;
932972 }
933973
934974 @variant ember-light {
......@@ -1064,6 +1104,11 @@
10641104 --color-adaptive-zinc-500-a12-a16: oklch(55.2% 0.016 285.938 / 12%);
10651105 --color-adaptive-zinc-500-400: oklch(55.2% 0.016 285.938);
10661106 --color-adaptive-zinc-600-300: oklch(44.2% 0.017 285.786);
1107 --color-clerk-page: #f2f2f7;
1108 --color-clerk-foreground: #262626;
1109 --color-clerk-foreground-muted: #737373;
1110 --color-clerk-border: rgba(229, 229, 234, 0.06);
1111 --color-clerk-danger: #dc2626;
10671112 }
10681113
10691114 @variant ember-dark {
......@@ -1199,6 +1244,11 @@
11991244 --color-adaptive-zinc-500-a12-a16: oklch(55.2% 0.016 285.938 / 16%);
12001245 --color-adaptive-zinc-500-400: oklch(70.5% 0.015 286.067);
12011246 --color-adaptive-zinc-600-300: oklch(87.1% 0.006 286.286);
1247 --color-clerk-page: #0e0e0e;
1248 --color-clerk-foreground: #f5f5f5;
1249 --color-clerk-foreground-muted: #a3a3a3;
1250 --color-clerk-border: rgba(42, 42, 42, 0.06);
1251 --color-clerk-danger: #fca5a5;
12021252 }
12031253
12041254 @variant iris-light {
......@@ -1334,6 +1384,11 @@
13341384 --color-adaptive-zinc-500-a12-a16: oklch(55.2% 0.016 285.938 / 12%);
13351385 --color-adaptive-zinc-500-400: oklch(55.2% 0.016 285.938);
13361386 --color-adaptive-zinc-600-300: oklch(44.2% 0.017 285.786);
1387 --color-clerk-page: #f2f2f7;
1388 --color-clerk-foreground: #262626;
1389 --color-clerk-foreground-muted: #737373;
1390 --color-clerk-border: rgba(229, 229, 234, 0.06);
1391 --color-clerk-danger: #dc2626;
13371392 }
13381393
13391394 @variant iris-dark {
......@@ -1469,6 +1524,11 @@
14691524 --color-adaptive-zinc-500-a12-a16: oklch(55.2% 0.016 285.938 / 16%);
14701525 --color-adaptive-zinc-500-400: oklch(70.5% 0.015 286.067);
14711526 --color-adaptive-zinc-600-300: oklch(87.1% 0.006 286.286);
1527 --color-clerk-page: #0e0e0e;
1528 --color-clerk-foreground: #f5f5f5;
1529 --color-clerk-foreground-muted: #a3a3a3;
1530 --color-clerk-border: rgba(42, 42, 42, 0.06);
1531 --color-clerk-danger: #fca5a5;
14721532 }
14731533 }
14741534}
apps/mobile/global.css-25
......@@ -233,31 +233,6 @@
233233 }
234234}
235235
236/* ─── Clerk native profile ──────────────────────────────────────────── */
237/* Fixed palette for custom pages inside Clerk's native user profile. Mirrors
238 clerk-theme.json, which themes the SDK's own screens, so ours match them.
239 Kept out of the runtime palette above on purpose: custom themes must not
240 restyle Clerk's chrome. Keep in sync with clerk-theme.json. */
241@layer theme {
242 :root {
243 @variant light {
244 --color-clerk-page: #f2f2f7;
245 --color-clerk-foreground: #262626;
246 --color-clerk-foreground-muted: #737373;
247 --color-clerk-border: rgba(229, 229, 234, 0.06);
248 --color-clerk-danger: #dc2626;
249 }
250
251 @variant dark {
252 --color-clerk-page: #0e0e0e;
253 --color-clerk-foreground: #f5f5f5;
254 --color-clerk-foreground-muted: #a3a3a3;
255 --color-clerk-border: rgba(42, 42, 42, 0.06);
256 --color-clerk-danger: #fca5a5;
257 }
258 }
259}
260
261236/* ─── Typography ────────────────────────────────────────────────────── */
262237@theme {
263238 /* Keep these native family names aligned with app.config.ts. */
apps/mobile/scripts/generate-uniwind-themes.mts+22-2
......@@ -5,9 +5,11 @@ import * as NodePath from "node:path";
55import tailwindColors from "tailwindcss/colors";
66import { BUILT_IN_THEME_IDS, type BuiltInThemeId } from "@t3tools/shared/themePalettes";
77
8import clerkTheme from "../clerk-theme.json" with { type: "json" };
89import {
910 getMobileThemeVariables,
1011 MOBILE_THEME_VARIABLE_NAMES,
12 themeColorWithAlpha,
1113 type MobileThemeAppearance,
1214 type MobileThemeVariables,
1315} from "../src/lib/mobileTheme.ts";
......@@ -152,8 +154,22 @@ const adaptiveVariablesFor = (appearance: MobileThemeAppearance) =>
152154const variablesFor = (themeId: BuiltInThemeId, appearance: MobileThemeAppearance) => ({
153155 ...getMobileThemeVariables(themeId, appearance),
154156 ...adaptiveVariablesFor(appearance),
157 ...clerkVariablesFor(appearance),
155158});
156159
160// Clerk's native screens use one build-time palette per appearance. Custom
161// profile pages must match it even when the rest of the app uses a named theme.
162const clerkVariablesFor = (appearance: MobileThemeAppearance) => {
163 const colors = appearance === "dark" ? clerkTheme.darkColors : clerkTheme.colors;
164 return {
165 "--color-clerk-page": colors.background.toLowerCase(),
166 "--color-clerk-foreground": colors.foreground.toLowerCase(),
167 "--color-clerk-foreground-muted": colors.mutedForeground.toLowerCase(),
168 "--color-clerk-border": themeColorWithAlpha(colors.border, 0.06),
169 "--color-clerk-danger": colors.danger.toLowerCase(),
170 };
171};
172
157173const renderVariant = (name: string, variables: Readonly<Record<string, string>>) => {
158174 const declarations = Object.entries(variables)
159175 .map(([variable, value]) => ` ${variable}: ${value};`)
......@@ -163,8 +179,12 @@ const renderVariant = (name: string, variables: Readonly<Record<string, string>>
163179
164180export const renderUniwindThemesCSS = () => {
165181 const variants = [
166 renderVariant("light", adaptiveVariablesFor("light")),
167 renderVariant("dark", adaptiveVariablesFor("dark")),
182 ...APPEARANCES.map((appearance) =>
183 renderVariant(appearance, {
184 ...adaptiveVariablesFor(appearance),
185 ...clerkVariablesFor(appearance),
186 }),
187 ),
168188 ...BUILT_IN_THEME_IDS.flatMap((themeId) =>
169189 APPEARANCES.map((appearance) =>
170190 renderVariant(`${themeId}-${appearance}`, variablesFor(themeId, appearance)),
apps/mobile/scripts/generate-uniwind-themes.test.ts+33
......@@ -52,4 +52,37 @@ describe("generate mobile Uniwind themes", () => {
5252 expect(variables.dark["--color-screen"]).toBe("#0a0a0a");
5353 expect(Object.keys(variables.light)).toEqual(Object.keys(variables.dark));
5454 });
55
56 it("gives every theme the same variables and a fixed Clerk palette for its appearance", () => {
57 const css =
58 NodeFS.readFileSync(NodePath.resolve(import.meta.dirname, "../global.css"), "utf8") +
59 renderUniwindThemesCSS();
60 const themes = new Map<string, Map<string, string>>(
61 ["light", "dark", ...customThemeNames].map((name) => [name, new Map()]),
62 );
63 for (const [, name, body] of css.matchAll(/@variant ([\w-]+) \{([^}]+)\}/gu)) {
64 const variables = themes.get(name!);
65 for (const [, variable, value] of body!.matchAll(/(--[\w-]+):\s*([^;]+);/gu)) {
66 variables?.set(variable!, value!.trim().toLowerCase());
67 }
68 }
69
70 const lightVariables = themes.get("light")!;
71 for (const [name, variables] of themes) {
72 expect([...variables.keys()].sort(), name).toEqual([...lightVariables.keys()].sort());
73 const isDark = name === "dark" || name.endsWith("-dark");
74 expect(
75 Object.fromEntries(
76 [...variables].filter(([variable]) => variable.startsWith("--color-clerk-")),
77 ),
78 name,
79 ).toEqual({
80 "--color-clerk-page": isDark ? "#0e0e0e" : "#f2f2f7",
81 "--color-clerk-foreground": isDark ? "#f5f5f5" : "#262626",
82 "--color-clerk-foreground-muted": isDark ? "#a3a3a3" : "#737373",
83 "--color-clerk-border": isDark ? "rgba(42, 42, 42, 0.06)" : "rgba(229, 229, 234, 0.06)",
84 "--color-clerk-danger": isDark ? "#fca5a5" : "#dc2626",
85 });
86 }
87 });
5588});