@botiverse/raft-ui 0.0.0-stage → 0.7.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/CHANGELOG.md +576 -0
- package/LICENSE +21 -0
- package/README.md +103 -2
- package/codemods/package-name.mjs +168 -0
- package/codemods/token-names.mjs +150 -0
- package/dist/cn.d.mts +5 -0
- package/dist/cn.mjs +48 -0
- package/dist/components/combobox-select/combobox-select.css +286 -0
- package/dist/components/float-button/float-button.css +316 -0
- package/dist/components/steps/otter-indicator.css +141 -0
- package/dist/components/steps/steps.css +370 -0
- package/dist/components/timeline/timeline.css +255 -0
- package/dist/dialog-CY6S-Xqf.mjs +505 -0
- package/dist/fonts.css +13 -0
- package/dist/foundation.css +623 -0
- package/dist/index-B3_j9XYd.d.mts +329 -0
- package/dist/index.d.mts +5114 -0
- package/dist/index.mjs +16442 -0
- package/dist/style.css +1315 -0
- package/dist/styles.css +444 -0
- package/dist/tokens.json +3811 -0
- package/dist/wip.d.mts +92 -0
- package/dist/wip.mjs +517 -0
- package/package.json +92 -3
- package/skills/raft-ui-critique/SKILL.md +167 -0
- package/skills/raft-ui-critique/agents/openai.yaml +4 -0
- package/skills/raft-ui-critique/references/theme-lenses.md +69 -0
- package/skills/raft-ui-guide/SKILL.md +96 -0
- package/skills/raft-ui-guide/agents/openai.yaml +4 -0
- package/skills/raft-ui-guide/rules/adoption.md +55 -0
- package/skills/raft-ui-guide/rules/composition.md +14 -0
- package/skills/raft-ui-guide/rules/conversation.md +15 -0
- package/skills/raft-ui-guide/rules/display.md +14 -0
- package/skills/raft-ui-guide/rules/feedback.md +13 -0
- package/skills/raft-ui-guide/rules/forms.md +13 -0
- package/skills/raft-ui-guide/rules/layout.md +15 -0
- package/skills/raft-ui-guide/rules/lookup.md +78 -0
- package/skills/raft-ui-guide/rules/overlays.md +13 -0
- package/skills/raft-ui-guide/rules/overview.md +30 -0
- package/skills/raft-ui-guide/rules/styling.md +17 -0
- package/skills/raft-ui-guide/rules/technical-overview.md +80 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Botiverse
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,104 @@
|
|
|
1
|
-
#
|
|
1
|
+
# raft-ui
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Raft Design System foundations for plain HTML, plus React components built with Base UI and Tailwind CSS v4.
|
|
4
|
+
|
|
5
|
+
## React
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Button } from "@botiverse/raft-ui";
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```css
|
|
12
|
+
@import "tailwindcss";
|
|
13
|
+
@import "@botiverse/raft-ui/styles.css";
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Plain HTML
|
|
17
|
+
|
|
18
|
+
Link `dist/foundation.css` directly, or copy it beside your HTML. It exposes the same theme variables used by the components, without React, Tailwind, or a CSS reset.
|
|
19
|
+
|
|
20
|
+
```html
|
|
21
|
+
<link rel="stylesheet" href="./foundation.css" />
|
|
22
|
+
<style>
|
|
23
|
+
body {
|
|
24
|
+
background: var(--layer-canvas);
|
|
25
|
+
color: var(--foreground);
|
|
26
|
+
}
|
|
27
|
+
article {
|
|
28
|
+
background: var(--layer-canvas);
|
|
29
|
+
}
|
|
30
|
+
</style>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Put `class="light"` or `class="dark"` on the root to pick a mode; omit it to follow the system mode. Optional `fonts.css` loads web fonts and defines `--heading-font`, `--sans-font`, and `--mono-font`.
|
|
34
|
+
|
|
35
|
+
`styles.css` remains the Tailwind integration entry and imports this foundation automatically.
|
|
36
|
+
|
|
37
|
+
## Tokens
|
|
38
|
+
|
|
39
|
+
`@botiverse/raft-ui/tokens.json` holds every token in the [Design Tokens Community Group format](https://www.designtokens.org/), with light and dark values, for design tools and code generators. `foundation.css` is generated from it.
|
|
40
|
+
|
|
41
|
+
## Agent Skills
|
|
42
|
+
|
|
43
|
+
raft-ui ships two version-matched Agent Skills in the npm package:
|
|
44
|
+
|
|
45
|
+
- `raft-ui-guide` for adopting raft-ui, looking up its API, and where the design system's guidance is.
|
|
46
|
+
- `raft-ui-critique` for visual, interaction, accessibility, and light/dark review.
|
|
47
|
+
|
|
48
|
+
### TanStack Intent
|
|
49
|
+
|
|
50
|
+
Allow raft-ui skills in the consuming project's `package.json`:
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"intent": {
|
|
55
|
+
"skills": ["@botiverse/raft-ui"]
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Install the project guidance, then inspect or load either skill:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pnpm dlx @tanstack/intent@latest install --map
|
|
64
|
+
pnpm dlx @tanstack/intent@latest load @botiverse/raft-ui#raft-ui-guide
|
|
65
|
+
pnpm dlx @tanstack/intent@latest load @botiverse/raft-ui#raft-ui-critique
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Other installers
|
|
69
|
+
|
|
70
|
+
The package uses the standard `skills/*/SKILL.md` layout, so consumers can also use tools such as `skills-npm` or Vercel `skills`. Installer-specific destination names may add their own package namespace.
|
|
71
|
+
|
|
72
|
+
## Development
|
|
73
|
+
|
|
74
|
+
- Install dependencies:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
vp install
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
- Build the library:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
vp pack
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Upgrading from `raft-ui`
|
|
87
|
+
|
|
88
|
+
The package was published as `raft-ui` up to 0.6.0. To move a project:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
pnpm add @botiverse/raft-ui
|
|
92
|
+
node node_modules/@botiverse/raft-ui/codemods/package-name.mjs src
|
|
93
|
+
node node_modules/@botiverse/raft-ui/codemods/token-names.mjs src
|
|
94
|
+
pnpm remove raft-ui
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The first command rewrites imports, CSS `@import` and `@source` paths and the dependency, and lists any bare `"raft-ui"` strings (bundler aliases, path mappings) to change by hand. The second moves to the current token names; the former names keep working until 1.0.
|
|
98
|
+
|
|
99
|
+
Review two kinds of code by hand after `token-names.mjs`:
|
|
100
|
+
|
|
101
|
+
- **Guards that forbid a name.** A lint rule, a test or an allowlist that bans a former name (for example a `doesNotMatch(/text-foreground-strong/)`) has that name rewritten too, so it now bans the current one. Put the former name back there.
|
|
102
|
+
- **Patterns built from parts.** A regex such as `/bg-brutal-[a-z]+/` is not a literal token, so it keeps the former name and stops matching the rewritten source. Widen it to the current name (`palette-`, `brand-`, `attention-`).
|
|
103
|
+
|
|
104
|
+
If a package you depend on declares `raft-ui` as a peer dependency, also install `"raft-ui": "npm:@botiverse/raft-ui@<version>"` so it resolves to the same copy instead of bundling a second one.
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Moves a project from the unscoped `raft-ui` package to `@botiverse/raft-ui`.
|
|
3
|
+
//
|
|
4
|
+
// pnpm add @botiverse/raft-ui # or npm install, yarn add
|
|
5
|
+
// node node_modules/@botiverse/raft-ui/codemods/package-name.mjs <dir>... [--version 0.7.0] [--dry]
|
|
6
|
+
//
|
|
7
|
+
// Rewrites import and require specifiers, CSS `@import` / `@source` /
|
|
8
|
+
// `@reference` paths, `node_modules/raft-ui` paths, and the `raft-ui`
|
|
9
|
+
// dependency in package.json files (to `--version`, default the version of the
|
|
10
|
+
// package this script ships in). It lists, without changing, any other place
|
|
11
|
+
// that names the bare string "raft-ui" (a bundler alias, a tsconfig path), so
|
|
12
|
+
// you can check those by hand. Run `token-names.mjs` afterwards to move to the
|
|
13
|
+
// current token names.
|
|
14
|
+
import { readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
import { fileURLToPath } from "node:url";
|
|
17
|
+
|
|
18
|
+
const NEW = "@botiverse/raft-ui";
|
|
19
|
+
const SOURCE = new Set([
|
|
20
|
+
".ts",
|
|
21
|
+
".tsx",
|
|
22
|
+
".js",
|
|
23
|
+
".jsx",
|
|
24
|
+
".mjs",
|
|
25
|
+
".cjs",
|
|
26
|
+
".mts",
|
|
27
|
+
".cts",
|
|
28
|
+
".css",
|
|
29
|
+
".html",
|
|
30
|
+
".vue",
|
|
31
|
+
".svelte",
|
|
32
|
+
]);
|
|
33
|
+
const SKIP = new Set(["node_modules", "dist", "build", ".git", ".next", ".turbo", "coverage"]);
|
|
34
|
+
|
|
35
|
+
const RULES = [
|
|
36
|
+
// `(?<!@)import`: a CSS `@import` is the next rule's, not a JS import.
|
|
37
|
+
[/(from\s+|import\(\s*|require\(\s*|(?<!@)import\s+)(["'])raft-ui(?=["'/])/g, `$1$2${NEW}`],
|
|
38
|
+
[
|
|
39
|
+
/(@import|@source|@reference|@plugin)(\s+(?:url\()?["'])([^"']*?)(?<![\w@/-])raft-ui(?=["'/])/g,
|
|
40
|
+
`$1$2$3${NEW}`,
|
|
41
|
+
],
|
|
42
|
+
[/node_modules\/raft-ui(?=["'/\s)])/g, `node_modules/${NEW}`],
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
/** Whether `index` in `line` sits inside a JavaScript regex literal (`/…from "raft-ui"…/`). */
|
|
46
|
+
function insideRegexLiteral(line, index) {
|
|
47
|
+
const before = line.slice(0, index);
|
|
48
|
+
const opener = /(?:^|[(,=:[!&|?{};])\s*\/(?![/*])/g;
|
|
49
|
+
let start = -1;
|
|
50
|
+
for (const match of before.matchAll(opener)) start = match.index + match[0].length;
|
|
51
|
+
if (start < 0) return false;
|
|
52
|
+
// Closed again before the match: an unescaped `/` after the opener.
|
|
53
|
+
return !/(?<!\\)\//.test(before.slice(start));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function renameSpecifiers(source) {
|
|
57
|
+
return source
|
|
58
|
+
.split("\n")
|
|
59
|
+
.map((line) => {
|
|
60
|
+
let out = line;
|
|
61
|
+
for (const [pattern, replacement] of RULES) {
|
|
62
|
+
out = out.replace(pattern, (...args) => {
|
|
63
|
+
const offset = args.at(-2);
|
|
64
|
+
const groups = args.slice(1, -2);
|
|
65
|
+
const replaced = replacement.replace(/\$(\d)/g, (_, n) => groups[Number(n) - 1] ?? "");
|
|
66
|
+
// A slash in a regex literal ends it: write `@botiverse\/raft-ui` there.
|
|
67
|
+
return insideRegexLiteral(out, offset)
|
|
68
|
+
? replaced.replace(NEW, NEW.replace("/", "\\/"))
|
|
69
|
+
: replaced;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
})
|
|
74
|
+
.join("\n");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function renameDependency(json, version) {
|
|
78
|
+
let changed = false;
|
|
79
|
+
for (const field of [
|
|
80
|
+
"dependencies",
|
|
81
|
+
"devDependencies",
|
|
82
|
+
"peerDependencies",
|
|
83
|
+
"optionalDependencies",
|
|
84
|
+
]) {
|
|
85
|
+
const deps = json[field];
|
|
86
|
+
// `"raft-ui": "npm:@botiverse/raft-ui@…"` is an alias kept on purpose, for a dependency
|
|
87
|
+
// that peers on the unscoped name: leave it.
|
|
88
|
+
if (!deps || !("raft-ui" in deps) || deps["raft-ui"].startsWith(`npm:${NEW}@`)) continue;
|
|
89
|
+
const next = {};
|
|
90
|
+
for (const [name, range] of Object.entries(deps)) {
|
|
91
|
+
if (name === "raft-ui")
|
|
92
|
+
next[NEW] = range.startsWith("workspace:") || range.startsWith("link:") ? range : version;
|
|
93
|
+
else next[name] = range;
|
|
94
|
+
}
|
|
95
|
+
json[field] = next;
|
|
96
|
+
changed = true;
|
|
97
|
+
}
|
|
98
|
+
return changed;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function* walk(target) {
|
|
102
|
+
const stat = statSync(target);
|
|
103
|
+
if (stat.isFile()) {
|
|
104
|
+
yield target;
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
for (const name of readdirSync(target)) {
|
|
108
|
+
if (SKIP.has(name)) continue;
|
|
109
|
+
yield* walk(path.join(target, name));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function main(argv) {
|
|
114
|
+
const dry = argv.includes("--dry");
|
|
115
|
+
const versionIndex = argv.indexOf("--version");
|
|
116
|
+
const ownPackage = JSON.parse(
|
|
117
|
+
readFileSync(
|
|
118
|
+
path.join(path.dirname(fileURLToPath(import.meta.url)), "../package.json"),
|
|
119
|
+
"utf8",
|
|
120
|
+
),
|
|
121
|
+
);
|
|
122
|
+
const version = versionIndex >= 0 ? argv[versionIndex + 1] : ownPackage.version;
|
|
123
|
+
const targets = argv.filter(
|
|
124
|
+
(arg, index) => !arg.startsWith("--") && argv[index - 1] !== "--version",
|
|
125
|
+
);
|
|
126
|
+
if (targets.length === 0) {
|
|
127
|
+
console.error("usage: package-name.mjs <dir>... [--version x.y.z] [--dry]");
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
let changed = 0;
|
|
131
|
+
const review = [];
|
|
132
|
+
for (const target of targets) {
|
|
133
|
+
for (const file of walk(target)) {
|
|
134
|
+
const base = path.basename(file);
|
|
135
|
+
const before = readFileSync(file, "utf8");
|
|
136
|
+
let after = before;
|
|
137
|
+
if (base === "package.json") {
|
|
138
|
+
const json = JSON.parse(before);
|
|
139
|
+
if (renameDependency(json, version)) after = `${JSON.stringify(json, null, 2)}\n`;
|
|
140
|
+
} else if (SOURCE.has(path.extname(file))) {
|
|
141
|
+
after = renameSpecifiers(before);
|
|
142
|
+
} else continue;
|
|
143
|
+
if (after !== before) {
|
|
144
|
+
changed += 1;
|
|
145
|
+
console.log(`${dry ? "would update" : "updated"} ${file}`);
|
|
146
|
+
if (!dry) writeFileSync(file, after);
|
|
147
|
+
}
|
|
148
|
+
after.split("\n").forEach((line, index) => {
|
|
149
|
+
// A bare "raft-ui" or "raft-ui/<subpath>" string left over: an alias key, a dedupe list,
|
|
150
|
+
// require.resolve, a path mapping.
|
|
151
|
+
if (/(?<![\w@/-])["']raft-ui(?:\/[^"']*)?["']/.test(line) && base !== "package.json") {
|
|
152
|
+
review.push(`${file}:${index + 1}: ${line.trim()}`);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
console.log(`${changed} file${changed === 1 ? "" : "s"} ${dry ? "to update" : "updated"}`);
|
|
158
|
+
if (review.length) {
|
|
159
|
+
console.log(
|
|
160
|
+
`\nCheck these by hand (a bare "raft-ui" string, such as an alias or a path mapping):`,
|
|
161
|
+
);
|
|
162
|
+
for (const line of review) console.log(` ${line}`);
|
|
163
|
+
}
|
|
164
|
+
if (!dry && changed)
|
|
165
|
+
console.log("\nNext: install dependencies, then run token-names.mjs on the same directories.");
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (import.meta.url === `file://${process.argv[1]}`) main(process.argv.slice(2));
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Renames raft-ui tokens to the names the Raft Design System docs use.
|
|
3
|
+
//
|
|
4
|
+
// node node_modules/@botiverse/raft-ui/codemods/token-names.mjs <dir-or-file>... [--dry]
|
|
5
|
+
//
|
|
6
|
+
// Rewrites Tailwind utilities (`bg-layer-panel`, `hover:border-line-muted/50`)
|
|
7
|
+
// and CSS custom properties (`var(--primary-soft)`) in .ts, .tsx, .js, .jsx,
|
|
8
|
+
// .mjs, .css and .html files. The old names keep working as deprecated aliases
|
|
9
|
+
// until raft-ui 1.0, so the rewrite can land at any time after upgrading.
|
|
10
|
+
//
|
|
11
|
+
// Review by hand afterwards: a guard that forbids a former name (a lint rule,
|
|
12
|
+
// a test's doesNotMatch, an allowlist) now forbids the current one, and a
|
|
13
|
+
// pattern built from parts (/bg-brutal-[a-z]+/) keeps the former name.
|
|
14
|
+
import { readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
|
|
17
|
+
/** Old token name -> new token name. Used for both utilities and `--` properties. */
|
|
18
|
+
export const TOKEN_RENAMES = {
|
|
19
|
+
"layer-panel": "layer-canvas",
|
|
20
|
+
"layer-inset": "fill-muted",
|
|
21
|
+
"layer-canvas-muted": "layer-sidebar",
|
|
22
|
+
"foreground-strong": "foreground",
|
|
23
|
+
"foreground-icon": "foreground-muted",
|
|
24
|
+
"foreground-placeholder": "foreground-hint",
|
|
25
|
+
"layer-hud-foreground": "foreground-on-hud",
|
|
26
|
+
"line-muted": "line-hairline",
|
|
27
|
+
line: "line-seam",
|
|
28
|
+
"theme-shadow-float": "elevation-float",
|
|
29
|
+
"theme-shadow-lg": "elevation-float",
|
|
30
|
+
"theme-shadow-xl": "elevation-float",
|
|
31
|
+
"theme-shadow-sticker": "elevation-sticker",
|
|
32
|
+
"theme-focus-ring": "focus-ring",
|
|
33
|
+
"soft-signal": "palette-yellow",
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/** Families renamed with every step: `primary-soft` -> `brand-soft`. */
|
|
37
|
+
export const FAMILY_RENAMES = {
|
|
38
|
+
primary: "brand",
|
|
39
|
+
accent: "attention",
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/** The brand palette loses the name of a theme family that no longer exists. */
|
|
43
|
+
export const PALETTE_COLORS = {
|
|
44
|
+
yellow: "yellow",
|
|
45
|
+
pink: "pink",
|
|
46
|
+
cyan: "cyan",
|
|
47
|
+
lime: "lime",
|
|
48
|
+
orange: "orange",
|
|
49
|
+
red: "red",
|
|
50
|
+
purple: "lavender",
|
|
51
|
+
lavender: "lavender",
|
|
52
|
+
stone: "stone",
|
|
53
|
+
cream: "cream",
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/** Shadow utilities, which are named apart from their properties. */
|
|
57
|
+
export const UTILITY_RENAMES = {
|
|
58
|
+
"shadow-raft-float": "shadow-float",
|
|
59
|
+
"shadow-raft-lg": "shadow-float",
|
|
60
|
+
"shadow-raft-xl": "shadow-float",
|
|
61
|
+
"shadow-raft-sticker": "shadow-sticker",
|
|
62
|
+
"shadow-raft-focus": "shadow-focus",
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const COLOR_UTILITY =
|
|
66
|
+
"(?:bg|text|border(?:-[trblxyse])?|ring|ring-offset|inset-ring|outline|fill|stroke|divide|from|via|to|shadow|inset-shadow|decoration|caret|accent|placeholder)";
|
|
67
|
+
const END = "(?![\\w-])";
|
|
68
|
+
// A custom property, not a BEM modifier: `var(--primary-soft)` but not `.r-button--primary`.
|
|
69
|
+
const VAR = "(?<![\\w-])--";
|
|
70
|
+
// A reference, not a declaration: `var(--line)` is renamed, `--line: …` is left alone,
|
|
71
|
+
// so the deprecated aliases (`--line: var(--line-seam)`) survive a rerun.
|
|
72
|
+
const REF_END = "(?![\\w-])(?!\\s*:)";
|
|
73
|
+
const escape = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
74
|
+
|
|
75
|
+
function rules() {
|
|
76
|
+
const list = [];
|
|
77
|
+
for (const [from, to] of Object.entries(UTILITY_RENAMES)) {
|
|
78
|
+
list.push([new RegExp(`(?<![\\w-])${escape(from)}${END}`, "g"), to]);
|
|
79
|
+
}
|
|
80
|
+
// Longer names first, so `line-muted` is handled before `line`.
|
|
81
|
+
const tokens = Object.entries(TOKEN_RENAMES).sort(([a], [b]) => b.length - a.length);
|
|
82
|
+
for (const [from, to] of tokens) {
|
|
83
|
+
list.push([new RegExp(`(?<![\\w-])(${COLOR_UTILITY})-${escape(from)}${END}`, "g"), `$1-${to}`]);
|
|
84
|
+
list.push([new RegExp(`${VAR}${escape(from)}${REF_END}`, "g"), `--${to}`]);
|
|
85
|
+
}
|
|
86
|
+
for (const [from, to] of Object.entries(FAMILY_RENAMES)) {
|
|
87
|
+
list.push([
|
|
88
|
+
new RegExp(`(?<![\\w-])(${COLOR_UTILITY})-${from}((?:-[a-z0-9]+)*)${END}`, "g"),
|
|
89
|
+
`$1-${to}$2`,
|
|
90
|
+
]);
|
|
91
|
+
list.push([new RegExp(`${VAR}${from}((?:-[a-z0-9]+)*)${REF_END}`, "g"), `--${to}$1`]);
|
|
92
|
+
}
|
|
93
|
+
for (const [from, to] of Object.entries(PALETTE_COLORS)) {
|
|
94
|
+
list.push([
|
|
95
|
+
new RegExp(`${VAR}color-brutal-${from}-(\\d+)${REF_END}`, "g"),
|
|
96
|
+
`--palette-${to}-$1`,
|
|
97
|
+
]);
|
|
98
|
+
list.push([new RegExp(`${VAR}color-brutal-${from}${REF_END}`, "g"), `--color-palette-${to}`]);
|
|
99
|
+
list.push([
|
|
100
|
+
new RegExp(`(?<![\\w-])(${COLOR_UTILITY})-brutal-${from}${END}`, "g"),
|
|
101
|
+
`$1-palette-${to}`,
|
|
102
|
+
]);
|
|
103
|
+
}
|
|
104
|
+
list.push([new RegExp(`${VAR}color-soft-signal${REF_END}`, "g"), "--color-palette-yellow"]);
|
|
105
|
+
return list;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function renameTokens(source) {
|
|
109
|
+
let out = source;
|
|
110
|
+
for (const [pattern, replacement] of rules()) out = out.replace(pattern, replacement);
|
|
111
|
+
return out;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const EXTENSIONS = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".css", ".html"]);
|
|
115
|
+
const SKIP = new Set(["node_modules", "dist", "build", ".git"]);
|
|
116
|
+
|
|
117
|
+
function* walk(target) {
|
|
118
|
+
const stat = statSync(target);
|
|
119
|
+
if (stat.isFile()) {
|
|
120
|
+
if (EXTENSIONS.has(path.extname(target))) yield target;
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
for (const name of readdirSync(target)) {
|
|
124
|
+
if (SKIP.has(name) || name.startsWith(".")) continue;
|
|
125
|
+
yield* walk(path.join(target, name));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function main(argv) {
|
|
130
|
+
const dry = argv.includes("--dry");
|
|
131
|
+
const targets = argv.filter((arg) => !arg.startsWith("--"));
|
|
132
|
+
if (targets.length === 0) {
|
|
133
|
+
console.error("usage: token-names.mjs <dir-or-file>... [--dry]");
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
let changed = 0;
|
|
137
|
+
for (const target of targets) {
|
|
138
|
+
for (const file of walk(target)) {
|
|
139
|
+
const before = readFileSync(file, "utf8");
|
|
140
|
+
const after = renameTokens(before);
|
|
141
|
+
if (after === before) continue;
|
|
142
|
+
changed += 1;
|
|
143
|
+
console.log(`${dry ? "would update" : "updated"} ${file}`);
|
|
144
|
+
if (!dry) writeFileSync(file, after);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
console.log(`${changed} file${changed === 1 ? "" : "s"} ${dry ? "to update" : "updated"}`);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (import.meta.url === `file://${process.argv[1]}`) main(process.argv.slice(2));
|
package/dist/cn.d.mts
ADDED
package/dist/cn.mjs
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { clsx } from "cnfast";
|
|
2
|
+
import { extendTailwindMerge } from "tailwind-merge";
|
|
3
|
+
//#region src/lib/cn.ts
|
|
4
|
+
const twMerge = extendTailwindMerge({ extend: { classGroups: {
|
|
5
|
+
shadow: [{ shadow: [
|
|
6
|
+
"float",
|
|
7
|
+
"sticker",
|
|
8
|
+
"focus"
|
|
9
|
+
] }, { shadow: [{ raft: [
|
|
10
|
+
"xs",
|
|
11
|
+
"sm",
|
|
12
|
+
"md",
|
|
13
|
+
"lg",
|
|
14
|
+
"xl",
|
|
15
|
+
"float",
|
|
16
|
+
"sticker",
|
|
17
|
+
"focus"
|
|
18
|
+
] }] }],
|
|
19
|
+
rounded: [{ rounded: [
|
|
20
|
+
"sticker",
|
|
21
|
+
"chip",
|
|
22
|
+
"control",
|
|
23
|
+
"container",
|
|
24
|
+
"dialog"
|
|
25
|
+
] }],
|
|
26
|
+
"font-size": [{ text: [
|
|
27
|
+
"field",
|
|
28
|
+
"card-title",
|
|
29
|
+
"hero",
|
|
30
|
+
"display",
|
|
31
|
+
"title",
|
|
32
|
+
"headline",
|
|
33
|
+
"body",
|
|
34
|
+
"subhead",
|
|
35
|
+
"callout",
|
|
36
|
+
"footnote",
|
|
37
|
+
"caption",
|
|
38
|
+
"label",
|
|
39
|
+
"mono-meta",
|
|
40
|
+
"mono-code"
|
|
41
|
+
] }],
|
|
42
|
+
"font-weight": [{ font: ["field", "card-title"] }]
|
|
43
|
+
} } });
|
|
44
|
+
function cn(...inputs) {
|
|
45
|
+
return twMerge(clsx(inputs));
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
export { cn };
|