@texturehq/edges 1.33.2 → 1.34.1

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 (53) hide show
  1. package/README.md +57 -3
  2. package/dist/{RichTextEditor-CxrunTg7.d.cts → RichTextEditor-diZqy_s1.d.cts} +1 -1
  3. package/dist/{RichTextEditor-CxrunTg7.d.ts → RichTextEditor-diZqy_s1.d.ts} +1 -1
  4. package/dist/{TimeField-DRQshIHX.d.ts → TimeField-7pTUPh11.d.ts} +32 -17
  5. package/dist/{TimeField-WCmeiLu3.d.cts → TimeField-CqmVrAdn.d.cts} +32 -17
  6. package/dist/{colors-Cgs2MGZ8.d.ts → colors-CoULWZ5j.d.ts} +53 -18
  7. package/dist/{colors-DGRiGzgj.d.cts → colors-upTGgIQe.d.cts} +53 -18
  8. package/dist/form/index.cjs +1 -1
  9. package/dist/form/index.cjs.map +1 -1
  10. package/dist/form/index.d.cts +1 -1
  11. package/dist/form/index.d.ts +1 -1
  12. package/dist/form/index.js +1 -1
  13. package/dist/form/index.js.map +1 -1
  14. package/dist/{index-BqpWEc_N.d.ts → index-dofSwYId.d.cts} +5 -5
  15. package/dist/{index-BqpWEc_N.d.cts → index-dofSwYId.d.ts} +5 -5
  16. package/dist/index.cjs +10 -10
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +161 -41
  19. package/dist/index.d.ts +161 -41
  20. package/dist/index.js +10 -10
  21. package/dist/index.js.map +1 -1
  22. package/dist/prose.css +202 -0
  23. package/dist/rhf/index.cjs +1 -1
  24. package/dist/rhf/index.cjs.map +1 -1
  25. package/dist/rhf/index.d.cts +2 -2
  26. package/dist/rhf/index.d.ts +2 -2
  27. package/dist/rhf/index.js +1 -1
  28. package/dist/rhf/index.js.map +1 -1
  29. package/dist/server.cjs +2 -2
  30. package/dist/server.cjs.map +1 -1
  31. package/dist/server.d.cts +2 -2
  32. package/dist/server.d.ts +2 -2
  33. package/dist/server.js +2 -2
  34. package/dist/server.js.map +1 -1
  35. package/dist/styles/computed.css +2 -2
  36. package/dist/styles/responsive-typography.css +9 -7
  37. package/dist/styles/theme-light-override.css +26 -15
  38. package/dist/styles.css +2122 -820
  39. package/dist/theme.css +20 -7
  40. package/dist/typography.css +150 -0
  41. package/package.json +13 -14
  42. package/scripts/check-legacy-action-variants.mjs +96 -0
  43. package/scripts/check-legacy-font-colors.mjs +92 -0
  44. package/scripts/copy-assets.js +12 -0
  45. package/scripts/generate-color-utilities.mjs +163 -0
  46. package/templates/claude-rules/.claude +5 -5
  47. package/templates/claude-rules/claude.md +5 -5
  48. package/templates/codex-rules/codex.md +5 -5
  49. package/dist/generated/tailwind-tokens-dark.css +0 -397
  50. package/dist/generated/tailwind-tokens-light.css +0 -611
  51. package/dist/generated/viz-runtime.css +0 -243
  52. package/scripts/generate-viz-runtime.js +0 -107
  53. package/scripts/validate-tokens.js +0 -176
package/dist/theme.css CHANGED
@@ -3,10 +3,11 @@
3
3
  *
4
4
  * This file imports all theme layers in the correct order:
5
5
  * 1. Design tokens (from @texturehq/edges-tokens)
6
- * 2. Legacy aliases (backward compat for old variable names)
7
- * 3. Animation features
8
- * 4. Computed values (derived from tokens)
9
- * 5. Utility classes
6
+ * 2. Typography role utilities (from @texturehq/edges-tokens)
7
+ * 3. Legacy aliases (backward compat for old variable names)
8
+ * 4. Animation features
9
+ * 5. Computed values (derived from tokens)
10
+ * 6. Utility classes
10
11
  */
11
12
 
