@nebutra/tokens 0.1.2 → 0.1.3

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.
Files changed (69) hide show
  1. package/brands/gsap/brand.json +17 -64
  2. package/brands/linear/brand.json +18 -57
  3. package/brands/notion/brand.json +18 -62
  4. package/brands/raycast/brand.json +17 -64
  5. package/brands/stripe/brand.json +16 -72
  6. package/brands/vanta/brand.json +16 -63
  7. package/brands/vercel/brand.json +16 -50
  8. package/dist/brand-package/index.d.ts +123 -0
  9. package/dist/brand-package/index.js +61 -0
  10. package/dist/brand-package/index.js.map +1 -0
  11. package/dist/brand-package/use-brand.d.ts +2 -0
  12. package/dist/brand-package/use-brand.js +12 -0
  13. package/dist/brand-package/use-brand.js.map +1 -0
  14. package/dist/chunk-ALJTP5HL.js +881 -0
  15. package/dist/chunk-ALJTP5HL.js.map +1 -0
  16. package/dist/chunk-RGUJQOLH.js +1582 -0
  17. package/dist/chunk-RGUJQOLH.js.map +1 -0
  18. package/dist/index.d.ts +81 -0
  19. package/dist/index.js +187 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/use-brand-C8d4DPqE.d.ts +371 -0
  22. package/package.json +34 -9
  23. package/recipe.css +322 -1
  24. package/skins/README.md +2 -4
  25. package/skins/gsap.css +31 -119
  26. package/skins/linear.css +33 -117
  27. package/skins/notion.css +27 -117
  28. package/skins/raycast.css +32 -119
  29. package/skins/stripe.css +27 -129
  30. package/skins/vanta.css +27 -122
  31. package/skins/vercel.css +30 -111
  32. package/styles.css +388 -113
  33. package/.turbo/turbo-build.log +0 -8
  34. package/.turbo/turbo-typecheck.log +0 -4
  35. package/AGENTS.md +0 -45
  36. package/DESIGN.md +0 -357
  37. package/scripts/compile-brand-run.ts +0 -79
  38. package/scripts/compile-brand.mjs +0 -25
  39. package/scripts/emit-skins-run.ts +0 -62
  40. package/scripts/emit-skins.mjs +0 -31
  41. package/scripts/sync-styles.mjs +0 -29
  42. package/src/brand-package/__tests__/compile-refero.test.ts +0 -427
  43. package/src/brand-package/__tests__/emit-css.test.ts +0 -130
  44. package/src/brand-package/apply-brand.ts +0 -92
  45. package/src/brand-package/compile-helpers.ts +0 -170
  46. package/src/brand-package/compile-refero.ts +0 -69
  47. package/src/brand-package/emit-css.ts +0 -468
  48. package/src/brand-package/hex-to-hsl.ts +0 -100
  49. package/src/brand-package/index.ts +0 -71
  50. package/src/brand-package/infer-recipe.ts +0 -198
  51. package/src/brand-package/normalize.ts +0 -295
  52. package/src/brand-package/presets/context.ts +0 -17
  53. package/src/brand-package/presets/generic.ts +0 -214
  54. package/src/brand-package/presets/gsap.ts +0 -146
  55. package/src/brand-package/presets/index.ts +0 -31
  56. package/src/brand-package/presets/linear.ts +0 -183
  57. package/src/brand-package/presets/notion.ts +0 -208
  58. package/src/brand-package/presets/raycast.ts +0 -138
  59. package/src/brand-package/presets/recipe.ts +0 -61
  60. package/src/brand-package/presets/stripe.ts +0 -207
  61. package/src/brand-package/presets/vanta.ts +0 -220
  62. package/src/brand-package/presets/vercel.ts +0 -187
  63. package/src/brand-package/types.ts +0 -285
  64. package/src/brand-package/use-brand.ts +0 -207
  65. package/src/brand-package/validate.ts +0 -108
  66. package/src/index.ts +0 -67
  67. package/src/theme-provider.tsx +0 -246
  68. package/tsconfig.json +0 -11
  69. package/turbo.json +0 -15
