cursedbelt 2.3.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/styles-static.css +41 -19
- package/dist/styles-utilities.css +5074 -0
- package/dist/styles.css +41 -19
- package/package.json +13 -3
- package/scripts/generateUtilityStyles.ts +215 -0
- package/src/styles-static.css +41 -19
- package/src/styles-utilities.css +5074 -0
- package/src/styles.css +41 -19
- package/src/stylesUtilitiesMatches.spec.ts +314 -0
package/dist/styles.css
CHANGED
|
@@ -6,32 +6,54 @@
|
|
|
6
6
|
* This `@source` makes Tailwind v4 scan cursedbelt's source files so the utility
|
|
7
7
|
* classes used by its components are generated in the app's stylesheet.
|
|
8
8
|
*
|
|
9
|
-
* ── Two
|
|
9
|
+
* ── Two ways in, and which one an app wants ──────────────────────────────────
|
|
10
10
|
* `@source "."` is the expensive half: it points Tailwind's JIT at cursedbelt's
|
|
11
11
|
* ENTIRE source tree, so the app emits the union of the whole design system
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* whether it renders three components or eighty, and its own build walks 827
|
|
13
|
+
* files it does not own. Everything else in this file is CSS the components
|
|
14
|
+
* genuinely need and no app can do without.
|
|
15
15
|
*
|
|
16
|
-
* A CSS `@import` takes the whole file, so the split is
|
|
16
|
+
* A CSS `@import` takes the whole file, so the split is two more subpaths:
|
|
17
17
|
*
|
|
18
|
-
* cursedbelt/styles.css
|
|
19
|
-
* cursedbelt/styles-static.css
|
|
18
|
+
* cursedbelt/styles.css this file — the real CSS + the `@source` scan
|
|
19
|
+
* cursedbelt/styles-static.css the same file MINUS the `@source` line
|
|
20
|
+
* cursedbelt/styles-utilities.css the utilities that `@source` line would have
|
|
21
|
+
* produced, precompiled at cursedbelt's build
|
|
20
22
|
*
|
|
21
|
-
* The
|
|
22
|
-
* tooltip pointer-events fix, the Progress keyframes, the
|
|
23
|
-
* ambient/glass layer and `tw-animate-css`, and emits none
|
|
24
|
-
* utility classes,
|
|
25
|
-
* sources IT registers. Do not hand-list cursedbelt's component files to get
|
|
26
|
-
* them back: a relative `@source` reaching into this package's source tree is a
|
|
27
|
-
* `file:` dependency wearing a stylesheet's clothes, and it is the thing the
|
|
28
|
-
* previous generation's 29-line-per-app arrangement is remembered for.
|
|
23
|
+
* The second two are a PAIR and neither is usable alone — `styles-static.css` on
|
|
24
|
+
* its own gets the tooltip pointer-events fix, the Progress keyframes, the
|
|
25
|
+
* lightbox bridge, the ambient/glass layer and `tw-animate-css`, and emits none
|
|
26
|
+
* of cursedbelt's utility classes, i.e. an unstyled app:
|
|
29
27
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
28
|
+
* @import "tailwindcss";
|
|
29
|
+
* @import "cursedbelt/styles-static.css";
|
|
30
|
+
* @import "cursedbelt/styles-utilities.css";
|
|
31
|
+
* @import "cursedbelt/theme.css";
|
|
32
|
+
*
|
|
33
|
+
* 🔴 Do NOT hand-list cursedbelt's component files to get the utilities back.
|
|
34
|
+
* Tailwind v4 resolves every `@source` to a DIRECTORY and scans it recursively —
|
|
35
|
+
* a file path and a glob are both silently widened to their parent — and
|
|
36
|
+
* `VirtualList`/`VirtualCardGrid` live at the root of `src/react/`, so the finest
|
|
37
|
+
* grain an app can write is the whole design system. A relative `@source` reaching
|
|
38
|
+
* into this package's source tree is also a `file:` dependency wearing a
|
|
39
|
+
* stylesheet's clothes, and it buys nothing: it is the thing the previous
|
|
40
|
+
* generation's 29-line-per-app arrangement is remembered for, and those 29 lines
|
|
41
|
+
* were never file-level scoping either.
|
|
42
|
+
*
|
|
43
|
+
* The pair is not SMALLER than this file — measured, both routes emit the same
|
|
44
|
+
* rules within 54 bytes. What it buys is a build that scans nothing of another
|
|
45
|
+
* package's source tree, and a `styles-static.css` that finally has a correct
|
|
46
|
+
* use. The measurements, and what would actually shrink a consumer, are in the
|
|
47
|
+
* header of `scripts/generateUtilityStyles.ts`.
|
|
48
|
+
*
|
|
49
|
+
* 🔴 This header is in BOTH of the first two files, because one is generated from
|
|
50
|
+
* the other and a test holds them byte-identical apart from the `@source` line.
|
|
51
|
+
* Always edit `src/styles.css`; never `src/styles-static.css`, which
|
|
33
52
|
* `scripts/generateStaticStyles.ts` rewrites on every `bun run build` and
|
|
34
|
-
* `src/stylesStaticMatches.spec.ts` fails on when it is stale.
|
|
53
|
+
* `src/stylesStaticMatches.spec.ts` fails on when it is stale. Editing this file
|
|
54
|
+
* invalidates `src/styles-utilities.css` too — it is compiled against the static
|
|
55
|
+
* variant, `scripts/generateUtilityStyles.ts` rewrites it in the same build step,
|
|
56
|
+
* and `src/stylesUtilitiesMatches.spec.ts` is what fails when it is stale.
|
|
35
57
|
*/
|
|
36
58
|
|
|
37
59
|
/* Animation utilities (animate-in/out, fade/zoom/slide-in-from-*) consumed by the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cursedbelt",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
4
4
|
"license": "ISC",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Shared Bun + React framework",
|
|
@@ -30,13 +30,14 @@
|
|
|
30
30
|
"guardrails:report": "bun run scripts/guardrailsEnforce.ts no-raw-action-button,no-raw-overflow,no-raw-inputs,no-hardcoded-colors,no-raw-modal-footer,no-unguarded-editing-modal,control-height-tokens,no-raw-zindex,no-raw-data-table,no-tailwind-palette,no-raw-title-attr,micro-typography-floor,choice-needs-empty-state,no-select-as-action-menu,no-native-dialog,no-fast-tooltip --scope cursedbelt src/react src/styles.css",
|
|
31
31
|
"format": "biome format --write .",
|
|
32
32
|
"styles:static": "bun run scripts/generateStaticStyles.ts",
|
|
33
|
+
"styles:utilities": "bun run scripts/generateUtilityStyles.ts",
|
|
33
34
|
"test": "NODE_ENV=development bun test src",
|
|
34
35
|
"test:pg": "bun run scripts/testPg.ts",
|
|
35
36
|
"demo": "vite --config demo/vite.config.ts",
|
|
36
37
|
"demo:build": "vite build --config demo/vite.config.ts",
|
|
37
38
|
"demo:serve": "bun run demo:build && vite preview --config demo/vite.config.ts",
|
|
38
39
|
"prebuild": "bun run verify:gate",
|
|
39
|
-
"build": "rm -rf dist.next dist.prev && bun run scripts/generateStaticStyles.ts && tsc --project tsconfig.build.json --outDir dist.next && tsc --project tsconfig.build.server.json --outDir dist.next && CURSEDBELT_DIST_DIR=dist.next bun run scripts/copyDistAssets.ts && { [ -d dist ] && mv dist dist.prev || true; } && mv dist.next dist && rm -rf dist.prev",
|
|
40
|
+
"build": "rm -rf dist.next dist.prev && bun run scripts/generateStaticStyles.ts && bun run scripts/generateUtilityStyles.ts && tsc --project tsconfig.build.json --outDir dist.next && tsc --project tsconfig.build.server.json --outDir dist.next && CURSEDBELT_DIST_DIR=dist.next bun run scripts/copyDistAssets.ts && { [ -d dist ] && mv dist dist.prev || true; } && mv dist.next dist && rm -rf dist.prev",
|
|
40
41
|
"e2e": "playwright test --config e2e/playwright.config.ts",
|
|
41
42
|
"e2e:filter-rail": "playwright test --config e2e/playwright.config.ts filter-rail.spec.ts",
|
|
42
43
|
"e2e:table-layout": "playwright test --config e2e/playwright.config.ts table-layout.spec.ts",
|
|
@@ -488,7 +489,13 @@
|
|
|
488
489
|
"source": "./src/server/index.ts",
|
|
489
490
|
"import": "./dist/server/index.js"
|
|
490
491
|
},
|
|
491
|
-
"./server/
|
|
492
|
+
"./server/login-throttle": {
|
|
493
|
+
"types": "./dist/server/auth/loginThrottle.d.ts",
|
|
494
|
+
"bun": "./src/server/auth/loginThrottle.ts",
|
|
495
|
+
"source": "./src/server/auth/loginThrottle.ts",
|
|
496
|
+
"import": "./dist/server/auth/loginThrottle.js"
|
|
497
|
+
},
|
|
498
|
+
"./server/guard/revocations": {
|
|
492
499
|
"types": "./dist/server/guard/revocationStore.d.ts",
|
|
493
500
|
"bun": "./src/server/guard/revocationStore.ts",
|
|
494
501
|
"source": "./src/server/guard/revocationStore.ts",
|
|
@@ -514,6 +521,7 @@
|
|
|
514
521
|
},
|
|
515
522
|
"./styles.css": "./src/styles.css",
|
|
516
523
|
"./styles-static.css": "./src/styles-static.css",
|
|
524
|
+
"./styles-utilities.css": "./src/styles-utilities.css",
|
|
517
525
|
"./theme.css": "./src/theme.css",
|
|
518
526
|
"./workspace.css": "./src/workspace.css",
|
|
519
527
|
"./react/keep-awake": {
|
|
@@ -771,6 +779,8 @@
|
|
|
771
779
|
"@lezer/highlight": "^1.2.3",
|
|
772
780
|
"@node-rs/argon2": "^2.0.2",
|
|
773
781
|
"@playwright/test": "^1.61.0",
|
|
782
|
+
"@tailwindcss/node": "^4.3.1",
|
|
783
|
+
"@tailwindcss/oxide": "^4.3.1",
|
|
774
784
|
"@tailwindcss/vite": "^4.3.1",
|
|
775
785
|
"@tanstack/react-query": "5.101.2",
|
|
776
786
|
"@testing-library/dom": "^10.4.1",
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate `src/styles-utilities.css` — cursedbelt's component utilities, PRECOMPILED.
|
|
3
|
+
*
|
|
4
|
+
* ── The hole this fills ─────────────────────────────────────────────────────
|
|
5
|
+
* `src/styles-static.css` (the sibling generator, `generateStaticStyles.ts`) is
|
|
6
|
+
* `styles.css` minus its `@source "."`. It was shipped for "an app that pays for
|
|
7
|
+
* its own scanning" — and until this file existed, no app could pay. Tailwind v4
|
|
8
|
+
* (4.3.3, measured) resolves every `@source` to a DIRECTORY and scans it
|
|
9
|
+
* recursively: a file path is silently widened to its parent, a glob is widened
|
|
10
|
+
* the same way, and `@source not` over a package's own tree subtracts nothing.
|
|
11
|
+
* All four of these emit byte-for-byte identical CSS in a consuming app:
|
|
12
|
+
*
|
|
13
|
+
* @source ".../cursedbelt/src/react/index.*";
|
|
14
|
+
* @source ".../cursedbelt/src/react/styling.*";
|
|
15
|
+
* @source ".../cursedbelt/src/react/Virtual*.tsx";
|
|
16
|
+
* @source ".../cursedbelt/src/react/*.{ts,tsx}";
|
|
17
|
+
*
|
|
18
|
+
* `VirtualList` and `VirtualCardGrid` sit at the ROOT of `src/react/`, so the
|
|
19
|
+
* finest grain an app can ask for is that directory — whose recursive contents
|
|
20
|
+
* are the whole design system. Scanning it is the union; not scanning it is an
|
|
21
|
+
* UNSTYLED app. There was no third option, which made `styles-static.css` as
|
|
22
|
+
* published a subpath with no correct use. (It also retires a long-standing
|
|
23
|
+
* misreading: the previous generation's hand-written 29-line `@source` list in
|
|
24
|
+
* `satellite-kit` was never file-level scoping — every line was widened to its
|
|
25
|
+
* parent directory, which is why that arrangement still emitted ~113 KB.)
|
|
26
|
+
*
|
|
27
|
+
* So the utilities become a BUILD ARTIFACT: compiled once, here, and shipped.
|
|
28
|
+
* The consumer scans nothing.
|
|
29
|
+
*
|
|
30
|
+
* ── What it is worth, measured, so nobody re-runs this blind ────────────────
|
|
31
|
+
* 🔴 It is NOT smaller. Compiling cursedbelt's own surface both ways (this repo,
|
|
32
|
+
* tailwindcss 4.3.3, lightningcss-minified):
|
|
33
|
+
*
|
|
34
|
+
* @import "cursedbelt/styles.css" 131,795 B · 22,490 gz
|
|
35
|
+
* styles-static.css + styles-utilities.css 131,741 B · 22,462 gz
|
|
36
|
+
*
|
|
37
|
+
* — a 54-byte difference, i.e. the same stylesheet by another route. The union
|
|
38
|
+
* is the union however it is assembled. The 10.3 kB gzip saving flix measured
|
|
39
|
+
* came from scanning exactly the 29 modules Rollup put in its chunk, and that is
|
|
40
|
+
* per-app tree-shaking, which no single shared artifact can do. What this DOES
|
|
41
|
+
* buy: `styles-static.css` gains a correct use, a consuming app stops reaching
|
|
42
|
+
* into another package's source tree from its CSS (a `file:` dependency wearing
|
|
43
|
+
* a stylesheet's clothes), and the app's Tailwind build stops walking 827 files
|
|
44
|
+
* it does not own. Reaching the 16 KB-of-utilities floor flix measured needs
|
|
45
|
+
* per-module CSS the bundler can tree-shake — a different, larger change.
|
|
46
|
+
*
|
|
47
|
+
* ── Why GENERATED, and what holds it honest ─────────────────────────────────
|
|
48
|
+
* Same bargain as `styles-static.css`: this script writes it, `bun run build`
|
|
49
|
+
* runs this script, and `src/stylesUtilitiesMatches.spec.ts` fails when what is
|
|
50
|
+
* on disk is not what `src/react/**` currently produces. That spec does not
|
|
51
|
+
* compare SIZE — an unstyled app is invisible to a byte count read in one
|
|
52
|
+
* direction — it compiles a consumer both ways and compares the emitted rules.
|
|
53
|
+
*
|
|
54
|
+
* Usage:
|
|
55
|
+
* bun run styles:utilities # write src/styles-utilities.css
|
|
56
|
+
* bun run styles:utilities --check # exit 1 if it is stale, write nothing
|
|
57
|
+
*/
|
|
58
|
+
import { compile } from '@tailwindcss/node';
|
|
59
|
+
import { Scanner } from '@tailwindcss/oxide';
|
|
60
|
+
import { REPO_ROOT, STATIC_STYLESHEET, readStylesheets } from './generateStaticStyles';
|
|
61
|
+
|
|
62
|
+
export { REPO_ROOT };
|
|
63
|
+
|
|
64
|
+
export const UTILITY_STYLESHEET = 'src/styles-utilities.css';
|
|
65
|
+
|
|
66
|
+
/** Every relative specifier below resolves against `src/`, like `styles.css` does. */
|
|
67
|
+
export const STYLES_BASE = `${REPO_ROOT}/src`;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The compile input.
|
|
71
|
+
*
|
|
72
|
+
* `@reference` rather than `@import` for the two cursedbelt stylesheets: it makes
|
|
73
|
+
* their `@theme` tokens, `@custom-variant dark` and `tw-animate-css`'s `@utility`
|
|
74
|
+
* definitions available to the compiler while emitting NONE of their CSS — the
|
|
75
|
+
* consumer imports those files itself, and duplicating them here would be the
|
|
76
|
+
* drift this whole arrangement exists to prevent. Dropping the
|
|
77
|
+
* `@reference "./styles-static.css"` line costs 41 rules: every `animate-in` /
|
|
78
|
+
* `fade-out` / `slide-in-from-*` / `data-[state=open]:*` utility the dialog,
|
|
79
|
+
* tooltip and collapsible families animate with, plus
|
|
80
|
+
* `.animate-progress-indeterminate`.
|
|
81
|
+
*
|
|
82
|
+
* `./react` and not `.`: `.` would scan `src/` — including this file's own output,
|
|
83
|
+
* making each generation depend on the last one. It buys nothing anyway; the whole
|
|
84
|
+
* of `src/` outside `src/react` adds 32 bytes.
|
|
85
|
+
*/
|
|
86
|
+
export const UTILITY_INPUT = `@layer theme, base, components, utilities;
|
|
87
|
+
@import "tailwindcss/theme.css" layer(theme);
|
|
88
|
+
@reference "./styles-static.css";
|
|
89
|
+
@reference "./theme.css";
|
|
90
|
+
@import "tailwindcss/utilities.css" layer(utilities);
|
|
91
|
+
@source "./react";
|
|
92
|
+
`;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* What a consuming app's CSS is, today and after adoption — the two inputs
|
|
96
|
+
* `src/stylesUtilitiesMatches.spec.ts` compiles and compares rule by rule.
|
|
97
|
+
*
|
|
98
|
+
* `source(none)` on the second one is the point of the exercise: it forbids
|
|
99
|
+
* Tailwind from scanning anything at all, so whatever comes out is what the
|
|
100
|
+
* precompiled artifact supplies and nothing else. A real app drops it and keeps
|
|
101
|
+
* scanning its OWN sources.
|
|
102
|
+
*/
|
|
103
|
+
export const CONSUMER_SCANNING = '@import "tailwindcss";\n@import "./styles.css";\n@import "./theme.css";\n';
|
|
104
|
+
export const CONSUMER_PRECOMPILED =
|
|
105
|
+
'@import "tailwindcss" source(none);\n' +
|
|
106
|
+
'@import "./styles-static.css";\n' +
|
|
107
|
+
'@import "./styles-utilities.css";\n' +
|
|
108
|
+
'@import "./theme.css";\n';
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* A floor on the scan, because the failure this artifact can have is SILENT.
|
|
112
|
+
* If `src/react` moves, or a `.gitignore` starts swallowing it, or oxide's walk
|
|
113
|
+
* returns nothing, the compile still succeeds — it just emits an empty utility
|
|
114
|
+
* layer, and every consumer ships an unstyled app. 16,594 candidates today; this
|
|
115
|
+
* is a collapse tripwire, not a ratchet.
|
|
116
|
+
*/
|
|
117
|
+
export const MIN_CANDIDATES = 1_000;
|
|
118
|
+
|
|
119
|
+
/** cursedbelt's stylesheets are self-contained — nothing here loads a JS config. */
|
|
120
|
+
const noDependency = (): void => {};
|
|
121
|
+
|
|
122
|
+
/** Compile one of the inputs above against `src/`. */
|
|
123
|
+
export const compileStylesheet = (
|
|
124
|
+
input: string,
|
|
125
|
+
): ReturnType<typeof compile> => compile(input, { base: STYLES_BASE, onDependency: noDependency });
|
|
126
|
+
|
|
127
|
+
/** The banner the artifact carries, so a reader never mistakes it for a source file. */
|
|
128
|
+
export const BANNER = `/*
|
|
129
|
+
* cursedbelt component utilities — GENERATED BUILD ARTIFACT. Never hand-edit.
|
|
130
|
+
*
|
|
131
|
+
* Every Tailwind utility class cursedbelt's own components (\`src/react/**\`) use,
|
|
132
|
+
* compiled once at this package's build time so a consuming app does not have to
|
|
133
|
+
* scan this package's source tree — which Tailwind v4 cannot do at any grain
|
|
134
|
+
* finer than "the whole design system" anyway. Import it after Tailwind itself,
|
|
135
|
+
* beside the static entry point:
|
|
136
|
+
*
|
|
137
|
+
* @import "tailwindcss";
|
|
138
|
+
* @import "cursedbelt/styles-static.css";
|
|
139
|
+
* @import "cursedbelt/styles-utilities.css";
|
|
140
|
+
* @import "cursedbelt/theme.css";
|
|
141
|
+
*
|
|
142
|
+
* That pair emits the same rules as \`@import "cursedbelt/styles.css"\` — proved
|
|
143
|
+
* rule by rule, not by byte count, in src/stylesUtilitiesMatches.spec.ts. It is
|
|
144
|
+
* not SMALLER; see the header of scripts/generateUtilityStyles.ts for the
|
|
145
|
+
* measurements and for what it does buy.
|
|
146
|
+
*
|
|
147
|
+
* Written by scripts/generateUtilityStyles.ts (\`bun run styles:utilities\`),
|
|
148
|
+
* which \`bun run build\` runs. src/stylesUtilitiesMatches.spec.ts fails when what
|
|
149
|
+
* is on disk is not what src/react/** currently produces.
|
|
150
|
+
*/
|
|
151
|
+
`;
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Compile the utility layer from `src/react/**`.
|
|
155
|
+
*
|
|
156
|
+
* Refuses to run against a stale `styles-static.css`, rather than quietly baking
|
|
157
|
+
* the stale one's tokens in: the two artifacts are generated from the same
|
|
158
|
+
* `styles.css`, and `bun run build` orders them, but `bun run styles:utilities`
|
|
159
|
+
* on its own would not.
|
|
160
|
+
*/
|
|
161
|
+
export const buildUtilityStyles = async (): Promise<{ css: string; candidates: string[] }> => {
|
|
162
|
+
const { expected, onDisk } = await readStylesheets();
|
|
163
|
+
if (onDisk !== expected) {
|
|
164
|
+
throw new Error(
|
|
165
|
+
`${STATIC_STYLESHEET} is ${onDisk === null ? 'missing' : 'stale'}, and this artifact is compiled\n` +
|
|
166
|
+
`against it (\`@reference\`), so it would bake in the wrong tokens. Run:\n` +
|
|
167
|
+
` cd ${REPO_ROOT} && bun run styles:static`,
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
const compiled = await compileStylesheet(UTILITY_INPUT);
|
|
171
|
+
const candidates = new Scanner({ sources: compiled.sources }).scan();
|
|
172
|
+
if (candidates.length < MIN_CANDIDATES) {
|
|
173
|
+
throw new Error(
|
|
174
|
+
`the scan of src/react found ${candidates.length} candidates (floor ${MIN_CANDIDATES}).\n` +
|
|
175
|
+
'That is a collapsed scan, not a small design system — the artifact it would write\n' +
|
|
176
|
+
'emits an empty utility layer and every consuming app ships UNSTYLED. Check that\n' +
|
|
177
|
+
`${STYLES_BASE}/react exists and is not excluded by a .gitignore.`,
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
return { css: `${BANNER}${compiled.build(candidates)}`, candidates };
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
/** The artifact's expected contents and what is on disk right now. */
|
|
184
|
+
export const readUtilityStylesheet = async (): Promise<{
|
|
185
|
+
expected: string;
|
|
186
|
+
onDisk: string | null;
|
|
187
|
+
candidates: string[];
|
|
188
|
+
}> => {
|
|
189
|
+
const { css, candidates } = await buildUtilityStyles();
|
|
190
|
+
const target = Bun.file(`${REPO_ROOT}/${UTILITY_STYLESHEET}`);
|
|
191
|
+
return { expected: css, onDisk: (await target.exists()) ? await target.text() : null, candidates };
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
if (import.meta.main) {
|
|
195
|
+
const { expected, onDisk, candidates } = await readUtilityStylesheet();
|
|
196
|
+
if (process.argv.includes('--check')) {
|
|
197
|
+
if (onDisk === expected) {
|
|
198
|
+
console.log(`✅ ${UTILITY_STYLESHEET} is current (${candidates.length} candidates).`);
|
|
199
|
+
process.exit(0);
|
|
200
|
+
}
|
|
201
|
+
console.error(
|
|
202
|
+
`❌ ${UTILITY_STYLESHEET} is ${onDisk === null ? 'missing' : 'stale'}. Run:\n` +
|
|
203
|
+
` cd ${REPO_ROOT} && bun run styles:utilities`,
|
|
204
|
+
);
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
if (onDisk === expected) {
|
|
208
|
+
console.log(`✅ ${UTILITY_STYLESHEET} already current (${candidates.length} candidates).`);
|
|
209
|
+
} else {
|
|
210
|
+
await Bun.write(`${REPO_ROOT}/${UTILITY_STYLESHEET}`, expected);
|
|
211
|
+
console.log(
|
|
212
|
+
`✅ wrote ${UTILITY_STYLESHEET} — ${candidates.length} candidates scanned from src/react.`,
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
}
|
package/src/styles-static.css
CHANGED
|
@@ -6,32 +6,54 @@
|
|
|
6
6
|
* This `@source` makes Tailwind v4 scan cursedbelt's source files so the utility
|
|
7
7
|
* classes used by its components are generated in the app's stylesheet.
|
|
8
8
|
*
|
|
9
|
-
* ── Two
|
|
9
|
+
* ── Two ways in, and which one an app wants ──────────────────────────────────
|
|
10
10
|
* `@source "."` is the expensive half: it points Tailwind's JIT at cursedbelt's
|
|
11
11
|
* ENTIRE source tree, so the app emits the union of the whole design system
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* whether it renders three components or eighty, and its own build walks 827
|
|
13
|
+
* files it does not own. Everything else in this file is CSS the components
|
|
14
|
+
* genuinely need and no app can do without.
|
|
15
15
|
*
|
|
16
|
-
* A CSS `@import` takes the whole file, so the split is
|
|
16
|
+
* A CSS `@import` takes the whole file, so the split is two more subpaths:
|
|
17
17
|
*
|
|
18
|
-
* cursedbelt/styles.css
|
|
19
|
-
* cursedbelt/styles-static.css
|
|
18
|
+
* cursedbelt/styles.css this file — the real CSS + the `@source` scan
|
|
19
|
+
* cursedbelt/styles-static.css the same file MINUS the `@source` line
|
|
20
|
+
* cursedbelt/styles-utilities.css the utilities that `@source` line would have
|
|
21
|
+
* produced, precompiled at cursedbelt's build
|
|
20
22
|
*
|
|
21
|
-
* The
|
|
22
|
-
* tooltip pointer-events fix, the Progress keyframes, the
|
|
23
|
-
* ambient/glass layer and `tw-animate-css`, and emits none
|
|
24
|
-
* utility classes,
|
|
25
|
-
* sources IT registers. Do not hand-list cursedbelt's component files to get
|
|
26
|
-
* them back: a relative `@source` reaching into this package's source tree is a
|
|
27
|
-
* `file:` dependency wearing a stylesheet's clothes, and it is the thing the
|
|
28
|
-
* previous generation's 29-line-per-app arrangement is remembered for.
|
|
23
|
+
* The second two are a PAIR and neither is usable alone — `styles-static.css` on
|
|
24
|
+
* its own gets the tooltip pointer-events fix, the Progress keyframes, the
|
|
25
|
+
* lightbox bridge, the ambient/glass layer and `tw-animate-css`, and emits none
|
|
26
|
+
* of cursedbelt's utility classes, i.e. an unstyled app:
|
|
29
27
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
28
|
+
* @import "tailwindcss";
|
|
29
|
+
* @import "cursedbelt/styles-static.css";
|
|
30
|
+
* @import "cursedbelt/styles-utilities.css";
|
|
31
|
+
* @import "cursedbelt/theme.css";
|
|
32
|
+
*
|
|
33
|
+
* 🔴 Do NOT hand-list cursedbelt's component files to get the utilities back.
|
|
34
|
+
* Tailwind v4 resolves every `@source` to a DIRECTORY and scans it recursively —
|
|
35
|
+
* a file path and a glob are both silently widened to their parent — and
|
|
36
|
+
* `VirtualList`/`VirtualCardGrid` live at the root of `src/react/`, so the finest
|
|
37
|
+
* grain an app can write is the whole design system. A relative `@source` reaching
|
|
38
|
+
* into this package's source tree is also a `file:` dependency wearing a
|
|
39
|
+
* stylesheet's clothes, and it buys nothing: it is the thing the previous
|
|
40
|
+
* generation's 29-line-per-app arrangement is remembered for, and those 29 lines
|
|
41
|
+
* were never file-level scoping either.
|
|
42
|
+
*
|
|
43
|
+
* The pair is not SMALLER than this file — measured, both routes emit the same
|
|
44
|
+
* rules within 54 bytes. What it buys is a build that scans nothing of another
|
|
45
|
+
* package's source tree, and a `styles-static.css` that finally has a correct
|
|
46
|
+
* use. The measurements, and what would actually shrink a consumer, are in the
|
|
47
|
+
* header of `scripts/generateUtilityStyles.ts`.
|
|
48
|
+
*
|
|
49
|
+
* 🔴 This header is in BOTH of the first two files, because one is generated from
|
|
50
|
+
* the other and a test holds them byte-identical apart from the `@source` line.
|
|
51
|
+
* Always edit `src/styles.css`; never `src/styles-static.css`, which
|
|
33
52
|
* `scripts/generateStaticStyles.ts` rewrites on every `bun run build` and
|
|
34
|
-
* `src/stylesStaticMatches.spec.ts` fails on when it is stale.
|
|
53
|
+
* `src/stylesStaticMatches.spec.ts` fails on when it is stale. Editing this file
|
|
54
|
+
* invalidates `src/styles-utilities.css` too — it is compiled against the static
|
|
55
|
+
* variant, `scripts/generateUtilityStyles.ts` rewrites it in the same build step,
|
|
56
|
+
* and `src/stylesUtilitiesMatches.spec.ts` is what fails when it is stale.
|
|
35
57
|
*/
|
|
36
58
|
|
|
37
59
|
/* Animation utilities (animate-in/out, fade/zoom/slide-in-from-*) consumed by the
|