12
13
  /* Layer 1: Design Tokens
@@ -15,9 +16,20 @@
15
16
  @import "@texturehq/edges-tokens/css/light";
16
17
  @import "@texturehq/edges-tokens/css/dark";
17
18
 
19
+ /* Typography role utilities
20
+ Generated from edges-tokens so full Edges consumers get the same lightweight
21
+ role classes marketing consumers can import directly. */
22
+ @import "@texturehq/edges-tokens/css/typography";
23
+
24
+ /* Prose: editorial composition wrapper for long-form content. Pairs with
25
+ the <Prose> component exported from this package. */
26
+ @import "@texturehq/edges-tokens/css/prose";
27
+
18
28
  /* Layer 1b: Legacy Aliases
19
29
  Backward-compat aliases from old variable names (--action-brand-background,
20
- --feedback-*, --skeleton-*, etc.) to new ones. Remove after migration. */
30
+ --feedback-*, --skeleton-*, and deprecated font color names) to new ones.
31
+ Font colors should use text-primary/text-secondary/text-inverse/text-disabled;
32
+ remove this import after the broader compatibility migration. */
21
33
  @import "@texturehq/edges-tokens/css/legacy";
22
34
 
23
35
  /* Layer 1b2: Legacy Bridge
@@ -25,8 +37,9 @@
25
37
  Remove once utility classes are updated to reference --color-* directly. */
26
38
  @import "./styles/legacy-bridge.css";
27
39
 
28
- /* Layer 1c: Responsive Typography
29
- Overrides font-size tokens with responsive values (smaller on mobile, larger on desktop) */
40
+ /* Layer 1c: Legacy Responsive Typography Compatibility
41
+ Deprecated compatibility layer for primitive Tailwind size utilities.
42
+ Semantic typography roles now own responsive behavior at the token layer. */
30
43
  @import "./styles/responsive-typography.css";
31
44
 
