1
18
19import { BLOCK_COLORS, STATUS_COLORS } from "@factory/shared/constants";
20
21
22export const TOKEN_NAMES = [
23 "background",
24 "foreground",
25 "card",
26 "card-foreground",
27 "popover",
28 "popover-foreground",
29 "primary",
30 "primary-foreground",
31 "secondary",
32 "muted",
33 "muted-foreground",
34 "accent",
35 "accent-foreground",
36 "destructive",
37 "destructive-foreground",
38 "border",
39 "input",
40 "ring",
41 "sidebar",
42 "editor-background",
43 "editor-foreground",
44 "selection",
45 "success",
46 "success-bg",
47 "warning",
48 "warning-bg",
49 "info",
50 "info-bg",
51 "focus-ring",
52] as const;
53
54export type TokenName = (typeof TOKEN_NAMES)[number];
55
56
57export const LIGHT_TOKENS: Record<TokenName, string> = {
58 background: "#FFFFFF",
59 foreground: "#37352F",
60 card: "#FFFFFF",
61 "card-foreground": "#37352F",
62 popover: "#FFFFFF",
63 "popover-foreground": "#37352F",
64 primary: "#37352F",
65 "primary-foreground": "#FFFFFF",
66 secondary: "#F1F1EF",
67 muted: "#F7F6F3",
68 "muted-foreground": "#787C7E",
69 accent: "#EBEBE7",
70 "accent-foreground": "#37352F",
71 destructive: "#E03E3E",
72 "destructive-foreground": "#FFFFFF",
73 border: "#E9E9E7",
74 input: "#D3D1CB",
75 ring: "#337EA9",
76 sidebar: "#F7F6F3",
77 "editor-background": "#FFFFFF",
78 "editor-foreground": "#37352F",
79 selection: "#D7E7F5",
80 success: "#448361",
81 "success-bg": "#EDF3EC",
82 warning: "#CB912F",
83 "warning-bg": "#FDF3D0",
84 info: "#337EA9",
85 "info-bg": "#E7F0F6",
86 "focus-ring": "rgba(51, 126, 169, 0.4)",
87};
88
89
90export const DARK_TOKENS: Record<TokenName, string> = {
91 background: "#191919",
92 foreground: "#DFDEDC",
93 card: "#202020",
94 "card-foreground": "#DFDEDC",
95 popover: "#252525",
96 "popover-foreground": "#DFDEDC",
97 primary: "#E9E9E7",
98 "primary-foreground": "#191919",
99 secondary: "#2F2F2F",
100 muted: "#2A2A2A",
101 "muted-foreground": "#9B9B98",
102 accent: "#373737",
103 "accent-foreground": "#DFDEDC",
104 destructive: "#E03E3E",
105 "destructive-foreground": "#FFFFFF",
106 border: "#2F2F2F",
107 input: "#444444",
108 ring: "#529CCA",
109 sidebar: "#1E1E1E",
110 "editor-background": "#191919",
111 "editor-foreground": "#DFDEDC",
112 selection: "#3A5F7E",
113 success: "#6FBF8E",
114 "success-bg": "#22352B",
115 warning: "#D9A94B",
116 "warning-bg": "#3A3120",
117 info: "#529CCA",
118 "info-bg": "#233A4A",
119 "focus-ring": "rgba(82, 156, 202, 0.45)",
120};
121
122
123export const BLOCK_COLOR_KEYS = BLOCK_COLORS.map((c) => `block-color-${c.token}`) as string[];
124
125
126export const STATUS_COLOR_KEYS = STATUS_COLORS.map((s) => `status-${s}`) as string[];
127
128
129const LIGHT_BLOCK_COLORS: Record<string, string> = Object.fromEntries(
130 BLOCK_COLORS.map((c) => [`block-color-${c.token}`, c.hex]),
131);
132
133const DARK_BLOCK_COLORS: Record<string, string> = { ...LIGHT_BLOCK_COLORS };
134
135
136const lightStatus = (): Record<string, string> => {
137 const out: Record<string, string> = {};
138 for (const s of STATUS_COLORS) {
139 out[`status-${s}`] = LIGHT_BLOCK_COLORS[`block-color-${s}`] ?? "inherit";
140 }
141 return out;
142};
143const DARK_STATUS_COLORS: Record<string, string> = lightStatus();
144const LIGHT_STATUS_COLORS: Record<string, string> = lightStatus();
145
146
150export const THEME_TOKENS: { light: Record<string, string>; dark: Record<string, string> } = {
151 light: { ...LIGHT_TOKENS, ...LIGHT_BLOCK_COLORS, ...LIGHT_STATUS_COLORS },
152 dark: { ...DARK_TOKENS, ...DARK_BLOCK_COLORS, ...DARK_STATUS_COLORS },
153};
154
155function emitRule(selector: string, values: Record<string, string>): string {
156 const lines = Object.keys(values)
157 .map((name) => ` --${name}: ${values[name]};`)
158 .join("\n");
159 return `${selector} {\n${lines}\n}`;
160}
161
162
166export function cssVariables(theme: "light" | "dark" = "light"): string {
167 if (theme === "light") {
168 return emitRule(":root", THEME_TOKENS.light);
169 }
170 return emitRule('[data-theme="dark"]', THEME_TOKENS.dark);
171}
172
173
174export function cssTokensFile(): string {
175 return [
176 "/* @factory/ui tokens.css — generated by tokens.ts (do not edit by hand).",
177 ' * Light values on :root; dark overrides on the data-theme dark selector.',
178 " * Block/status colors are reused from @factory/shared (BLOCK_COLORS/STATUS_COLORS). */",
179 "",
180 cssVariables("light"),
181 "",
182 cssVariables("dark"),
183 "",
184 ].join("\n");
185}
186