@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.
- package/README.md +57 -3
- package/dist/{RichTextEditor-CxrunTg7.d.cts → RichTextEditor-diZqy_s1.d.cts} +1 -1
- package/dist/{RichTextEditor-CxrunTg7.d.ts → RichTextEditor-diZqy_s1.d.ts} +1 -1
- package/dist/{TimeField-DRQshIHX.d.ts → TimeField-7pTUPh11.d.ts} +32 -17
- package/dist/{TimeField-WCmeiLu3.d.cts → TimeField-CqmVrAdn.d.cts} +32 -17
- package/dist/{colors-Cgs2MGZ8.d.ts → colors-CoULWZ5j.d.ts} +53 -18
- package/dist/{colors-DGRiGzgj.d.cts → colors-upTGgIQe.d.cts} +53 -18
- package/dist/form/index.cjs +1 -1
- package/dist/form/index.cjs.map +1 -1
- package/dist/form/index.d.cts +1 -1
- package/dist/form/index.d.ts +1 -1
- package/dist/form/index.js +1 -1
- package/dist/form/index.js.map +1 -1
- package/dist/{index-BqpWEc_N.d.ts → index-dofSwYId.d.cts} +5 -5
- package/dist/{index-BqpWEc_N.d.cts → index-dofSwYId.d.ts} +5 -5
- package/dist/index.cjs +10 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +161 -41
- package/dist/index.d.ts +161 -41
- package/dist/index.js +10 -10
- package/dist/index.js.map +1 -1
- package/dist/prose.css +202 -0
- package/dist/rhf/index.cjs +1 -1
- package/dist/rhf/index.cjs.map +1 -1
- package/dist/rhf/index.d.cts +2 -2
- package/dist/rhf/index.d.ts +2 -2
- package/dist/rhf/index.js +1 -1
- package/dist/rhf/index.js.map +1 -1
- package/dist/server.cjs +2 -2
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/server.js +2 -2
- package/dist/server.js.map +1 -1
- package/dist/styles/computed.css +2 -2
- package/dist/styles/responsive-typography.css +9 -7
- package/dist/styles/theme-light-override.css +26 -15
- package/dist/styles.css +2122 -820
- package/dist/theme.css +20 -7
- package/dist/typography.css +150 -0
- package/package.json +13 -14
- package/scripts/check-legacy-action-variants.mjs +96 -0
- package/scripts/check-legacy-font-colors.mjs +92 -0
- package/scripts/copy-assets.js +12 -0
- package/scripts/generate-color-utilities.mjs +163 -0
- package/templates/claude-rules/.claude +5 -5
- package/templates/claude-rules/claude.md +5 -5
- package/templates/codex-rules/codex.md +5 -5
- package/dist/generated/tailwind-tokens-dark.css +0 -397
- package/dist/generated/tailwind-tokens-light.css +0 -611
- package/dist/generated/viz-runtime.css +0 -243
- package/scripts/generate-viz-runtime.js +0 -107
- 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.
|
|
7
|
-
* 3.
|
|
8
|
-
* 4.
|
|
9
|
-
* 5.
|
|
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-*,
|
|
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
|
-
|
|
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.
|
|
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
|
|
48
|
-
"build:
|
|
49
|
-
"
|
|
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
|
|
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.");
|
package/scripts/copy-assets.js
CHANGED
|
@@ -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-
|
|
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-
|
|
43
|
-
<h2 class="text-text-
|
|
44
|
-
<p class="text-text-
|
|
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 -->
|