32
45
  /* Layer 1d: Light Theme Override
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Typography utilities — @texturehq/edges-tokens
3
+ * Generated by Style Dictionary. DO NOT EDIT.
4
+ *
5
+ * Import after @texturehq/edges-tokens/css/light so the referenced
6
+ * --text-* and --font-family-* custom properties are defined.
7
+ */
8
+
9
+ .text-agent {
10
+ font: var(--text-agent);
11
+ letter-spacing: var(--text-agent-letter-spacing);
12
+ font-optical-sizing: auto;
13
+ }
14
+
15
+ .text-body-lg {
16
+ font: var(--text-body-lg);
17
+ letter-spacing: var(--text-body-lg-letter-spacing);
18
+ }
19
+
20
+ .text-body-md {
21
+ font: var(--text-body-md);
22
+ letter-spacing: var(--text-body-md-letter-spacing);
23
+ }
24
+
25
+ .text-body-sm {
26
+ font: var(--text-body-sm);
27
+ letter-spacing: var(--text-body-sm-letter-spacing);
28
+ }
29
+
30
+ .text-code {
31
+ font: var(--text-code);
32
+ letter-spacing: var(--text-code-letter-spacing);
33
+ font-variant-numeric: tabular-nums;
34
+ }
35
+
36
+ .text-display-lg {
37
+ font: var(--text-display-lg);
38
+ letter-spacing: var(--text-display-lg-letter-spacing);
39
+ }
40
+
41
+ .text-display-md {
42
+ font: var(--text-display-md);
43
+ letter-spacing: var(--text-display-md-letter-spacing);
44
+ }
45
+
46
+ .text-display-sm {
47
+ font: var(--text-display-sm);
48
+ letter-spacing: var(--text-display-sm-letter-spacing);
49
+ }
50
+
51
+ .text-display-xl {
52
+ font: var(--text-display-xl);
53
+ letter-spacing: var(--text-display-xl-letter-spacing);
54
+ }
55
+
56
+ .text-heading-lg {
57
+ font: var(--text-heading-lg);
58
+ letter-spacing: var(--text-heading-lg-letter-spacing);
59
+ }
60
+
61
+ .text-heading-md {
62
+ font: var(--text-heading-md);
63
+ letter-spacing: var(--text-heading-md-letter-spacing);
64
+ }
65
+
66
+ .text-heading-sm {
67
+ font: var(--text-heading-sm);
68
+ letter-spacing: var(--text-heading-sm-letter-spacing);
69
+ }
70
+
71
+ .text-heading-xl {
72
+ font: var(--text-heading-xl);
73
+ letter-spacing: var(--text-heading-xl-letter-spacing);
74
+ }
75
+
76
+ .text-label-lg {
77
+ font: var(--text-label-lg);
78
+ letter-spacing: var(--text-label-lg-letter-spacing);
79
+ }
80
+
81
+ .text-label-md {
82
+ font: var(--text-label-md);
83
+ letter-spacing: var(--text-label-md-letter-spacing);
84
+ }
85
+
86
+ .text-label-sm {
87
+ font: var(--text-label-sm);
88
+ letter-spacing: var(--text-label-sm-letter-spacing);
89
+ }
90
+
91
+ /* Semantic text color utilities. Pair with role utilities above. */
92
+ .text-text-body {
93
+ color: var(--color-text-body);
94
+ }
95
+
96
+ .text-text-caption {
97
+ color: var(--color-text-caption);
98
+ }
99
+
100
+ .text-text-disabled {
101
+ color: var(--color-text-disabled);
102
+ }
103
+
104
+ .text-text-heading {
105
+ color: var(--color-text-heading);
106
+ }
107
+
108
+ .text-text-inverse {
109
+ color: var(--color-text-inverse);
110
+ }
111
+
112
+ .text-text-label {
113
+ color: var(--color-text-label);
114
+ }
115
+
116
+ .text-text-link-default {
117
+ color: var(--color-text-link-default);
118
+ }
119
+
120
+ .text-text-link-hover {
121
+ color: var(--color-text-link-hover);
122
+ }
123
+
124
+ .text-text-muted {
125
+ color: var(--color-text-muted);
126
+ }
127
+
128
+ .text-text-onBrand {
129
+ color: var(--color-text-onBrand);
130
+ }
131
+
132
+ .text-text-onPrimary {
133
+ color: var(--color-text-onPrimary);
134
+ }
135
+
136
+ .text-text-placeholder {
137
+ color: var(--color-text-placeholder);
138
+ }
139
+
140
+ .text-text-primary {
141
+ color: var(--color-text-primary);
142
+ }
143
+
144
+ .text-text-secondary {
145
+ color: var(--color-text-secondary);
146
+ }
147
+
148
+ .text-text-subtle {
149
+ color: var(--color-text-subtle);
150
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@texturehq/edges",
3
- "version": "1.33.2",
3
+ "version": "1.34.1",
4
4
  "author": "Nicholas Brown <nick@texturehq.com>",
5
5
  "description": "A shared component library for Texture",
6
6
  "type": "module",
@@ -39,23 +39,21 @@
39
39
  "./dist/styles.css": "./dist/styles.css",
40
40
  "./theme.css": "./dist/theme.css",
41
41
  "./dist/theme.css": "./dist/theme.css",
42
- "./responsive-typography.css": "./dist/styles/responsive-typography.css"
42
+ "./responsive-typography.css": "./dist/styles/responsive-typography.css",
43
+ "./typography.css": "./dist/typography.css",
44
+ "./prose.css": "./dist/prose.css"
43
45
  },
44
46
  "scripts": {
45
- "dev": "yarn watch",
47
+ "dev": "yarn build:pre && yarn watch",
46
48
  "watch": "tsup --watch",
47
- "build": "yarn tokens:build && tsup && yarn build:post",
48
- "build:post": "postcss src/styles.css -o dist/styles.css && node scripts/copy-assets.js",
49
- "tokens:build": "node style-dictionary.config.mjs && node scripts/generate-viz-runtime.js",
50
- "tokens:build:tailwind": "node style-dictionary.config.mjs",
51
- "tokens:watch": "nodemon --watch tokens --ext json --exec 'yarn tokens:build'",
52
- "tokens:watch:tailwind": "nodemon --watch tokens --ext json --exec 'yarn tokens:build:tailwind'",
53
- "tokens:validate": "node scripts/validate-tokens.js",
49
+ "build": "yarn build:pre && tsup && yarn build:post",
50
+ "build:pre": "node scripts/generate-color-utilities.mjs",
51
+ "build:post": "node scripts/copy-assets.js",
54
52
  "build:yalc": "yarn build && npx yalc publish && npx yalc push",
55
53
  "dev:yalc": "yarn build && npx yalc publish && npx yalc push && echo 'Package updated! Run in target project: yarn install && yarn dev'",
56
54
  "dev:yalc:force": "yarn build && npx yalc publish && npx yalc push && echo 'Package updated! Run in target project: rm -rf .vite && yarn cache clean && yarn dev --force'",
57
55
  "clean": "rm -rf dist",
58
- "lint": "biome check src/",
56
+ "lint": "biome check src/ && yarn lint:action-variants",
59
57
  "lint:quiet": "biome check --reporter=github src/",
60
58
  "lint:fix": "biome check --write src/",
61
59
  "typecheck": "tsc --noEmit -p tsconfig.prod.json",
@@ -66,7 +64,9 @@
66
64
  "test:coverage": "vitest run --coverage",
67
65
  "test:ui": "vitest --ui",
68
66
  "storybook": "VITE_MAPBOX_ACCESS_TOKEN=pk.eyJ1IjoidmljdG9yLXRleHR1cmUiLCJhIjoiY2x1cXM5dnVqMDFvYTJrcWszbnZmdGo4cCJ9.uEu0gqmITLtBMKEVW0aFtA storybook dev -p ${STORYBOOK_PORT:-6010} --no-open",
69
- "build-storybook": "storybook build"
67
+ "build-storybook": "storybook build",
68
+ "lint:font-colors": "node scripts/check-legacy-font-colors.mjs",
69
+ "lint:action-variants": "node scripts/check-legacy-action-variants.mjs"
70
70
  },
