@tangle-network/brand 1.15.7 → 1.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.17.0
4
+
5
+ ### Minor Changes
6
+
7
+ - ffd18ec: `palettes` exports Brand's resolved colors (light, dark, websiteLight, websiteDark) for renderers that cannot read CSS variables, such as PDF, canvas, WebGL, email and the theme-color meta. `scripts/gen-palette.mjs` generates them from tokens.css and named-themes.css, and the build fails when they are stale.
8
+
9
+ ## 1.16.0
10
+
11
+ ### Minor Changes
12
+
13
+ - e012234: The canonical surface ladder is GTM's. Dark is indigo-lifted (`#0a0a14` canvas, `#191826` card, `#221f33` nested, `#2c2942` overlay, `#2a293d` hairline); light is a cool `#eceef3` canvas with white paper, `#f1f2f7` wells and a `#c7c6d6` hairline; light syntax uses GTM's palette. Every product on the default theme now renders GTM's surfaces without restating them, and `tangle-dark` repeats the new spine. Ink stays achromatic and surfaces stay below 0.1 chroma, so the accent still signals interaction. Charts regenerates its CSS from the new tokens.
14
+
3
15
  ## 1.15.7
4
16
 
5
17
  ### Patch Changes
package/README.md CHANGED
@@ -101,7 +101,9 @@ import { Logo, TangleKnot } from "@tangle-network/brand";
101
101
 
102
102
  ## Palette and typography
103
103
 
104
- The base dark surface ladder is neutral `#161616` / `#303030` / `#353535` / `#3b3b3b`. Light uses a neutral `#eaeaea` canvas, white paper cards and overlays, and `#f2f2f2` nested wells. Indigo is the interaction accent, not the base field. Existing named products may supply deliberate surface overrides.
104
+ Renderers that cannot read CSS variables (PDF, canvas, WebGL, email, the `theme-color` meta) import `palettes` from `@tangle-network/brand`: resolved hex values for `light`, `dark`, `websiteLight` and `websiteDark`, generated from the stylesheets by `pnpm gen:palette`. Pages styled with CSS keep using the variables.
105
+
106
+ The base surface ladder is the one GTM proved out: dark is indigo-lifted `#0a0a14` / `#191826` / `#221f33` / `#2c2942`; light is a cool `#eceef3` canvas, white paper cards and overlays, and `#f1f2f7` nested wells. The cast stays below 0.1 chroma and ink stays achromatic, so indigo still reads as the interaction accent, not the base field. Existing named products may supply deliberate surface overrides.
105
107
 
106
108
  Chrome radii are 6/8/10/12px in both base modes; the composer has its own 26px role. Inter leads body and display stacks, with Geist fallbacks; Geist Mono leads code with JetBrains Mono and system fallbacks. Runtime font/radius registrations retain their public override names; tests keep Tailwind registration defaults aligned with the canonical declarations.
107
109
 
package/dist/index.d.ts CHANGED
@@ -12,4 +12,175 @@ export declare function TangleKnot({ size, className }: {
12
12
  }): React.JSX.Element;
13
13
  export declare function Logo({ size, variant, suffix, className }: LogoProps): React.JSX.Element;
14
14
  //#endregion