@@ -1,208 +0,0 @@
1
- /**
2
- * notion design-language compile preset (stress fixture).
3
- */
4
- import { leafHex } from "../compile-helpers";
5
- import { tryHexToHsl } from "../hex-to-hsl";
6
- import type { BrandPackage } from "../types";
7
- import type { CompileContext } from "./context";
8
- import { buildRecipe } from "./recipe";
9
-
10
- export function buildNotion(ctx: CompileContext): BrandPackage {
11
- const paper = ctx.colors["paper-warmth"] ?? "#f6f5f4";
12
- const white = ctx.colors["pure-white"] ?? "#ffffff";
13
- const ink = ctx.colors["ink-black"] ?? "#000000";
14
- const charcoal = ctx.colors.charcoal ?? "#111111";
15
- const stone = ctx.colors.stone ?? "#757575";
16
- const graphite = ctx.colors.graphite ?? "#615d59";
17
- const blue = ctx.colors["notion-blue"] ?? "#0075de";
18
- const sky = ctx.colors["sky-tint"] ?? "#e6f3fe";
19
- const marigold = ctx.colors.marigold ?? "#ffb110";
20
- const coral = ctx.colors.coral ?? "#f64932";
21
- const midnight = ctx.colors["midnight-ink"] ?? "#02093a";
22
- const signal = ctx.colors["signal-blue"] ?? "#097fe8";
23
-
24
- ctx.warnings.push(
25
- "Notion: notion-blue is the only filled CTA; marigold/coral/midnight are decorative card washes (never default action).",
26
- );
27
- ctx.warnings.push(
28
- "Notion: content cards use 1px hairline + elev=none; soft shadows only for sticky nav / product mockups (raised slot).",
29
- );
30
-
31
- const brand: BrandPackage = {
32
- id: "notion",
33
- name: "Notion",
34
- darkDefault: false,
35
- version: "1.0.0",
36
- roles: {
37
- canvas: tryHexToHsl(paper, "30 9% 96%"),
38
- canvasForeground: tryHexToHsl(ink, "0 0% 0%"),
39
- surface: tryHexToHsl(white, "0 0% 100%"),
40
- surfaceForeground: tryHexToHsl(ink, "0 0% 0%"),
41
- action: tryHexToHsl(blue, "208 100% 44%"),
42
- actionForeground: tryHexToHsl(white, "0 0% 100%"),
43
- // Logo / wordmark / ink hierarchy — not blue CTA
44
- brand: tryHexToHsl(ink, "0 0% 0%"),
45
- brandForeground: tryHexToHsl(white, "0 0% 100%"),
46
- // Ghost CTA wash
47
- quiet: tryHexToHsl(sky, "206 90% 95%"),
48
- quietForeground: tryHexToHsl(blue, "208 100% 44%"),
49
- muted: tryHexToHsl(sky, "206 90% 95%"),
50
- mutedForeground: tryHexToHsl(stone, "0 0% 46%"),
51
- // Approx hairline rgba(0,0,0,0.08) on warm paper
52
- border: "30 5% 88%",
53
- input: tryHexToHsl(white, "0 0% 100%"),
54
- ring: tryHexToHsl(blue, "208 100% 44%"),
55
- destructive: tryHexToHsl(coral, "6 91% 58%"),
56
- destructiveForeground: tryHexToHsl(white, "0 0% 100%"),
57
- warning: tryHexToHsl(marigold, "40 100% 53%"),
58
- warningForeground: tryHexToHsl(ink, "0 0% 0%"),
59
- info: tryHexToHsl(signal, "207 93% 47%"),
60
- infoForeground: tryHexToHsl(white, "0 0% 100%"),
61
- },
62
- semantic: {
63
- background: tryHexToHsl(paper, "30 9% 96%"),
64
- foreground: tryHexToHsl(ink, "0 0% 0%"),
65
- card: tryHexToHsl(white, "0 0% 100%"),
66
- cardForeground: tryHexToHsl(ink, "0 0% 0%"),
67
- popover: tryHexToHsl(white, "0 0% 100%"),
68
- popoverForeground: tryHexToHsl(ink, "0 0% 0%"),
69
- primary: tryHexToHsl(blue, "208 100% 44%"),
70
- primaryForeground: tryHexToHsl(white, "0 0% 100%"),
71
- secondary: tryHexToHsl(sky, "206 90% 95%"),
72
- secondaryForeground: tryHexToHsl(blue, "208 100% 44%"),
73
- muted: tryHexToHsl(sky, "206 90% 95%"),
74
- mutedForeground: tryHexToHsl(stone, "0 0% 46%"),
75
- accent: tryHexToHsl(sky, "206 90% 95%"),
76
- accentForeground: tryHexToHsl(blue, "208 100% 44%"),
77
- destructive: tryHexToHsl(coral, "6 91% 58%"),
78
- destructiveForeground: tryHexToHsl(white, "0 0% 100%"),
79
- border: "30 5% 88%",
80
- input: tryHexToHsl(white, "0 0% 100%"),
81
- ring: tryHexToHsl(blue, "208 100% 44%"),
82
- warning: tryHexToHsl(marigold, "40 100% 53%"),
83
- warningForeground: tryHexToHsl(ink, "0 0% 0%"),
84
- info: tryHexToHsl(signal, "207 93% 47%"),
85
- infoForeground: tryHexToHsl(white, "0 0% 100%"),
86
- },
87
- recipe: buildRecipe({
88
- buttonDefault: ctx.recipeHints.buttonDefault ?? "solid",
89
- radii: {
90
- button:
91
- ctx.recipeHints.radii?.button ??
92
- leafHex(ctx.radius, ["buttons"]) ??
93
- leafHex(ctx.radius, ["lg"]) ??
94
- "8px",
95
- card:
96
- ctx.recipeHints.radii?.card ??
97
- leafHex(ctx.radius, ["cards"]) ??
98
- leafHex(ctx.radius, ["xl"]) ??
99
- "12px",
100
- badge: leafHex(ctx.radius, ["pills"]) ?? leafHex(ctx.radius, ["full"]) ?? "9999px",
101
- input: leafHex(ctx.radius, ["buttons"]) ?? leafHex(ctx.radius, ["lg"]) ?? "8px",
102
- },
103
- elevationPreset: ctx.recipeHints.elevationPreset ?? "none",
104
- density: ctx.recipeHints.density ?? "comfortable",
105
- badgeDefault: "muted",
106
- outlineBorder: ink,
107
- // Sticky nav soft shadow lives in raised; cards stay flat
108
- elevationTokens: {
109
- card: "0 0 #0000",
110
- control: "0 0 #0000",
111
- raised: "0px 0.7px 1.462px 0px rgb(0 0 0 / 0.015), 0px 3px 9px 0px rgb(0 0 0 / 0.03)",
112
- },
113
- }),
114
- typography: {
115
- fontSans: `'NotionInter', 'Inter', ui-sans-serif, system-ui, sans-serif`,
116
- fontDisplay: `'NotionInter', 'Inter', ui-sans-serif, system-ui, sans-serif`,
117
- headingWeight: 700,
118
- faces: [
119
- {
120
- family: "NotionInter",
121
- src: [
122
- {
123
- url: "https://cdn.jsdelivr.net/fontsource/fonts/inter:vf@latest/latin-wght-normal.woff2",
124
- format: "woff2",
125
- },
126
- ],
127
- weight: "100 900",
128
- display: "swap",
129
- },
130
- {
131
- family: "Lyon Text",
132
- src: [{ url: "/brand-assets/lyon-text-regular.woff2", format: "woff2" }],
133
- weight: 400,
134
- display: "swap",
135
- },
136
- ],
137
- },
138
- zones: {
139
- product: {
140
- caption: {
141
- fontSize: "12px",
142
- lineHeight: 1.33,
143
- fontWeight: 500,
144
- letterSpacing: "0.12px",
145
- },
146
- bodySm: { fontSize: "14px", lineHeight: 1.43, fontWeight: 500 },
147
- body: { fontSize: "16px", lineHeight: 1.5, fontWeight: 400 },
148
- subheading: { fontSize: "20px", lineHeight: 1, fontWeight: 500 },
149
- headingSm: {
150
- fontSize: "22px",
151
- lineHeight: 1.27,
152
- fontWeight: 600,
153
- letterSpacing: "-0.242px",
154
- },
155
- heading: { fontSize: "40px", lineHeight: 1.5, fontWeight: 600 },
156
- },
157
- marketing: {
158
- body: { fontSize: "16px", lineHeight: 1.5, fontWeight: 400 },
159
- heading: {
160
- fontSize: "48px",
161
- lineHeight: 1.5,
162
- fontWeight: 700,
163
- },
164
- headingLg: {
165
- fontSize: "54px",
166
- lineHeight: 1.04,
167
- fontWeight: 700,
168
- letterSpacing: "-1.89px",
169
- },
170
- display: {
171
- fontSize: "72px",
172
- lineHeight: 1.21,
173
- fontWeight: 700,
174
- letterSpacing: "-2.016px",
175
- },
176
- },
177
- },
178
- extensions: {
179
- categories: {
180
- brand: ink,
181
- action: blue,
182
- ghost: sky,
183
- marigold,
184
- coral,
185
- midnight,
186
- charcoal,
187
- graphite,
188
- },
189
- decorative: {
190
- marigold,
191
- coral,
192
- saffron: ctx.colors.saffron ?? "#e89d01",
193
- "sky-wash": ctx.colors["sky-wash"] ?? "#62aef0",
194
- midnight,
195
- },
196
- sourceUrl: typeof ctx.refero.url === "string" ? ctx.refero.url : "https://www.notion.com",
197
- notes: [
198
- "roles.action = Notion Blue (only filled CTA). Accent hues are decorative card washes.",
199
- "roles.brand = Ink Black (logo / wordmark / text hierarchy via alpha).",
200
- "Canvas = Paper Warmth; cards = Pure White — never invert.",
201
- "Card elev=none + hairline border; sticky nav soft shadow → elevation raised slot.",
202
- "Buttons 8px, cards 12px, pills 9999px.",
203
- "Lyon Text is editorial accent only — not product chrome UI.",
204
- ],
205
- },
206
- };
207
- return brand;
208
- }
@@ -1,138 +0,0 @@
1
- /**
2
- * raycast design-language compile preset (stress fixture).
3
- */
4
- import { tryHexToHsl } from "../hex-to-hsl";
5
- import type { BrandPackage } from "../types";
6
- import type { CompileContext } from "./context";
7
- import { buildRecipe } from "./recipe";
8
-
9
- export function buildRaycast(ctx: CompileContext): BrandPackage {
10
- const canvas = ctx.colors["void-black"] ?? ctx.colors.canvas ?? "#040506";
11
- const ink = ctx.colors.ink ?? ctx.colors.card ?? "#07080a";
12
- const obsidian = ctx.colors.obsidian ?? ctx.colors.recessed ?? "#111214";
13
- const graphite = ctx.colors.graphite ?? ctx.colors.badge ?? "#1b1c1e";
14
- const smoke = ctx.colors.smoke ?? "#6a6b6c";
15
- const ash = ctx.colors.ash ?? "#9c9c9d";
16
- const mist = ctx.colors.mist ?? "#e6e6e6";
17
- const iron = ctx.colors.iron ?? "#454647";
18
- const slate = ctx.colors.slate ?? "#2f3031";
19
- const paper = ctx.colors["pure-white"] ?? "#ffffff";
20
- const coral = ctx.colors["coral-pulse"] ?? "#ff6363";
21
- const success = ctx.colors["success-green"] ?? "#59d499";
22
- const info = ctx.colors["info-blue"] ?? "#56c2ff";
23
-
24
- ctx.warnings.push(
25
- "Raycast: coral-pulse is brand mark only — primary CTA is Mist/Iron neutral solid.",
26
- );
27
-
28
- const brand: BrandPackage = {
29
- id: "raycast",
30
- name: "Raycast",
31
- darkDefault: true,
32
- version: "1.0.0",
33
- semantic: {
34
- background: tryHexToHsl(canvas, "210 20% 2%"),
35
- foreground: tryHexToHsl(paper, "0 0% 100%"),
36
- card: tryHexToHsl(ink, "220 18% 3%"),
37
- cardForeground: tryHexToHsl(paper, "0 0% 100%"),
38
- popover: tryHexToHsl(ink, "220 18% 3%"),
39
- popoverForeground: tryHexToHsl(paper, "0 0% 100%"),
40
- // Filled CTA = Mist on dark (not coral)
41
- primary: tryHexToHsl(mist, "0 0% 90%"),
42
- primaryForeground: tryHexToHsl(iron, "210 1% 27%"),
43
- secondary: tryHexToHsl(graphite, "220 4% 11%"),
44
- secondaryForeground: tryHexToHsl(paper, "0 0% 100%"),
45
- muted: tryHexToHsl(obsidian, "220 6% 7%"),
46
- mutedForeground: tryHexToHsl(smoke, "240 1% 42%"),
47
- // Coral as accent for brand-adjacent UI (badges that opt-in to accent)
48
- accent: tryHexToHsl(coral, "0 100% 69%"),
49
- accentForeground: tryHexToHsl(paper, "0 0% 100%"),
50
- destructive: "0 72% 51%",
51
- destructiveForeground: tryHexToHsl(paper, "0 0% 100%"),
52
- border: tryHexToHsl(slate, "210 2% 19%"),
53
- input: tryHexToHsl(obsidian, "220 6% 7%"),
54
- ring: tryHexToHsl(ash, "240 1% 61%"),
55
- success: tryHexToHsl(success, "150 58% 59%"),
56
- successForeground: tryHexToHsl(canvas, "210 20% 2%"),
57
- info: tryHexToHsl(info, "200 100% 67%"),
58
- infoForeground: tryHexToHsl(canvas, "210 20% 2%"),
59
- },
60
- recipe: buildRecipe({
61
- buttonDefault: "solid",
62
- radii: {
63
- button: ctx.recipeHints.radii?.button ?? "8px",
64
- card: "16px",
65
- badge: "6px",
66
- input: "8px",
67
- },
68
- elevationPreset: ctx.recipeHints.elevationPreset ?? "key",
69
- density: ctx.recipeHints.density ?? "comfortable",
70
- badgeDefault: "muted",
71
- }),
72
- typography: {
73
- fontSans: `'Inter', ui-sans-serif, system-ui, -apple-system, sans-serif`,
74
- fontMono: `'Geist Mono', 'GeistMono', ui-monospace, Menlo, monospace`,
75
- fontDisplay: `'Inter', ui-sans-serif, system-ui, sans-serif`,
76
- headingWeight: 400,
77
- faces: [
78
- {
79
- family: "Inter",
80
- src: [
81
- {
82
- url: "https://cdn.jsdelivr.net/fontsource/fonts/inter:vf@latest/latin-wght-normal.woff2",
83
- format: "woff2",
84
- },
85
- ],
86
- weight: "100 900",
87
- display: "swap",
88
- },
89
- ],
90
- },
91
- zones: {
92
- product: {
93
- caption: {
94
- fontSize: "11px",
95
- lineHeight: 0.91,
96
- fontWeight: 500,
97
- letterSpacing: "0.8px",
98
- },
99
- bodySm: { fontSize: "13px", lineHeight: 1.2, fontWeight: 500 },
100
- body: { fontSize: "16px", lineHeight: 1.15, fontWeight: 400 },
101
- bodyLg: { fontSize: "18px", lineHeight: 1.15, fontWeight: 400 },
102
- subheading: {
103
- fontSize: "20px",
104
- lineHeight: 1.2,
105
- fontWeight: 400,
106
- letterSpacing: "0.2px",
107
- },
108
- headingSm: { fontSize: "24px", lineHeight: 1.15, fontWeight: 500 },
109
- heading: { fontSize: "32px", lineHeight: 1.15, fontWeight: 500 },
110
- },
111
- marketing: {
112
- body: { fontSize: "16px", lineHeight: 1.15, fontWeight: 400 },
113
- heading: { fontSize: "32px", lineHeight: 1.15, fontWeight: 500 },
114
- headingLg: {
115
- fontSize: "56px",
116
- lineHeight: 1.17,
117
- fontWeight: 400,
118
- letterSpacing: "0.22px",
119
- },
120
- display: { fontSize: "64px", lineHeight: 1.1, fontWeight: 600 },
121
- },
122
- },
123
- extensions: {
124
- categories: {
125
- brand: coral,
126
- ember: ctx.colors["ember-hush"] ?? "#452324",
127
- sky: ctx.colors["electric-sky"] ?? "#63a1ff",
128
- },
129
- sourceUrl: typeof ctx.refero.url === "string" ? ctx.refero.url : "https://raycast.com",
130
- notes: [
131
- "Primary CTA = Mist fill + Iron text (neutral solid).",
132
- "Coral Pulse is brand mark only — do not use for general product chrome CTAs.",
133
- "Cards use elevation=key (keyboard-key inset shadow stack).",
134
- ],
135
- },
136
- };
137
- return brand;
138
- }
@@ -1,61 +0,0 @@
1
- /**
2
- * Canonical recipe builder for compile presets.
3
- * Output: free radii + free elevationTokens only.
4
- */
5
-
6
- import { elevationPresetToTokens } from "../normalize";
7
- import type {
8
- BadgeDefaultStyle,
9
- BrandElevationTokens,
10
- BrandRadii,
11
- BrandRecipe,
12
- ButtonDefaultStyle,
13
- Density,
14
- ElevationStyle,
15
- } from "../types";
16
-
17
- export interface RecipeInput {
18
- buttonDefault: ButtonDefaultStyle;
19
- density?: Density;
20
- /** Free radii slots (preferred API) */
21
- radii?: Partial<BrandRadii>;
22
- /**
23
- * Elevation preset expanded to elevationTokens when elevationTokens omitted.
24
- * Prefer passing elevationTokens for free CSS stacks.
25
- */
26
- elevationPreset?: ElevationStyle;
27
- elevationTokens?: BrandElevationTokens;
28
- badgeDefault?: BadgeDefaultStyle;
29
- primaryStrokeGradient?: string;
30
- outlineBorder?: string;
31
- /** Used when expanding elevationPreset key/hairline */
32
- cardShadow?: string;
33
- }
34
-
35
- /** Build a canonical BrandRecipe (radii + elevationTokens only). */
36
- export function buildRecipe(input: RecipeInput): BrandRecipe {
37
- const button = input.radii?.button ?? "0.375rem";
38
- const card = input.radii?.card ?? "0.75rem";
39
- const badge = input.radii?.badge ?? "9999px";
40
- const radii: BrandRadii = {
41
- button,
42
- card,
43
- badge,
44
- input: input.radii?.input ?? button,
45
- pill: input.radii?.pill ?? "9999px",
46
- };
47
- const elevationPreset = input.elevationPreset ?? "soft";
48
- const elevationTokens =
49
- input.elevationTokens ?? elevationPresetToTokens(elevationPreset, input.cardShadow);
50
-
51
- const recipe: BrandRecipe = {
52
- buttonDefault: input.buttonDefault,
53
- density: input.density ?? "comfortable",
54
- radii,
55
- elevationTokens,
56
- };
57
- if (input.badgeDefault) recipe.badgeDefault = input.badgeDefault;
58
- if (input.primaryStrokeGradient) recipe.primaryStrokeGradient = input.primaryStrokeGradient;
59
- if (input.outlineBorder) recipe.outlineBorder = input.outlineBorder;
60
- return recipe;
61
- }
@@ -1,207 +0,0 @@
1
- /**
2
- * stripe design-language compile preset (stress fixture).
3
- */
4
- import { tryHexToHsl } from "../hex-to-hsl";
5
- import type { BrandPackage } from "../types";
6
- import type { CompileContext } from "./context";
7
- import { buildRecipe } from "./recipe";
8
-
9
- export function buildStripe(ctx: CompileContext): BrandPackage {
10
- const white = ctx.colors["pure-white"] ?? "#ffffff";
11
- const mist = ctx.colors.mist ?? "#f8fafd";
12
- const frost = ctx.colors.frost ?? "#e5edf5";
13
- const midnight = ctx.colors["midnight-ink"] ?? "#061b31";
14
- const slate = ctx.colors.slate ?? "#64748d";
15
- const steel = ctx.colors.steel ?? "#50617a";
16
- const indigo = ctx.colors["indigo-ink"] ?? "#533afd";
17
- const indigoHover = ctx.colors["indigo-hover"] ?? "#7389ff";
18
- const lavender = ctx.colors["lavender-border"] ?? "#b9b9f9";
19
- const periwinkle = ctx.colors["periwinkle-wash"] ?? "#e8e9ff";
20
- const deep = ctx.colors["deep-violet"] ?? "#182659";
21
- const smoke = ctx.colors.smoke ?? "#839bc8";
22
-
23
- ctx.warnings.push(
24
- "Stripe: indigo-ink is action CTA only; midnight-ink is brand-mark/wordmark (never default CTA fill).",
25
- );
26
- ctx.warnings.push(
27
- "Stripe: elevation=none — depth via white→mist→frost tints + 1px frost rules, never box-shadow.",
28
- );
29
-
30
- const brand: BrandPackage = {
31
- id: "stripe",
32
- name: "Stripe",
33
- darkDefault: false,
34
- version: "1.0.0",
35
- roles: {
36
- canvas: tryHexToHsl(white, "0 0% 100%"),
37
- canvasForeground: tryHexToHsl(midnight, "208 78% 11%"),
38
- surface: tryHexToHsl(white, "0 0% 100%"),
39
- surfaceForeground: tryHexToHsl(midnight, "208 78% 11%"),
40
- action: tryHexToHsl(indigo, "248 98% 61%"),
41
- actionForeground: tryHexToHsl(white, "0 0% 100%"),
42
- brand: tryHexToHsl(midnight, "208 78% 11%"),
43
- brandForeground: tryHexToHsl(white, "0 0% 100%"),
44
- quiet: tryHexToHsl(periwinkle, "238 100% 95%"),
45
- quietForeground: tryHexToHsl(indigo, "248 98% 61%"),
46
- muted: tryHexToHsl(mist, "210 56% 98%"),
47
- mutedForeground: tryHexToHsl(slate, "215 16% 47%"),
48
- border: tryHexToHsl(frost, "210 36% 93%"),
49
- input: tryHexToHsl(white, "0 0% 100%"),
50
- ring: tryHexToHsl(indigo, "248 98% 61%"),
51
- destructive: "0 72% 51%",
52
- destructiveForeground: tryHexToHsl(white, "0 0% 100%"),
53
- info: tryHexToHsl(indigoHover, "230 100% 73%"),
54
- infoForeground: tryHexToHsl(midnight, "208 78% 11%"),
55
- },
56
- semantic: {
57
- background: tryHexToHsl(white, "0 0% 100%"),
58
- foreground: tryHexToHsl(midnight, "208 78% 11%"),
59
- card: tryHexToHsl(white, "0 0% 100%"),
60
- cardForeground: tryHexToHsl(midnight, "208 78% 11%"),
61
- popover: tryHexToHsl(white, "0 0% 100%"),
62
- popoverForeground: tryHexToHsl(midnight, "208 78% 11%"),
63
- primary: tryHexToHsl(indigo, "248 98% 61%"),
64
- primaryForeground: tryHexToHsl(white, "0 0% 100%"),
65
- secondary: tryHexToHsl(periwinkle, "238 100% 95%"),
66
- secondaryForeground: tryHexToHsl(indigo, "248 98% 61%"),
67
- muted: tryHexToHsl(mist, "210 56% 98%"),
68
- mutedForeground: tryHexToHsl(slate, "215 16% 47%"),
69
- accent: tryHexToHsl(indigo, "248 98% 61%"),
70
- accentForeground: tryHexToHsl(white, "0 0% 100%"),
71
- destructive: "0 72% 51%",
72
- destructiveForeground: tryHexToHsl(white, "0 0% 100%"),
73
- border: tryHexToHsl(frost, "210 36% 93%"),
74
- input: tryHexToHsl(white, "0 0% 100%"),
75
- ring: tryHexToHsl(indigo, "248 98% 61%"),
76
- info: tryHexToHsl(indigoHover, "230 100% 73%"),
77
- infoForeground: tryHexToHsl(midnight, "208 78% 11%"),
78
- },
79
- recipe: buildRecipe({
80
- buttonDefault: ctx.recipeHints.buttonDefault ?? "solid",
81
- radii: {
82
- button: ctx.recipeHints.radii?.button ?? "4px",
83
- card: "4px",
84
- badge: "9999px",
85
- input: "4px",
86
- },
87
- elevationPreset: ctx.recipeHints.elevationPreset ?? "none",
88
- density: ctx.recipeHints.density ?? "comfortable",
89
- badgeDefault: "muted",
90
- // Ghost outline companion uses lavender hairline, not carbon
91
- outlineBorder: lavender,
92
- }),
93
- typography: {
94
- fontSans: `'sohne-var', 'Inter Tight', 'Inter', ui-sans-serif, system-ui, sans-serif`,
95
- fontDisplay: `'sohne-var', 'Inter Tight', ui-sans-serif, system-ui, sans-serif`,
96
- // Whisper weight is the Stripe signature (even at 56px display)
97
- headingWeight: 300,
98
- faces: [
99
- {
100
- family: "sohne-var",
101
- // Placeholder — Create Center / licensed Söhne Variable
102
- src: [{ url: "/brand-assets/sohne-var.woff2", format: "woff2" }],
103
- weight: "300 400",
104
- display: "swap",
105
- },
106
- ],
107
- },
108
- zones: {
109
- product: {
110
- caption: {
111
- fontSize: "12px",
112
- lineHeight: 1.45,
113
- fontWeight: 300,
114
- letterSpacing: "-0.12px",
115
- },
116
- bodySm: {
117
- fontSize: "14px",
118
- lineHeight: 1.4,
119
- fontWeight: 400,
120
- letterSpacing: "-0.14px",
121
- },
122
- body: {
123
- fontSize: "16px",
124
- lineHeight: 1.2,
125
- fontWeight: 400,
126
- letterSpacing: "-0.16px",
127
- },
128
- bodyLg: {
129
- fontSize: "20px",
130
- lineHeight: 1.4,
131
- fontWeight: 300,
132
- letterSpacing: "-0.2px",
133
- },
134
- subheading: {
135
- fontSize: "22px",
136
- lineHeight: 1.1,
137
- fontWeight: 300,
138
- letterSpacing: "-0.22px",
139
- },
140
- headingSm: {
141
- fontSize: "26px",
142
- lineHeight: 1.12,
143
- fontWeight: 300,
144
- letterSpacing: "-0.26px",
145
- },
146
- heading: {
147
- fontSize: "32px",
148
- lineHeight: 1.1,
149
- fontWeight: 300,
150
- letterSpacing: "-0.64px",
151
- },
152
- },
153
- marketing: {
154
- body: {
155
- fontSize: "16px",
156
- lineHeight: 1.4,
157
- fontWeight: 300,
158
- letterSpacing: "-0.16px",
159
- },
160
- heading: {
161
- fontSize: "32px",
162
- lineHeight: 1.1,
163
- fontWeight: 300,
164
- letterSpacing: "-0.64px",
165
- },
166
- headingLg: {
167
- fontSize: "48px",
168
- lineHeight: 1.03,
169
- fontWeight: 300,
170
- letterSpacing: "-0.96px",
171
- },
172
- display: {
173
- fontSize: "56px",
174
- lineHeight: 1.03,
175
- fontWeight: 300,
176
- letterSpacing: "-1.4px",
177
- },
178
- },
179
- },
180
- extensions: {
181
- categories: {
182
- brand: midnight,
183
- action: indigo,
184
- link: indigo,
185
- hover: indigoHover,
186
- ghostBorder: lavender,
187
- wash: periwinkle,
188
- deep: deep,
189
- smoke,
190
- steel,
191
- },
192
- decorative: {
193
- "section-band": mist,
194
- frost,
195
- },
196
- sourceUrl: typeof ctx.refero.url === "string" ? ctx.refero.url : "https://stripe.com",
197
- notes: [
198
- "roles.action = Indigo Ink filled CTA; roles.brand = Midnight Ink wordmark.",
199
- "Elevation none — tint ladder + 1px frost rules; never box-shadow.",
200
- "Control ctx.radius 4px (not pill); tags may stay full-pill.",
201
- "Typography weight 300 is the product signature (Inter Tight substitute).",
202
- "Pair solid CTA with ghost outline (lavender border) as secondary.",
203
- ],
204
- },
205
- };
206
- return brand;
207
- }