71
71
  "peerDependencies": {
72
72
  "@hookform/resolvers": "^3.x || ^5.x",
@@ -93,7 +93,7 @@
93
93
  "@dnd-kit/utilities": "^3.2.2",
94
94
  "@phosphor-icons/react": "^2.1.7",
95
95
  "@tanstack/react-virtual": "^3.13.12",
96
- "@texturehq/edges-tokens": "^0.3.0",
96
+ "@texturehq/edges-tokens": "^0.4.3",
97
97
  "@tiptap/core": "^3.4.5",
98
98
  "@tiptap/extension-link": "^3.4.5",
99
99
  "@tiptap/pm": "^3.4.5",
@@ -169,7 +169,6 @@
169
169
  "react-dom": "19.2.0",
170
170
  "react-hook-form": "^7.53.0",
171
171
  "storybook": "^8.6.14",
172
- "style-dictionary": "^5.0.4",
173
172
  "tailwindcss": "^4.1.14",
174
173
  "tsup": "^8.0.2",
175
174
  "typescript": "~5.9.2",
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
3
+ import { join, relative } from "node:path";
4
+
5
+ const root = process.cwd();
6
+ const scanRoots = ["src/components", "src/form", "src/docs", "templates", "README.md"];
7
+ const ignoredParts = new Set(["node_modules", "dist", "generated"]);
8
+ const extensions = new Set([".ts", ".tsx", ".md", ".mdx"]);
9
+
10
+ const legacyPropPatterns = [
11
+ /\b(?:Button|TextLink|Chip|Badge|Checkbox|Switch|RadioGroup|RadioCard|Tabs|ToggleButton|SegmentedControl|ChipInputField|TextAreaWithChips|DeviceMetaDisplay|SiteMetaDisplay|ContactMetaDisplay)\b[^\n>]*variant=[{]?\s*["'](?:default|brand)["']/,
12
+ /chipVariant=[{]?\s*["'](?:default|brand)["']/,
13
+ /linkVariant=[{]?\s*["'](?:default|brand)["']/,
14
+ /\b(?:Button|TextLink|Chip|Badge|Checkbox|Switch|RadioGroup|RadioCard|Tabs|ToggleButton|SegmentedControl|ChipInputField|TextAreaWithChips|DeviceMetaDisplay|SiteMetaDisplay|ContactMetaDisplay)\b[^\n]*(?:variant|chipVariant|linkVariant):\s*["'](?:default|brand)["']/,
15
+ ];
16
+
17
+ const legacyClassPatterns = [];
18
+
19
+ const allowlistedFiles = new Set([
20
+ // Compatibility aliases intentionally remain in the primitives while consumers migrate.
21
+ "src/components/Button/Button.test.tsx",
22
+ "src/components/Chip/Chip.test.tsx",
23
+ "src/components/TextLink/TextLink.tsx",
24
+ "src/components/Button/Button.tsx",
25
+ "src/components/Chip/Chip.tsx",
26
+ "src/components/Badge/Badge.tsx",
27
+ "src/components/Checkbox/Checkbox.tsx",
28
+ "src/components/Switch/Switch.tsx",
29
+ "src/components/RadioGroup/RadioGroup.tsx",
30
+ "src/components/RadioCard/RadioCard.tsx",
31
+ "src/components/Tabs/Tabs.tsx",
32
+ "src/components/ToggleButton/ToggleButton.tsx",
33
+ "src/components/SegmentedControl/SegmentedControl.tsx",
34
+ "src/components/ChipInputField/ChipInputField.tsx",
35
+ "src/components/TextAreaWithChips/TextAreaWithChips.tsx",
36
+ "src/components/TextAreaWithChips/ChipTextArea.tsx",
37
+ "src/components/DeviceMetaDisplay/DeviceMetaDisplay.tsx",
38
+ "src/components/SiteMetaDisplay/SiteMetaDisplay.tsx",
39
+ "src/components/ContactMetaDisplay/ContactMetaDisplay.tsx",
40
+ "src/components/DataTable/cells/TextCell.tsx",
41
+ "src/components/DataTable/cells/SiteMetaCell.tsx",
42
+ "src/components/DataTable/cells/ContactMetaCell.tsx",
43
+ "src/components/DataTable/cells/DeviceMetaCell.tsx",
44
+ "src/components/DataTable/cells/cellUtils.tsx",
45
+ ]);
46
+
47
+ function hasSupportedExtension(path) {
48
+ if (path.endsWith("README.md")) return true;
49
+ return [...extensions].some((extension) => path.endsWith(extension));
50
+ }
51
+
52
+ function walk(path, files = []) {
53
+ const rel = relative(root, path);
54
+ if (rel.split("/").some((part) => ignoredParts.has(part))) return files;
55
+
56
+ const stat = statSync(path);
57
+ if (stat.isDirectory()) {
58
+ for (const entry of readdirSync(path)) walk(join(path, entry), files);
59
+ return files;
60
+ }
61
+
62
+ if (stat.isFile() && hasSupportedExtension(path)) files.push(path);
63
+ return files;
64
+ }
65
+
66
+ const violations = [];
67
+ for (const scanRoot of scanRoots) {
68
+ const path = join(root, scanRoot);
69
+ if (!existsSync(path)) continue;
70
+
71
+ for (const file of walk(path)) {
72
+ const rel = relative(root, file);
73
+ if (allowlistedFiles.has(rel)) continue;
74
+
75
+ const contents = readFileSync(file, "utf8");
76
+ const lines = contents.split(/\r?\n/);
77
+ lines.forEach((line, index) => {
78
+ if (legacyPropPatterns.some((pattern) => pattern.test(line))) {
79
+ violations.push(`${rel}:${index + 1}: legacy default/brand variant prop`);
80
+ }
81
+ if (legacyClassPatterns.some((pattern) => pattern.test(line))) {
82
+ violations.push(`${rel}:${index + 1}: legacy action brand/default styling class`);
83
+ }
84
+ });
85
+ }
86
+ }
87
+
88
+ if (violations.length > 0) {
89
+ console.error(
90
+ "Legacy action variant vocabulary found. Use primary/secondary APIs and action-primary/action-secondary tokens instead.\n"
91
+ );
92
+ console.error(violations.join("\n"));
93
+ process.exit(1);
94
+ }
95
+
96
+ console.log("No legacy action variant vocabulary found.");
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env node
2
+ import { readdirSync, readFileSync, statSync } from "node:fs";
3
+ import { join, relative } from "node:path";
4
+
5
+ const root = process.cwd();
6
+ const scanRoots = ["src/components", "src/form", "src/docs", "src/utils", "templates", "README.md"];
7
+ const ignoredParts = new Set(["node_modules", "dist", "generated"]);
8
+
9
+ const deprecatedPatterns = [
10
+ "text-text-heading",
11
+ "text-text-body",
12
+ "text-text-muted",
13
+ "text-text-caption",
14
+ "text-text-placeholder",
15
+ "text-text-onPrimary",
16
+ "text-text-onBrand",
17
+ "text-textFaint",
18
+ "--color-text-heading",
19
+ "--color-text-body",
20
+ "--color-text-muted",
21
+ "--color-text-caption",
22
+ "--color-text-placeholder",
23
+ "--color-text-onPrimary",
24
+ "--color-text-onBrand",
25
+ "--color-text-label",
26
+ "--theme-text-label",
27
+ "fill-text-body",
28
+ "fill-text-muted",
29
+ "fill-text-caption",
30
+ "fill-text-heading",
31
+ "bg-text-caption",
32
+ "bg-text-muted",
33
+ "bg-text-heading",
34
+ "border-text-heading",
35
+ "hover:bg-text-body",
36
+ ];
37
+
38
+ const extensions = new Set([".ts", ".tsx", ".css", ".md", ".mdx"]);
39
+ const allowlistedFiles = new Set([
40
+ "src/styles/theme-light-override.css",
41
+ "theme.css",
42
+ ]);
43
+
44
+ function hasSupportedExtension(path) {
45
+ if (path.endsWith("README.md")) return true;
46
+ return [...extensions].some((extension) => path.endsWith(extension));
47
+ }
48
+
49
+ function walk(path, files = []) {
50
+ const rel = relative(root, path);
51
+ if (rel.split("/").some((part) => ignoredParts.has(part))) return files;
52
+
53
+ const stat = statSync(path);
54
+ if (stat.isDirectory()) {
55
+ for (const entry of readdirSync(path)) {
56
+ walk(join(path, entry), files);
57
+ }
58
+ return files;
59
+ }
60
+
61
+ if (stat.isFile() && hasSupportedExtension(path)) {
62
+ files.push(path);
63
+ }
64
+ return files;
65
+ }
66
+
67
+ const violations = [];
68
+ for (const scanRoot of scanRoots) {
69
+ const path = join(root, scanRoot);
70
+ for (const file of walk(path)) {
71
+ const rel = relative(root, file);
72
+ if (allowlistedFiles.has(rel)) continue;
73
+
74
+ const contents = readFileSync(file, "utf8");
75
+ const lines = contents.split(/\r?\n/);
76
+ lines.forEach((line, index) => {
77
+ for (const pattern of deprecatedPatterns) {
78
+ if (line.includes(pattern)) {
79
+ violations.push(`${rel}:${index + 1}: ${pattern}`);
80
+ }
81
+ }
82
+ });
83
+ }
84
+ }
85
+
86
+ if (violations.length > 0) {
87
+ console.error("Deprecated font color tokens found. Use text-primary/text-secondary/text-inverse/text-disabled instead.\n");
88
+ console.error(violations.join("\n"));
89
+ process.exit(1);
90
+ }
91
+
92
+ console.log("No deprecated font color tokens found.");
@@ -9,6 +9,18 @@ try {
9
9
  fs.copyFileSync("src/theme.css", "dist/theme.css");
10
10
  console.log("✅ Theme CSS copied to dist");
11
11
 
12
+ // Expose generated typography utilities as @texturehq/edges/typography.css.
13
+ // @texturehq/edges/theme.css already imports them for full Edges consumers,
14
+ // while this standalone file gives lighter consumers the same role classes.
15
+ fs.copyFileSync("../edges-tokens/dist/typography.css", "dist/typography.css");
16
+ console.log("✅ Typography utilities copied to dist");
17
+
18
+ // Expose the editorial Prose CSS as @texturehq/edges/prose.css for
19
+ // lightweight consumers. Full Edges consumers already get it bundled via
20
+ // @texturehq/edges/styles.css → theme.css.
21
+ fs.copyFileSync("../edges-tokens/dist/prose.css", "dist/prose.css");
22
+ console.log("✅ Prose CSS copied to dist");
23
+
12
24
  // Copy style layer files to dist
13
25
  const styleFiles = [
14
26
  "src/styles/animations.css",
@@ -0,0 +1,163 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Generates `src/styles/generated-color-utilities.css` — a single
5
+ * `@source inline(...)` block that guarantees every color token in
6
+ * `@texturehq/edges-tokens` has its appropriate Tailwind utility class
7
+ * shipped in the compiled `dist/styles.css`.
8
+ *
9
+ * Without this, Tailwind v4's content-scan would only emit utilities
10
+ * for the subset of color tokens actually referenced in `packages/edges/src`,
11
+ * leaving consumers of the prebuilt CSS (apps that don't run Tailwind
12
+ * themselves) unable to use the rest of the token set — even though the
13
+ * underlying CSS variables are present in the shipped theme.
14
+ *
15
+ * Token name → utility mapping (by prefix):
16
+ * text-* → text-{token}
17
+ * border-* → border-{token}
18
+ * background-* → bg-{token}
19
+ * input-background-* → bg-{token}
20
+ * scrim-*, skeleton-* → bg-{token}
21
+ * feedback-*-background → bg-{token}
22
+ * feedback-*-border → border-{token}
23
+ * feedback-*-text → text-{token}
24
+ * state-*-border → border-{token}
25
+ * state-*-text → text-{token}
26
+ * state-*-data → (skipped — consumed via CSS var directly)
27
+ * state-* (other) → bg-{token} + text-{token}
28
+ * action-*-text → text-{token}
29
+ * action-*-hover → bg-{token}
30
+ * action-* (other) → bg-{token} + text-{token}
31
+ * map-*-border → border-{token}
32
+ * map-* → bg-{token}
33
+ * viz-* → bg-{token} + text-{token}
34
+ * color primitives (gray-/iris-/ocean-/etc., black/white/graphite/ink/linen/paper)
35
+ * → bg-, text-, border- (all three)
36
+ *
37
+ * Token names that don't match any rule above are skipped with a warning so
38
+ * a maintainer can decide whether to extend the mapping.
39
+ */
40
+
41
+ import fs from "node:fs";
42
+ import path from "node:path";
43
+ import { fileURLToPath } from "node:url";
44
+
45
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
46
+ const PKG_ROOT = path.resolve(__dirname, "..");
47
+
48
+ const TOKENS_PATH = path.resolve(PKG_ROOT, "../edges-tokens/dist/tokens.json");
49
+ const OUTPUT_PATH = path.resolve(PKG_ROOT, "src/generated/color-utilities.css");
50
+
51
+ const PRIMITIVES = new Set(["black", "white", "graphite", "ink", "linen", "paper"]);
52
+ const PALETTE_PREFIXES = ["gray-", "iris-", "moss-", "ocean-", "rose-", "honey-", "canary-", "plum-"];
53
+ const SEMANTIC_BASE_NAMES = new Set([
54
+ "error-light",
55
+ "error-base",
56
+ "error-dark",
57
+ "warning-light",
58
+ "warning-base",
59
+ "warning-dark",
60
+ "success-light",
61
+ "success-base",
62
+ "success-dark",
63
+ "info-light",
64
+ "info-base",
65
+ "info-dark",
66
+ ]);
67
+
68
+ function classify(token) {
69
+ if (token === "paper-border") return ["border"];
70
+ if (token.startsWith("text-")) return ["text"];
71
+ if (token.startsWith("border-")) return ["border"];
72
+ if (token.startsWith("background-")) return ["bg"];
73
+ if (token.startsWith("input-background-")) return ["bg"];
74
+ if (token.startsWith("scrim-") || token.startsWith("skeleton-")) return ["bg"];
75
+
76
+ if (token.startsWith("feedback-")) {
77
+ if (token.endsWith("-background")) return ["bg"];
78
+ if (token.endsWith("-border")) return ["border"];
79
+ if (token.endsWith("-text")) return ["text"];
80
+ return null;
81
+ }
82
+
83
+ if (token.startsWith("state-")) {
84
+ if (token.endsWith("-border")) return ["border"];
85
+ if (token.endsWith("-text")) return ["text"];
86
+ if (token.endsWith("-data")) return [];
87
+ return ["bg", "text"];
88
+ }
89
+
90
+ if (token.startsWith("action-")) {
91
+ if (token.endsWith("-text")) return ["text"];
92
+ if (token.endsWith("-hover")) return ["bg"];
93
+ return ["bg", "text"];
94
+ }
95
+
96
+ if (token.startsWith("map-")) {
97
+ if (token.endsWith("-border")) return ["border"];
98
+ return ["bg"];
99
+ }
100
+
101
+ if (token.startsWith("viz-")) return ["bg", "text"];
102
+
103
+ if (PRIMITIVES.has(token)) return ["bg", "text", "border"];
104
+ if (PALETTE_PREFIXES.some((p) => token.startsWith(p))) return ["bg", "text", "border"];
105
+ if (SEMANTIC_BASE_NAMES.has(token)) return ["bg", "text", "border"];
106
+
107
+ return null;
108
+ }
109
+
110
+ const tokensRaw = JSON.parse(fs.readFileSync(TOKENS_PATH, "utf8"));
111
+ const colorTokens = Object.keys(tokensRaw)
112
+ .filter((k) => k.startsWith("color-"))
113
+ .map((k) => k.slice("color-".length))
114
+ .sort();
115
+
116
+ const classes = new Set();
117
+ const unmatched = [];
118
+
119
+ for (const token of colorTokens) {
120
+ const prefixes = classify(token);
121
+ if (prefixes === null) {
122
+ unmatched.push(token);
123
+ continue;
124
+ }
125
+ for (const prefix of prefixes) {
126
+ classes.add(`${prefix}-${token}`);
127
+ }
128
+ }
129
+
130
+ const sorted = Array.from(classes).sort();
131
+
132
+ const banner = `/**
133
+ * GENERATED FILE — DO NOT EDIT BY HAND.
134
+ *
135
+ * Source: scripts/generate-color-utilities.mjs
136
+ * Inputs: ../edges-tokens/dist/tokens.json
137
+ * Tokens: ${colorTokens.length} color tokens → ${sorted.length} utility classes
138
+ *
139
+ * Forces Tailwind to ship a utility for every color token in the design
140
+ * system, regardless of whether it's referenced in this package's source.
141
+ * Apps that consume the prebuilt dist/styles.css get the full token set.
142
+ *
143
+ * To regenerate: \`yarn build\` (runs this script as build:pre, before tsup).
144
+ */
145
+ `;
146
+
147
+ const sourceBlock = `@source inline("${sorted.join(" ")}");\n`;
148
+
149
+ fs.mkdirSync(path.dirname(OUTPUT_PATH), { recursive: true });
150
+ fs.writeFileSync(OUTPUT_PATH, `${banner}\n${sourceBlock}`);
151
+
152
+ console.log(`✅ Wrote ${sorted.length} color utilities to ${path.relative(PKG_ROOT, OUTPUT_PATH)}`);
153
+
154
+ if (unmatched.length > 0) {
155
+ console.error("");
156
+ console.error(`❌ ${unmatched.length} color tokens did not match any classification rule:`);
157
+ for (const t of unmatched) console.error(` - color-${t}`);
158
+ console.error("");
159
+ console.error(" Every color token must map to at least one utility (or be explicitly");
160
+ console.error(" skipped by returning []). Extend the rules in classify() in");
161
+ console.error(" scripts/generate-color-utilities.mjs so this PR's contract holds.");
162
+ process.exit(1);
163
+ }
@@ -20,13 +20,13 @@ The theme is imported via CSS:
20
20
  ## Usage Guidelines
21
21
 
22
22
  - **Use semantic classes over arbitrary values**
23
- - Prefer: `bg-brand-primary`, `text-text-body`, `p-md`, `rounded-lg`
23
+ - Prefer: `bg-brand-primary`, `text-text-primary`, `text-text-secondary`, `p-md`, `rounded-lg`
24
24
  - Avoid: `bg-[#444ae1]`, `text-[#333333]`, `p-[1rem]`, `rounded-[0.5rem]`
25
25
 
26
26
  ## Naming Conventions
27
27
 
28
28
  - Brand colors: `brand-primary`, `brand-light`, `brand-dark`
29
- - Text colors: `text-body`, `text-heading`, `text-muted`, `text-caption`
29
+ - Text colors: `text-primary`, `text-secondary`, `text-inverse`, `text-disabled` (legacy aliases: `text-body`, `text-heading`, `text-muted`, `text-caption`)
30
30
  - Background colors: `background-body`, `background-surface`, `background-muted`
31
31
  - Border colors: `border-default`, `border-focus`, `border-muted`
32
32
  - Action colors: `action-primary`, `action-secondary`, `action-destructive`
@@ -39,9 +39,9 @@ The theme is imported via CSS:
39
39
 
40
40
  ```html
41
41
  <!-- ✅ Good - Uses semantic classes -->
42
- <div class="bg-brand-primary text-text-on-primary p-md rounded-lg shadow-md">
43
- <h2 class="text-text-heading text-lg font-medium">Title</h2>
44
- <p class="text-text-body text-base">Content</p>
42
+ <div class="bg-brand-primary text-text-inverse p-md rounded-lg shadow-md">
43
+ <h2 class="text-text-primary text-lg font-medium">Title</h2>
44
+ <p class="text-text-primary text-base">Content</p>
45
45
  </div>
46
46
 
47
47
  <!-- ❌ Avoid - Uses arbitrary values -->