15
- export type { LogoProps };
15
+ //#region src/palette.generated.d.ts
16
+ /** A theme's resolved roles: hex strings, or rgba() where the token is translucent. */
17
+ interface BrandPalette {
18
+ canvas: string;
19
+ card: string;
20
+ well: string;
21
+ nested: string;
22
+ overlay: string;
23
+ deepest: string;
24
+ hairline: string;
25
+ ink: string;
26
+ inkSecondary: string;
27
+ inkMuted: string;
28
+ accent: string;
29
+ accentHover: string;
30
+ onAccent: string;
31
+ accentText: string;
32
+ danger: string;
33
+ successBg: string;
34
+ successBorder: string;
35
+ successText: string;
36
+ warningBg: string;
37
+ warningBorder: string;
38
+ warningText: string;
39
+ dangerBg: string;
40
+ dangerBorder: string;
41
+ dangerText: string;
42
+ infoBg: string;
43
+ infoBorder: string;
44
+ infoText: string;
45
+ brandVibrant: string;
46
+ brandCool: string;
47
+ brandGlow: string;
48
+ brandPurple: string;
49
+ }
50
+ /** Resolved palettes for the canonical light and dark themes and the website named theme. */
51
+ export declare const palettes: {
52
+ readonly light: {
53
+ readonly canvas: "#eceef3";
54
+ readonly card: "#ffffff";
55
+ readonly well: "#f6f7fa";
56
+ readonly nested: "#f1f2f7";
57
+ readonly overlay: "#ffffff";
58
+ readonly deepest: "#ffffff";
59
+ readonly hairline: "#c7c6d6";
60
+ readonly ink: "#2d2d2d";
61
+ readonly inkSecondary: "#3f3f3f";
62
+ readonly inkMuted: "#4c4c4c";
63
+ readonly accent: "#4c3fc5";
64
+ readonly accentHover: "#5a50d6";
65
+ readonly onAccent: "#ffffff";
66
+ readonly accentText: "#3c32e9";
67
+ readonly danger: "#b41d1d";
68
+ readonly successBg: "#ecfdf5";
69
+ readonly successBorder: "#a7f3d0";
70
+ readonly successText: "#047857";
71
+ readonly warningBg: "#fffbeb";
72
+ readonly warningBorder: "#fde68a";
73
+ readonly warningText: "#ab4f09";
74
+ readonly dangerBg: "#fff1f2";
75
+ readonly dangerBorder: "#fecdd3";
76
+ readonly dangerText: "#b91c1c";
77
+ readonly infoBg: "#f0f9ff";
78
+ readonly infoBorder: "#bae6fd";
79
+ readonly infoText: "#0369a1";
80
+ readonly brandVibrant: "#6366f1";
81
+ readonly brandCool: "#818cf8";
82
+ readonly brandGlow: "#a5aafc";
83
+ readonly brandPurple: "#6366f1";
84
+ };
85
+ readonly dark: {
86
+ readonly canvas: "#0a0a14";
87
+ readonly card: "#191826";
88
+ readonly well: "#12111f";
89
+ readonly nested: "#221f33";
90
+ readonly overlay: "#2c2942";
91
+ readonly deepest: "#050509";
92
+ readonly hairline: "#2a293d";
93
+ readonly ink: "#e6e6e6";
94
+ readonly inkSecondary: "#c7c7c7";
95
+ readonly inkMuted: "#b5b5b5";
96
+ readonly accent: "#4c3fc5";
97
+ readonly accentHover: "#5a50d6";
98
+ readonly onAccent: "#ffffff";
99
+ readonly accentText: "#a5b4fc";
100
+ readonly danger: "#d52044";
101
+ readonly successBg: "#2f5146";
102
+ readonly successBorder: "#3c7760";
103
+ readonly successText: "#5ed7a4";
104
+ readonly warningBg: "#4b4232";
105
+ readonly warningBorder: "#7a6236";
106
+ readonly warningText: "#f4c14f";
107
+ readonly dangerBg: "#5a3b40";
108
+ readonly dangerBorder: "#7d4d53";
109
+ readonly dangerText: "#ffada9";
110
+ readonly infoBg: "#2d4e67";
111
+ readonly infoBorder: "#3d7292";
112
+ readonly infoText: "#67cdff";
113
+ readonly brandVibrant: "#6366f1";
114
+ readonly brandCool: "#818cf8";
115
+ readonly brandGlow: "#a5aafc";
116
+ readonly brandPurple: "#c7d2fe";
117
+ };
118
+ readonly websiteLight: {
119
+ readonly canvas: "#f5f5fb";
120
+ readonly card: "#ffffff";
121
+ readonly well: "#fafafd";
122
+ readonly nested: "#ececf6";
123
+ readonly overlay: "#ffffff";
124
+ readonly deepest: "#ffffff";
125
+ readonly hairline: "#dbdbea";
126
+ readonly ink: "#191c24";
127
+ readonly inkSecondary: "#3a3a50";
128
+ readonly inkMuted: "#5e5e7a";
129
+ readonly accent: "#4c3fc5";
130
+ readonly accentHover: "#5a50d6";
131
+ readonly onAccent: "#ffffff";
132
+ readonly accentText: "#3c32e9";
133
+ readonly danger: "#b41d1d";
134
+ readonly successBg: "#ecfdf5";
135
+ readonly successBorder: "#a7f3d0";
136
+ readonly successText: "#047857";
137
+ readonly warningBg: "#fffbeb";
138
+ readonly warningBorder: "#fde68a";
139
+ readonly warningText: "#ab4f09";
140
+ readonly dangerBg: "#fff1f2";
141
+ readonly dangerBorder: "#fecdd3";
142
+ readonly dangerText: "#b91c1c";
143
+ readonly infoBg: "#f0f9ff";
144
+ readonly infoBorder: "#bae6fd";
145
+ readonly infoText: "#0369a1";
146
+ readonly brandVibrant: "#6366f1";
147
+ readonly brandCool: "#818cf8";
148
+ readonly brandGlow: "#a5aafc";
149
+ readonly brandPurple: "#6366f1";
150
+ };
151
+ readonly websiteDark: {
152
+ readonly canvas: "#0c0b1d";
153
+ readonly card: "#1d1b38";
154
+ readonly well: "#141328";
155
+ readonly nested: "#222041";
156
+ readonly overlay: "#262448";
157
+ readonly deepest: "#080814";
158
+ readonly hairline: "#33315f";
159
+ readonly ink: "#fdfdfd";
160
+ readonly inkSecondary: "#aaa8b8";
161
+ readonly inkMuted: "#9496bc";
162
+ readonly accent: "#4c3fc5";
163
+ readonly accentHover: "#5a50d6";
164
+ readonly onAccent: "#ffffff";
165
+ readonly accentText: "#a5b4fc";
166
+ readonly danger: "#d52044";
167
+ readonly successBg: "#2f5146";
168
+ readonly successBorder: "#3c7760";
169
+ readonly successText: "#5ed7a4";
170
+ readonly warningBg: "#4b4232";
171
+ readonly warningBorder: "#7a6236";
172
+ readonly warningText: "#f4c14f";
173
+ readonly dangerBg: "#5a3b40";
174
+ readonly dangerBorder: "#7d4d53";
175
+ readonly dangerText: "#ffada9";
176
+ readonly infoBg: "#2d4e67";
177
+ readonly infoBorder: "#3d7292";
178
+ readonly infoText: "#67cdff";
179
+ readonly brandVibrant: "#6366f1";
180
+ readonly brandCool: "#818cf8";
181
+ readonly brandGlow: "#a5aafc";
182
+ readonly brandPurple: "#c7d2fe";
183
+ };
184
+ };
185
+ //#endregion
186
+ export type { BrandPalette, LogoProps };
package/dist/index.js CHANGED
@@ -105,4 +105,141 @@ function Logo({ size = "md", variant = "full", suffix = null, className }) {
105
105
  });
106
106
  }
107
107
  //#endregion
108
- export { Logo, TangleKnot };
108
+ //#region src/palette.generated.ts
109
+ /** Resolved palettes for the canonical light and dark themes and the website named theme. */
110
+ const palettes = {
111
+ light: {
112
+ canvas: "#eceef3",
113
+ card: "#ffffff",
114
+ well: "#f6f7fa",
115
+ nested: "#f1f2f7",
116
+ overlay: "#ffffff",
117
+ deepest: "#ffffff",
118
+ hairline: "#c7c6d6",
119
+ ink: "#2d2d2d",
120
+ inkSecondary: "#3f3f3f",
121
+ inkMuted: "#4c4c4c",
122
+ accent: "#4c3fc5",
123
+ accentHover: "#5a50d6",
124
+ onAccent: "#ffffff",
125
+ accentText: "#3c32e9",
126
+ danger: "#b41d1d",
127
+ successBg: "#ecfdf5",
128
+ successBorder: "#a7f3d0",
129
+ successText: "#047857",
130
+ warningBg: "#fffbeb",
131
+ warningBorder: "#fde68a",
132
+ warningText: "#ab4f09",
133
+ dangerBg: "#fff1f2",
134
+ dangerBorder: "#fecdd3",
135
+ dangerText: "#b91c1c",
136
+ infoBg: "#f0f9ff",
137
+ infoBorder: "#bae6fd",
138
+ infoText: "#0369a1",
139
+ brandVibrant: "#6366f1",
140
+ brandCool: "#818cf8",
141
+ brandGlow: "#a5aafc",
142
+ brandPurple: "#6366f1"
143
+ },
144
+ dark: {
145
+ canvas: "#0a0a14",
146
+ card: "#191826",
147
+ well: "#12111f",
148
+ nested: "#221f33",
149
+ overlay: "#2c2942",
150
+ deepest: "#050509",
151
+ hairline: "#2a293d",
152
+ ink: "#e6e6e6",
153
+ inkSecondary: "#c7c7c7",
154
+ inkMuted: "#b5b5b5",
155
+ accent: "#4c3fc5",
156
+ accentHover: "#5a50d6",
157
+ onAccent: "#ffffff",
158
+ accentText: "#a5b4fc",
159
+ danger: "#d52044",
160
+ successBg: "#2f5146",
161
+ successBorder: "#3c7760",
162
+ successText: "#5ed7a4",
163
+ warningBg: "#4b4232",
164
+ warningBorder: "#7a6236",
165
+ warningText: "#f4c14f",
166
+ dangerBg: "#5a3b40",
167
+ dangerBorder: "#7d4d53",
168
+ dangerText: "#ffada9",
169
+ infoBg: "#2d4e67",
170
+ infoBorder: "#3d7292",
171
+ infoText: "#67cdff",
172
+ brandVibrant: "#6366f1",
173
+ brandCool: "#818cf8",
174
+ brandGlow: "#a5aafc",
175
+ brandPurple: "#c7d2fe"
176
+ },
177
+ websiteLight: {
178
+ canvas: "#f5f5fb",
179
+ card: "#ffffff",
180
+ well: "#fafafd",
181
+ nested: "#ececf6",
182
+ overlay: "#ffffff",
183
+ deepest: "#ffffff",
184
+ hairline: "#dbdbea",
185
+ ink: "#191c24",
186
+ inkSecondary: "#3a3a50",
187
+ inkMuted: "#5e5e7a",
188
+ accent: "#4c3fc5",
189
+ accentHover: "#5a50d6",
190
+ onAccent: "#ffffff",
191
+ accentText: "#3c32e9",
192
+ danger: "#b41d1d",
193
+ successBg: "#ecfdf5",
194
+ successBorder: "#a7f3d0",
195
+ successText: "#047857",
196
+ warningBg: "#fffbeb",
197
+ warningBorder: "#fde68a",
198
+ warningText: "#ab4f09",
199
+ dangerBg: "#fff1f2",
200
+ dangerBorder: "#fecdd3",
201
+ dangerText: "#b91c1c",
202
+ infoBg: "#f0f9ff",
203
+ infoBorder: "#bae6fd",
204
+ infoText: "#0369a1",
205
+ brandVibrant: "#6366f1",
206
+ brandCool: "#818cf8",
207
+ brandGlow: "#a5aafc",
208
+ brandPurple: "#6366f1"
209
+ },
210
+ websiteDark: {
211
+ canvas: "#0c0b1d",
212
+ card: "#1d1b38",
213
+ well: "#141328",
214
+ nested: "#222041",
215
+ overlay: "#262448",
216
+ deepest: "#080814",
217
+ hairline: "#33315f",
218
+ ink: "#fdfdfd",
219
+ inkSecondary: "#aaa8b8",
220
+ inkMuted: "#9496bc",
221
+ accent: "#4c3fc5",
222
+ accentHover: "#5a50d6",
223
+ onAccent: "#ffffff",
224
+ accentText: "#a5b4fc",
225
+ danger: "#d52044",
226
+ successBg: "#2f5146",
227
+ successBorder: "#3c7760",
228
+ successText: "#5ed7a4",
229
+ warningBg: "#4b4232",
230
+ warningBorder: "#7a6236",
231
+ warningText: "#f4c14f",
232
+ dangerBg: "#5a3b40",
233
+ dangerBorder: "#7d4d53",
234
+ dangerText: "#ffada9",
235
+ infoBg: "#2d4e67",
236
+ infoBorder: "#3d7292",
237
+ infoText: "#67cdff",
238
+ brandVibrant: "#6366f1",
239
+ brandCool: "#818cf8",
240
+ brandGlow: "#a5aafc",
241
+ brandPurple: "#c7d2fe"
242
+ }
243
+ };
244
+ //#endregion
245
+ export { Logo, TangleKnot, palettes };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/brand",
3
- "version": "1.15.7",
3
+ "version": "1.17.0",
4
4
  "description": "Tangle brand system — design tokens, logo, Tailwind v4 theme. Single source of truth for every Tangle app.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -69,11 +69,12 @@
69
69
  "tailwind"
70
70
  ],
71
71
  "scripts": {
72
- "build": "node scripts/gen-ladders.mjs --check && node scripts/gen-compat.mjs --check && tsdown",
72
+ "build": "node scripts/gen-ladders.mjs --check && node scripts/gen-compat.mjs --check && node scripts/gen-palette.mjs --check && tsdown",
73
73
  "gen:ladders": "node scripts/gen-ladders.mjs",
74
74
  "gen:compat": "node scripts/gen-compat.mjs",
75
75
  "check:compat": "node scripts/gen-ladders.mjs --check && node scripts/gen-compat.mjs --check && node --test scripts/gen-compat.test.mjs",
76
76
  "check:drift": "node --test scripts/tangle-drift.test.mjs",
77
- "dev": "tsdown --watch"
77
+ "dev": "tsdown --watch",
78
+ "gen:palette": "node scripts/gen-palette.mjs"
78
79
  }
79
80
  }
package/src/index.ts CHANGED
@@ -1 +1,2 @@
1
1
  export { Logo, TangleKnot, type LogoProps } from "./logo.js";
2
+ export { palettes, type BrandPalette } from "./palette.generated.js";
@@ -0,0 +1,174 @@
1
+ // GENERATED by scripts/gen-palette.mjs from tokens.css and named-themes.css; do not edit.
2
+ // Brand's resolved colors for renderers that cannot read CSS variables: PDF, canvas,
3
+ // WebGL, email and the theme-color meta. Pages styled with CSS use the variables.
4
+
5
+ /** A theme's resolved roles: hex strings, or rgba() where the token is translucent. */
6
+ export interface BrandPalette {
7
+ canvas: string;
8
+ card: string;
9
+ well: string;
10
+ nested: string;
11
+ overlay: string;
12
+ deepest: string;
13
+ hairline: string;
14
+ ink: string;
15
+ inkSecondary: string;
16
+ inkMuted: string;
17
+ accent: string;
18
+ accentHover: string;
19
+ onAccent: string;
20
+ accentText: string;
21
+ danger: string;
22
+ successBg: string;
23
+ successBorder: string;
24
+ successText: string;
25
+ warningBg: string;
26
+ warningBorder: string;
27
+ warningText: string;
28
+ dangerBg: string;
29
+ dangerBorder: string;
30
+ dangerText: string;
31
+ infoBg: string;
32
+ infoBorder: string;
33
+ infoText: string;
34
+ brandVibrant: string;
35
+ brandCool: string;
36
+ brandGlow: string;
37
+ brandPurple: string;
38
+ }
39
+
40
+ /** Resolved palettes for the canonical light and dark themes and the website named theme. */
41
+ export const palettes = {
42
+ light: {
43
+ canvas: "#eceef3",
44
+ card: "#ffffff",
45
+ well: "#f6f7fa",
46
+ nested: "#f1f2f7",
47
+ overlay: "#ffffff",
48
+ deepest: "#ffffff",
49
+ hairline: "#c7c6d6",
50
+ ink: "#2d2d2d",
51
+ inkSecondary: "#3f3f3f",
52
+ inkMuted: "#4c4c4c",
53
+ accent: "#4c3fc5",
54
+ accentHover: "#5a50d6",
55
+ onAccent: "#ffffff",
56
+ accentText: "#3c32e9",
57
+ danger: "#b41d1d",
58
+ successBg: "#ecfdf5",
59
+ successBorder: "#a7f3d0",
60
+ successText: "#047857",
61
+ warningBg: "#fffbeb",
62
+ warningBorder: "#fde68a",
63
+ warningText: "#ab4f09",
64
+ dangerBg: "#fff1f2",
65
+ dangerBorder: "#fecdd3",
66
+ dangerText: "#b91c1c",
67
+ infoBg: "#f0f9ff",
68
+ infoBorder: "#bae6fd",
69
+ infoText: "#0369a1",
70
+ brandVibrant: "#6366f1",
71
+ brandCool: "#818cf8",
72
+ brandGlow: "#a5aafc",
73
+ brandPurple: "#6366f1",
74
+ },
75
+ dark: {
76
+ canvas: "#0a0a14",
77
+ card: "#191826",
78
+ well: "#12111f",
79
+ nested: "#221f33",
80
+ overlay: "#2c2942",
81
+ deepest: "#050509",
82
+ hairline: "#2a293d",
83
+ ink: "#e6e6e6",
84
+ inkSecondary: "#c7c7c7",
85
+ inkMuted: "#b5b5b5",
86
+ accent: "#4c3fc5",
87
+ accentHover: "#5a50d6",
88
+ onAccent: "#ffffff",
89
+ accentText: "#a5b4fc",
90
+ danger: "#d52044",
91
+ successBg: "#2f5146",
92
+ successBorder: "#3c7760",
93
+ successText: "#5ed7a4",
94
+ warningBg: "#4b4232",
95
+ warningBorder: "#7a6236",
96
+ warningText: "#f4c14f",
97
+ dangerBg: "#5a3b40",
98
+ dangerBorder: "#7d4d53",
99
+ dangerText: "#ffada9",
100
+ infoBg: "#2d4e67",
101
+ infoBorder: "#3d7292",
102
+ infoText: "#67cdff",
103
+ brandVibrant: "#6366f1",
104
+ brandCool: "#818cf8",
105
+ brandGlow: "#a5aafc",
106
+ brandPurple: "#c7d2fe",
107
+ },
108
+ websiteLight: {
109
+ canvas: "#f5f5fb",
110
+ card: "#ffffff",
111
+ well: "#fafafd",
112
+ nested: "#ececf6",
113
+ overlay: "#ffffff",
114
+ deepest: "#ffffff",
115
+ hairline: "#dbdbea",
116
+ ink: "#191c24",
117
+ inkSecondary: "#3a3a50",
118
+ inkMuted: "#5e5e7a",
119
+ accent: "#4c3fc5",
120
+ accentHover: "#5a50d6",
121
+ onAccent: "#ffffff",
122
+ accentText: "#3c32e9",
123
+ danger: "#b41d1d",
124
+ successBg: "#ecfdf5",
125
+ successBorder: "#a7f3d0",
126
+ successText: "#047857",
127
+ warningBg: "#fffbeb",
128
+ warningBorder: "#fde68a",
129
+ warningText: "#ab4f09",
130
+ dangerBg: "#fff1f2",
131
+ dangerBorder: "#fecdd3",
132
+ dangerText: "#b91c1c",
133
+ infoBg: "#f0f9ff",
134
+ infoBorder: "#bae6fd",
135
+ infoText: "#0369a1",
136
+ brandVibrant: "#6366f1",
137
+ brandCool: "#818cf8",
138
+ brandGlow: "#a5aafc",
139
+ brandPurple: "#6366f1",
140
+ },
141
+ websiteDark: {
142
+ canvas: "#0c0b1d",
143
+ card: "#1d1b38",
144
+ well: "#141328",
145
+ nested: "#222041",
146
+ overlay: "#262448",
147
+ deepest: "#080814",
148
+ hairline: "#33315f",
149
+ ink: "#fdfdfd",
150
+ inkSecondary: "#aaa8b8",
151
+ inkMuted: "#9496bc",
152
+ accent: "#4c3fc5",
153
+ accentHover: "#5a50d6",
154
+ onAccent: "#ffffff",
155
+ accentText: "#a5b4fc",
156
+ danger: "#d52044",
157
+ successBg: "#2f5146",
158
+ successBorder: "#3c7760",
159
+ successText: "#5ed7a4",
160
+ warningBg: "#4b4232",
161
+ warningBorder: "#7a6236",
162
+ warningText: "#f4c14f",
163
+ dangerBg: "#5a3b40",
164
+ dangerBorder: "#7d4d53",
165
+ dangerText: "#ffada9",
166
+ infoBg: "#2d4e67",
167
+ infoBorder: "#3d7292",
168
+ infoText: "#67cdff",
169
+ brandVibrant: "#6366f1",
170
+ brandCool: "#818cf8",
171
+ brandGlow: "#a5aafc",
172
+ brandPurple: "#c7d2fe",
173
+ },
174
+ } as const satisfies Record<string, BrandPalette>;
@@ -0,0 +1,25 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import path from "node:path";
3
+ import { describe, expect, it } from "vitest";
4
+ import { palettes } from "./palette.generated";
5
+
6
+ describe("resolved palettes", () => {
7
+ it("are fresh against tokens.css and named-themes.css", () => {
8
+ const script = path.resolve(import.meta.dirname, "..", "scripts", "gen-palette.mjs");
9
+ expect(() => execFileSync(process.execPath, [script, "--check"], { stdio: "pipe" })).not.toThrow();
10
+ });
11
+
12
+ it("hold only concrete colors", () => {
13
+ for (const [theme, roles] of Object.entries(palettes)) {
14
+ for (const [role, value] of Object.entries(roles)) {
15
+ expect(value, `${theme}.${role}`).toMatch(/^(#[0-9a-f]{6}|rgba?\([^)]+\))$/);
16
+ }
17
+ }
18
+ });
19
+
20
+ it("keep the canonical planes", () => {
21
+ expect(palettes.light.card).toBe("#ffffff");
22
+ expect(palettes.light.canvas).not.toBe(palettes.light.card);
23
+ expect(palettes.dark.canvas).not.toBe(palettes.dark.card);
24
+ });
25
+ });