@hanzo/design 0.5.3 → 0.5.5
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/tokens.gen.d.ts +70 -70
- package/dist/tokens.gen.js +70 -70
- package/package.json +1 -1
- package/scripts/check-tokens.mjs +25 -0
- package/scripts/gen-tokens.mjs +25 -1
- package/scripts/lint.mjs +109 -6
- package/src/tokens.gen.ts +70 -70
package/dist/tokens.gen.d.ts
CHANGED
|
@@ -92,31 +92,31 @@ export declare const colors: {
|
|
|
92
92
|
/** typography tokens (from tokens/typography.css). Values are raw CSS. */
|
|
93
93
|
export declare const typography: {
|
|
94
94
|
readonly 'type-scale': "1";
|
|
95
|
-
readonly 'text-xs': "
|
|
96
|
-
readonly 'leading-xs': "
|
|
97
|
-
readonly 'text-sm': "
|
|
98
|
-
readonly 'leading-sm': "
|
|
99
|
-
readonly 'text-base': "
|
|
100
|
-
readonly 'leading-base': "
|
|
101
|
-
readonly 'text-lg': "
|
|
102
|
-
readonly 'leading-lg': "
|
|
103
|
-
readonly 'text-xl': "
|
|
104
|
-
readonly 'leading-xl': "
|
|
105
|
-
readonly 'text-2xl': "
|
|
106
|
-
readonly 'leading-2xl': "
|
|
107
|
-
readonly 'text-3xl': "
|
|
108
|
-
readonly 'leading-3xl': "
|
|
109
|
-
readonly 'text-4xl': "
|
|
110
|
-
readonly 'leading-4xl': "
|
|
111
|
-
readonly 'text-5xl': "
|
|
95
|
+
readonly 'text-xs': "0.6875rem";
|
|
96
|
+
readonly 'leading-xs': "1rem";
|
|
97
|
+
readonly 'text-sm': "0.8125rem";
|
|
98
|
+
readonly 'leading-sm': "1.15rem";
|
|
99
|
+
readonly 'text-base': "0.875rem";
|
|
100
|
+
readonly 'leading-base': "1.35rem";
|
|
101
|
+
readonly 'text-lg': "0.9375rem";
|
|
102
|
+
readonly 'leading-lg': "1.4rem";
|
|
103
|
+
readonly 'text-xl': "1.0625rem";
|
|
104
|
+
readonly 'leading-xl': "1.55rem";
|
|
105
|
+
readonly 'text-2xl': "1.3125rem";
|
|
106
|
+
readonly 'leading-2xl': "1.7rem";
|
|
107
|
+
readonly 'text-3xl': "1.625rem";
|
|
108
|
+
readonly 'leading-3xl': "1.95rem";
|
|
109
|
+
readonly 'text-4xl': "2rem";
|
|
110
|
+
readonly 'leading-4xl': "2.25rem";
|
|
111
|
+
readonly 'text-5xl': "2.5rem";
|
|
112
112
|
readonly 'leading-5xl': "1.05";
|
|
113
|
-
readonly 'text-6xl': "
|
|
113
|
+
readonly 'text-6xl': "3.25rem";
|
|
114
114
|
readonly 'leading-6xl': "1";
|
|
115
|
-
readonly 'text-7xl': "
|
|
115
|
+
readonly 'text-7xl': "4rem";
|
|
116
116
|
readonly 'leading-7xl': "1";
|
|
117
|
-
readonly 'text-8xl': "
|
|
117
|
+
readonly 'text-8xl': "5.25rem";
|
|
118
118
|
readonly 'leading-8xl': "1";
|
|
119
|
-
readonly 'text-9xl': "
|
|
119
|
+
readonly 'text-9xl': "7rem";
|
|
120
120
|
readonly 'leading-9xl': "1";
|
|
121
121
|
readonly 'font-size-xs': "var(--text-xs)";
|
|
122
122
|
readonly 'font-size-sm': "var(--text-sm)";
|
|
@@ -159,20 +159,20 @@ export declare const typography: {
|
|
|
159
159
|
export declare const spacing: {
|
|
160
160
|
readonly density: "1";
|
|
161
161
|
readonly 'space-0': "0";
|
|
162
|
-
readonly 'space-1': "
|
|
163
|
-
readonly 'space-2': "
|
|
164
|
-
readonly 'space-3': "
|
|
165
|
-
readonly 'space-4': "
|
|
166
|
-
readonly 'space-5': "
|
|
167
|
-
readonly 'space-6': "
|
|
168
|
-
readonly 'space-8': "
|
|
169
|
-
readonly 'space-10': "
|
|
170
|
-
readonly 'space-12': "
|
|
171
|
-
readonly 'space-14': "
|
|
172
|
-
readonly 'space-16': "
|
|
173
|
-
readonly 'space-20': "
|
|
174
|
-
readonly 'space-24': "
|
|
175
|
-
readonly 'space-32': "
|
|
162
|
+
readonly 'space-1': "0.25rem";
|
|
163
|
+
readonly 'space-2': "0.5rem";
|
|
164
|
+
readonly 'space-3': "0.75rem";
|
|
165
|
+
readonly 'space-4': "1rem";
|
|
166
|
+
readonly 'space-5': "1.25rem";
|
|
167
|
+
readonly 'space-6': "1.5rem";
|
|
168
|
+
readonly 'space-8': "2rem";
|
|
169
|
+
readonly 'space-10': "2.5rem";
|
|
170
|
+
readonly 'space-12': "3rem";
|
|
171
|
+
readonly 'space-14': "3.5rem";
|
|
172
|
+
readonly 'space-16': "4rem";
|
|
173
|
+
readonly 'space-20': "5rem";
|
|
174
|
+
readonly 'space-24': "6rem";
|
|
175
|
+
readonly 'space-32': "8rem";
|
|
176
176
|
readonly 'golden-1': "0.25rem";
|
|
177
177
|
readonly 'golden-2': "0.405rem";
|
|
178
178
|
readonly 'golden-3': "0.654rem";
|
|
@@ -384,31 +384,31 @@ export declare const cssVars: {
|
|
|
384
384
|
readonly '--chrome-dot-yellow': "rgb(234 179 8 / .6)";
|
|
385
385
|
readonly '--chrome-dot-green': "rgb(34 197 94 / .6)";
|
|
386
386
|
readonly '--type-scale': "1";
|
|
387
|
-
readonly '--text-xs': "
|
|
388
|
-
readonly '--leading-xs': "
|
|
389
|
-
readonly '--text-sm': "
|
|
390
|
-
readonly '--leading-sm': "
|
|
391
|
-
readonly '--text-base': "
|
|
392
|
-
readonly '--leading-base': "
|
|
393
|
-
readonly '--text-lg': "
|
|
394
|
-
readonly '--leading-lg': "
|
|
395
|
-
readonly '--text-xl': "
|
|
396
|
-
readonly '--leading-xl': "
|
|
397
|
-
readonly '--text-2xl': "
|
|
398
|
-
readonly '--leading-2xl': "
|
|
399
|
-
readonly '--text-3xl': "
|
|
400
|
-
readonly '--leading-3xl': "
|
|
401
|
-
readonly '--text-4xl': "
|
|
402
|
-
readonly '--leading-4xl': "
|
|
403
|
-
readonly '--text-5xl': "
|
|
387
|
+
readonly '--text-xs': "0.6875rem";
|
|
388
|
+
readonly '--leading-xs': "1rem";
|
|
389
|
+
readonly '--text-sm': "0.8125rem";
|
|
390
|
+
readonly '--leading-sm': "1.15rem";
|
|
391
|
+
readonly '--text-base': "0.875rem";
|
|
392
|
+
readonly '--leading-base': "1.35rem";
|
|
393
|
+
readonly '--text-lg': "0.9375rem";
|
|
394
|
+
readonly '--leading-lg': "1.4rem";
|
|
395
|
+
readonly '--text-xl': "1.0625rem";
|
|
396
|
+
readonly '--leading-xl': "1.55rem";
|
|
397
|
+
readonly '--text-2xl': "1.3125rem";
|
|
398
|
+
readonly '--leading-2xl': "1.7rem";
|
|
399
|
+
readonly '--text-3xl': "1.625rem";
|
|
400
|
+
readonly '--leading-3xl': "1.95rem";
|
|
401
|
+
readonly '--text-4xl': "2rem";
|
|
402
|
+
readonly '--leading-4xl': "2.25rem";
|
|
403
|
+
readonly '--text-5xl': "2.5rem";
|
|
404
404
|
readonly '--leading-5xl': "1.05";
|
|
405
|
-
readonly '--text-6xl': "
|
|
405
|
+
readonly '--text-6xl': "3.25rem";
|
|
406
406
|
readonly '--leading-6xl': "1";
|
|
407
|
-
readonly '--text-7xl': "
|
|
407
|
+
readonly '--text-7xl': "4rem";
|
|
408
408
|
readonly '--leading-7xl': "1";
|
|
409
|
-
readonly '--text-8xl': "
|
|
409
|
+
readonly '--text-8xl': "5.25rem";
|
|
410
410
|
readonly '--leading-8xl': "1";
|
|
411
|
-
readonly '--text-9xl': "
|
|
411
|
+
readonly '--text-9xl': "7rem";
|
|
412
412
|
readonly '--leading-9xl': "1";
|
|
413
413
|
readonly '--font-size-xs': "var(--text-xs)";
|
|
414
414
|
readonly '--font-size-sm': "var(--text-sm)";
|
|
@@ -448,20 +448,20 @@ export declare const cssVars: {
|
|
|
448
448
|
readonly '--type-eyebrow': "600 0.625rem/1 var(--font-sans)";
|
|
449
449
|
readonly '--density': "1";
|
|
450
450
|
readonly '--space-0': "0";
|
|
451
|
-
readonly '--space-1': "
|
|
452
|
-
readonly '--space-2': "
|
|
453
|
-
readonly '--space-3': "
|
|
454
|
-
readonly '--space-4': "
|
|
455
|
-
readonly '--space-5': "
|
|
456
|
-
readonly '--space-6': "
|
|
457
|
-
readonly '--space-8': "
|
|
458
|
-
readonly '--space-10': "
|
|
459
|
-
readonly '--space-12': "
|
|
460
|
-
readonly '--space-14': "
|
|
461
|
-
readonly '--space-16': "
|
|
462
|
-
readonly '--space-20': "
|
|
463
|
-
readonly '--space-24': "
|
|
464
|
-
readonly '--space-32': "
|
|
451
|
+
readonly '--space-1': "0.25rem";
|
|
452
|
+
readonly '--space-2': "0.5rem";
|
|
453
|
+
readonly '--space-3': "0.75rem";
|
|
454
|
+
readonly '--space-4': "1rem";
|
|
455
|
+
readonly '--space-5': "1.25rem";
|
|
456
|
+
readonly '--space-6': "1.5rem";
|
|
457
|
+
readonly '--space-8': "2rem";
|
|
458
|
+
readonly '--space-10': "2.5rem";
|
|
459
|
+
readonly '--space-12': "3rem";
|
|
460
|
+
readonly '--space-14': "3.5rem";
|
|
461
|
+
readonly '--space-16': "4rem";
|
|
462
|
+
readonly '--space-20': "5rem";
|
|
463
|
+
readonly '--space-24': "6rem";
|
|
464
|
+
readonly '--space-32': "8rem";
|
|
465
465
|
readonly '--golden-1': "0.25rem";
|
|
466
466
|
readonly '--golden-2': "0.405rem";
|
|
467
467
|
readonly '--golden-3': "0.654rem";
|
package/dist/tokens.gen.js
CHANGED
|
@@ -95,31 +95,31 @@ export const colors = {
|
|
|
95
95
|
/** typography tokens (from tokens/typography.css). Values are raw CSS. */
|
|
96
96
|
export const typography = {
|
|
97
97
|
'type-scale': '1',
|
|
98
|
-
'text-xs': '
|
|
99
|
-
'leading-xs': '
|
|
100
|
-
'text-sm': '
|
|
101
|
-
'leading-sm': '
|
|
102
|
-
'text-base': '
|
|
103
|
-
'leading-base': '
|
|
104
|
-
'text-lg': '
|
|
105
|
-
'leading-lg': '
|
|
106
|
-
'text-xl': '
|
|
107
|
-
'leading-xl': '
|
|
108
|
-
'text-2xl': '
|
|
109
|
-
'leading-2xl': '
|
|
110
|
-
'text-3xl': '
|
|
111
|
-
'leading-3xl': '
|
|
112
|
-
'text-4xl': '
|
|
113
|
-
'leading-4xl': '
|
|
114
|
-
'text-5xl': '
|
|
98
|
+
'text-xs': '0.6875rem',
|
|
99
|
+
'leading-xs': '1rem',
|
|
100
|
+
'text-sm': '0.8125rem',
|
|
101
|
+
'leading-sm': '1.15rem',
|
|
102
|
+
'text-base': '0.875rem',
|
|
103
|
+
'leading-base': '1.35rem',
|
|
104
|
+
'text-lg': '0.9375rem',
|
|
105
|
+
'leading-lg': '1.4rem',
|
|
106
|
+
'text-xl': '1.0625rem',
|
|
107
|
+
'leading-xl': '1.55rem',
|
|
108
|
+
'text-2xl': '1.3125rem',
|
|
109
|
+
'leading-2xl': '1.7rem',
|
|
110
|
+
'text-3xl': '1.625rem',
|
|
111
|
+
'leading-3xl': '1.95rem',
|
|
112
|
+
'text-4xl': '2rem',
|
|
113
|
+
'leading-4xl': '2.25rem',
|
|
114
|
+
'text-5xl': '2.5rem',
|
|
115
115
|
'leading-5xl': '1.05',
|
|
116
|
-
'text-6xl': '
|
|
116
|
+
'text-6xl': '3.25rem',
|
|
117
117
|
'leading-6xl': '1',
|
|
118
|
-
'text-7xl': '
|
|
118
|
+
'text-7xl': '4rem',
|
|
119
119
|
'leading-7xl': '1',
|
|
120
|
-
'text-8xl': '
|
|
120
|
+
'text-8xl': '5.25rem',
|
|
121
121
|
'leading-8xl': '1',
|
|
122
|
-
'text-9xl': '
|
|
122
|
+
'text-9xl': '7rem',
|
|
123
123
|
'leading-9xl': '1',
|
|
124
124
|
'font-size-xs': 'var(--text-xs)',
|
|
125
125
|
'font-size-sm': 'var(--text-sm)',
|
|
@@ -162,20 +162,20 @@ export const typography = {
|
|
|
162
162
|
export const spacing = {
|
|
163
163
|
'density': '1',
|
|
164
164
|
'space-0': '0',
|
|
165
|
-
'space-1': '
|
|
166
|
-
'space-2': '
|
|
167
|
-
'space-3': '
|
|
168
|
-
'space-4': '
|
|
169
|
-
'space-5': '
|
|
170
|
-
'space-6': '
|
|
171
|
-
'space-8': '
|
|
172
|
-
'space-10': '
|
|
173
|
-
'space-12': '
|
|
174
|
-
'space-14': '
|
|
175
|
-
'space-16': '
|
|
176
|
-
'space-20': '
|
|
177
|
-
'space-24': '
|
|
178
|
-
'space-32': '
|
|
165
|
+
'space-1': '0.25rem',
|
|
166
|
+
'space-2': '0.5rem',
|
|
167
|
+
'space-3': '0.75rem',
|
|
168
|
+
'space-4': '1rem',
|
|
169
|
+
'space-5': '1.25rem',
|
|
170
|
+
'space-6': '1.5rem',
|
|
171
|
+
'space-8': '2rem',
|
|
172
|
+
'space-10': '2.5rem',
|
|
173
|
+
'space-12': '3rem',
|
|
174
|
+
'space-14': '3.5rem',
|
|
175
|
+
'space-16': '4rem',
|
|
176
|
+
'space-20': '5rem',
|
|
177
|
+
'space-24': '6rem',
|
|
178
|
+
'space-32': '8rem',
|
|
179
179
|
'golden-1': '0.25rem',
|
|
180
180
|
'golden-2': '0.405rem',
|
|
181
181
|
'golden-3': '0.654rem',
|
|
@@ -387,31 +387,31 @@ export const cssVars = {
|
|
|
387
387
|
'--chrome-dot-yellow': 'rgb(234 179 8 / .6)',
|
|
388
388
|
'--chrome-dot-green': 'rgb(34 197 94 / .6)',
|
|
389
389
|
'--type-scale': '1',
|
|
390
|
-
'--text-xs': '
|
|
391
|
-
'--leading-xs': '
|
|
392
|
-
'--text-sm': '
|
|
393
|
-
'--leading-sm': '
|
|
394
|
-
'--text-base': '
|
|
395
|
-
'--leading-base': '
|
|
396
|
-
'--text-lg': '
|
|
397
|
-
'--leading-lg': '
|
|
398
|
-
'--text-xl': '
|
|
399
|
-
'--leading-xl': '
|
|
400
|
-
'--text-2xl': '
|
|
401
|
-
'--leading-2xl': '
|
|
402
|
-
'--text-3xl': '
|
|
403
|
-
'--leading-3xl': '
|
|
404
|
-
'--text-4xl': '
|
|
405
|
-
'--leading-4xl': '
|
|
406
|
-
'--text-5xl': '
|
|
390
|
+
'--text-xs': '0.6875rem',
|
|
391
|
+
'--leading-xs': '1rem',
|
|
392
|
+
'--text-sm': '0.8125rem',
|
|
393
|
+
'--leading-sm': '1.15rem',
|
|
394
|
+
'--text-base': '0.875rem',
|
|
395
|
+
'--leading-base': '1.35rem',
|
|
396
|
+
'--text-lg': '0.9375rem',
|
|
397
|
+
'--leading-lg': '1.4rem',
|
|
398
|
+
'--text-xl': '1.0625rem',
|
|
399
|
+
'--leading-xl': '1.55rem',
|
|
400
|
+
'--text-2xl': '1.3125rem',
|
|
401
|
+
'--leading-2xl': '1.7rem',
|
|
402
|
+
'--text-3xl': '1.625rem',
|
|
403
|
+
'--leading-3xl': '1.95rem',
|
|
404
|
+
'--text-4xl': '2rem',
|
|
405
|
+
'--leading-4xl': '2.25rem',
|
|
406
|
+
'--text-5xl': '2.5rem',
|
|
407
407
|
'--leading-5xl': '1.05',
|
|
408
|
-
'--text-6xl': '
|
|
408
|
+
'--text-6xl': '3.25rem',
|
|
409
409
|
'--leading-6xl': '1',
|
|
410
|
-
'--text-7xl': '
|
|
410
|
+
'--text-7xl': '4rem',
|
|
411
411
|
'--leading-7xl': '1',
|
|
412
|
-
'--text-8xl': '
|
|
412
|
+
'--text-8xl': '5.25rem',
|
|
413
413
|
'--leading-8xl': '1',
|
|
414
|
-
'--text-9xl': '
|
|
414
|
+
'--text-9xl': '7rem',
|
|
415
415
|
'--leading-9xl': '1',
|
|
416
416
|
'--font-size-xs': 'var(--text-xs)',
|
|
417
417
|
'--font-size-sm': 'var(--text-sm)',
|
|
@@ -451,20 +451,20 @@ export const cssVars = {
|
|
|
451
451
|
'--type-eyebrow': '600 0.625rem/1 var(--font-sans)',
|
|
452
452
|
'--density': '1',
|
|
453
453
|
'--space-0': '0',
|
|
454
|
-
'--space-1': '
|
|
455
|
-
'--space-2': '
|
|
456
|
-
'--space-3': '
|
|
457
|
-
'--space-4': '
|
|
458
|
-
'--space-5': '
|
|
459
|
-
'--space-6': '
|
|
460
|
-
'--space-8': '
|
|
461
|
-
'--space-10': '
|
|
462
|
-
'--space-12': '
|
|
463
|
-
'--space-14': '
|
|
464
|
-
'--space-16': '
|
|
465
|
-
'--space-20': '
|
|
466
|
-
'--space-24': '
|
|
467
|
-
'--space-32': '
|
|
454
|
+
'--space-1': '0.25rem',
|
|
455
|
+
'--space-2': '0.5rem',
|
|
456
|
+
'--space-3': '0.75rem',
|
|
457
|
+
'--space-4': '1rem',
|
|
458
|
+
'--space-5': '1.25rem',
|
|
459
|
+
'--space-6': '1.5rem',
|
|
460
|
+
'--space-8': '2rem',
|
|
461
|
+
'--space-10': '2.5rem',
|
|
462
|
+
'--space-12': '3rem',
|
|
463
|
+
'--space-14': '3.5rem',
|
|
464
|
+
'--space-16': '4rem',
|
|
465
|
+
'--space-20': '5rem',
|
|
466
|
+
'--space-24': '6rem',
|
|
467
|
+
'--space-32': '8rem',
|
|
468
468
|
'--golden-1': '0.25rem',
|
|
469
469
|
'--golden-2': '0.405rem',
|
|
470
470
|
'--golden-3': '0.654rem',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzo/design",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.5",
|
|
4
4
|
"packageManager": "pnpm@11.17.0",
|
|
5
5
|
"description": "Hanzo Design System \u2014 monochrome, dark-default tokens + components + brand assets, the single source of truth for every Hanzo surface. CSS + typed programmatic tokens.",
|
|
6
6
|
"license": "MIT OR Apache-2.0",
|
package/scripts/check-tokens.mjs
CHANGED
|
@@ -328,5 +328,30 @@ const pass = (msg) => console.log(` ok ${msg}`)
|
|
|
328
328
|
: pass('one light direction — every shadow in elevation.css drops straight down')
|
|
329
329
|
}
|
|
330
330
|
|
|
331
|
+
// ── 6. the TS table is arithmetic, not CSS ───────────────────────────────
|
|
332
|
+
// `--text-xs` is `calc(0.6875rem * var(--type-scale, 1))` in the sheet, which is
|
|
333
|
+
// how one property retunes the whole ramp. Emitting that string into the
|
|
334
|
+
// programmatic table too shipped a value no JavaScript can use: a consumer
|
|
335
|
+
// computing with it gets NaN, silently, and a consumer writing it into an inline
|
|
336
|
+
// style outranks every stylesheet.
|
|
337
|
+
//
|
|
338
|
+
// It happened. hanzo.ai's accuracy-at-cost scatter sizes SVG text by
|
|
339
|
+
// `parseFloat(typography['text-xs']) * 16`; against 0.5.x every label measured NaN,
|
|
340
|
+
// the placement pass could not seat a single one, and the chart gate went red —
|
|
341
|
+
// which, because the gates run in the publishing job, froze the whole SITE.
|
|
342
|
+
//
|
|
343
|
+
// So the generator unwraps a knob (`scale()` in gen-tokens.mjs) and this holds it:
|
|
344
|
+
// the knob belongs to the cascade, the value belongs to the table, and neither may
|
|
345
|
+
// take the other's job. styles.css is generated from the CSS directly, so the
|
|
346
|
+
// runtime knob is untouched — this only governs what JavaScript is handed.
|
|
347
|
+
{
|
|
348
|
+
const gen = read(join(root, 'src', 'tokens.gen.ts'))
|
|
349
|
+
const offenders = [...gen.matchAll(/'([^']+)': '([^']*var\(--(?:type-scale|density)[^']*)'/g)]
|
|
350
|
+
offenders.length
|
|
351
|
+
? offenders.forEach(([, n, v]) =>
|
|
352
|
+
fail(`${n} is emitted as \`${v}\` — a knob cannot be resolved in JS; the table carries the value`))
|
|
353
|
+
: pass('the generated table carries values, not knobs — every length parses as a number')
|
|
354
|
+
}
|
|
355
|
+
|
|
331
356
|
console.log(failures ? `\n${failures} check(s) failed` : '\nall token checks passed')
|
|
332
357
|
process.exit(failures ? 1 : 0)
|
package/scripts/gen-tokens.mjs
CHANGED
|
@@ -33,6 +33,29 @@ function parse(css) {
|
|
|
33
33
|
// light `@media`/`[data-theme]` override carry the SAME name. Keep the FIRST
|
|
34
34
|
// occurrence (dark, the default theme): the static map is the authored default;
|
|
35
35
|
// live theme switching happens through the CSS cascade via cssVar()/var(--x).
|
|
36
|
+
/**
|
|
37
|
+
* A knob is CSS's, so the TS table publishes the VALUE and leaves the knob behind.
|
|
38
|
+
*
|
|
39
|
+
* `--text-xs` and `--space-1` are authored as `calc(0.6875rem * var(--type-scale, 1))`
|
|
40
|
+
* so one property retunes the whole ramp at runtime. That is right in a stylesheet
|
|
41
|
+
* and unusable in JavaScript twice over: nothing in JS can resolve a `var()`, so a
|
|
42
|
+
* consumer computing with the value gets NaN, and a consumer writing it into an
|
|
43
|
+
* inline style outranks every stylesheet — which is the one thing this package
|
|
44
|
+
* spends its whole cascade avoiding.
|
|
45
|
+
*
|
|
46
|
+
* So the two facts braided into that one string are separated here. The value is
|
|
47
|
+
* the ramp; the knob is the cascade's, and `styles.css` is generated straight from
|
|
48
|
+
* the CSS, so it keeps the calc and the runtime knob still works. `cssVar()`'s
|
|
49
|
+
* fallback gets the literal too, which is what a host with no token layer mounted
|
|
50
|
+
* actually wants — there is no `--type-scale` there either.
|
|
51
|
+
*
|
|
52
|
+
* The shape is exact (`calc(<value> * var(--<knob>, 1))`) and only that shape is
|
|
53
|
+
* unwrapped; a genuinely composite calc (`calc(var(--fold) - 1px)`) is untouched.
|
|
54
|
+
* check-tokens.mjs holds it: no emitted value may name a knob.
|
|
55
|
+
*/
|
|
56
|
+
const KNOB = /^calc\(\s*([^*]+?)\s*\*\s*var\(--(?:type-scale|density),\s*1\)\s*\)$/
|
|
57
|
+
const scale = (v) => v.match(KNOB)?.[1] ?? v
|
|
58
|
+
|
|
36
59
|
const groupMaps = {} // groupName -> Map(name->value), first-wins
|
|
37
60
|
const flatMap = new Map() // '--name' -> value, first-wins
|
|
38
61
|
for (const f of FILES) {
|
|
@@ -44,7 +67,8 @@ for (const f of FILES) {
|
|
|
44
67
|
}
|
|
45
68
|
const g = GROUP[f] ?? f
|
|
46
69
|
const gm = (groupMaps[g] ??= new Map())
|
|
47
|
-
for (const [n,
|
|
70
|
+
for (const [n, v0] of parse(css)) {
|
|
71
|
+
const v = scale(v0)
|
|
48
72
|
if (!gm.has(n)) gm.set(n, v)
|
|
49
73
|
if (!flatMap.has(`--${n}`)) flatMap.set(`--${n}`, v)
|
|
50
74
|
}
|
package/scripts/lint.mjs
CHANGED
|
@@ -194,6 +194,64 @@ function lintFile(abs, root) {
|
|
|
194
194
|
for (const m of src.matchAll(/['"]@hanzoai\/design/g))
|
|
195
195
|
flag(rel, lineOf(src, m.index), 'wrong-package', '@hanzoai/design',
|
|
196
196
|
'the package is @hanzo/design — @hanzoai/design is a 404 and resolves nothing')
|
|
197
|
+
|
|
198
|
+
// ── the four layers ────────────────────────────────────────────────────
|
|
199
|
+
// Rules 1-8 catch a surface writing a VALUE it should have named. These four
|
|
200
|
+
// catch a surface OWNING something it should have imported, which is the
|
|
201
|
+
// failure that costs a fleet its coherence rather than a component its
|
|
202
|
+
// colour. The estate has exactly four layers and every surface is meant to
|
|
203
|
+
// be pure composition over them:
|
|
204
|
+
//
|
|
205
|
+
// values -> @hanzo/design tokens, the ramp, the ladder
|
|
206
|
+
// structure -> @hanzo/gui the primitives things are built from
|
|
207
|
+
// chrome -> @hanzogui/shell header, search, nav, launcher, footer
|
|
208
|
+
// components -> @hanzo/ui everything assembled from the above
|
|
209
|
+
//
|
|
210
|
+
// A surface holds data and layout. Nothing else. Each rule below is one of
|
|
211
|
+
// those four layers being re-implemented locally, and each has shipped:
|
|
212
|
+
// hanzo.ai carried a 244-line token table that disagreed with the canon on
|
|
213
|
+
// nearly every rung; hanzo.app loaded @hanzo/brand's sheet ahead of the one
|
|
214
|
+
// that owns the same names; both drew their own search control.
|
|
215
|
+
|
|
216
|
+
// 9. ONE table binds the ramp. `createGui`/`createTokens`/`createFont` is
|
|
217
|
+
// that binding, it lives in @hanzo/ui, and a second one is not a second
|
|
218
|
+
// opinion — every @hanzo/ui component asks the HOST's table for its
|
|
219
|
+
// sizes, so one library renders at two sizes depending on the site.
|
|
220
|
+
for (const m of src.matchAll(/\b(createGui|createTokens|createFont|createThemes)\s*\(/g))
|
|
221
|
+
flag(rel, lineOf(src, m.index), 'local-token-table', `${m[1]}()`,
|
|
222
|
+
"import the fleet's table — `export { config as default } from '@hanzo/ui/gui-config'`")
|
|
223
|
+
|
|
224
|
+
// 10. ONE publisher per token name. A surface that declares a name design
|
|
225
|
+
// already declares has entered a fight decided by LOAD ORDER, which is
|
|
226
|
+
// not a decision anyone made. (Declaring your own new name is fine —
|
|
227
|
+
// that is what a surface-specific token IS. This is only collision.)
|
|
228
|
+
//
|
|
229
|
+
// A DECLARATION, never a mention. In a stylesheet that is `--x:` at the
|
|
230
|
+
// top of a declaration; in JS it is a QUOTED key, which is the only way a
|
|
231
|
+
// custom property can be set from there. Matching the bare name in JS
|
|
232
|
+
// read every comment explaining WHICH token to use as a redeclaration of
|
|
233
|
+
// it — this package's own Button and Card, whose comments are the
|
|
234
|
+
// reasoning for reaching one rung over another.
|
|
235
|
+
for (const m of src.matchAll(isStyle ? /(?:^|[\s;{])--([A-Za-z0-9-]+)\s*:/g : /['"]--([A-Za-z0-9-]+)['"]\s*:/g))
|
|
236
|
+
if (TOKENS.has(m[1]))
|
|
237
|
+
flag(rel, lineOf(src, m.index), 'redeclared-token', `--${m[1]}`,
|
|
238
|
+
'the name is @hanzo/design\'s — read it with var(), or pick a name of your own')
|
|
239
|
+
|
|
240
|
+
// 11. a SECOND sheet of the same tokens. Two publishers is how a ramp drifts:
|
|
241
|
+
// the loser is silent, and which one loses depends on import order.
|
|
242
|
+
for (const m of src.matchAll(
|
|
243
|
+
/from\s+['"]([^'"]*(?:@hanzo\/brand\/styles|@hanzogui\/themes|tailwindcss\/theme|cdn\.tailwindcss\.com|fonts\.googleapis)[^'"]*)['"]|import\s+['"]([^'"]*(?:@hanzo\/brand\/styles|cdn\.tailwindcss\.com|fonts\.googleapis)[^'"]*)['"]/g))
|
|
244
|
+
flag(rel, lineOf(src, m.index), 'second-publisher', m[1] ?? m[2],
|
|
245
|
+
'@hanzo/design is the token layer; @hanzo/ui/theme.css or /glass.css carries the material')
|
|
246
|
+
|
|
247
|
+
// 12. the chrome is the shell's. A header, a search control, a product menu,
|
|
248
|
+
// an org switcher, an app launcher and a footer are ONE set of controls
|
|
249
|
+
// across every Hanzo surface — that is what makes them recognisable as
|
|
250
|
+
// one product. A local copy is a control that drifts by a release.
|
|
251
|
+
for (const m of src.matchAll(
|
|
252
|
+
/\b(?:function|const)\s+(Hanzo?(?:Header|Footer)|(?:Site|App|Global|Main|Top)(?:Header|Nav|Footer|Bar)|(?:Header|Nav|Command|Search)(?:Search|Palette|Trigger|Bar)|OrgSwitcher|OrgHeader|AppLauncher|MegaMenu)\b/g))
|
|
253
|
+
flag(rel, lineOf(src, m.index), 'shell-owned-chrome', m[1],
|
|
254
|
+
'import it from @hanzogui/shell — the chrome is one set of controls, fleet-wide')
|
|
197
255
|
}
|
|
198
256
|
|
|
199
257
|
// ── run ──────────────────────────────────────────────────────────────────
|
|
@@ -202,17 +260,62 @@ const roots = targets.length ? targets : [process.cwd()]
|
|
|
202
260
|
let scanned = 0
|
|
203
261
|
for (const r of roots) for (const f of walk(r)) { scanned++; lintFile(f, r === f ? dirname(r) : r) }
|
|
204
262
|
|
|
205
|
-
const RULES = [
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
263
|
+
const RULES = [
|
|
264
|
+
'unresolved-token', 'raw-color', 'raw-z-index', 'raw-font-size', 'all-caps',
|
|
265
|
+
'inline-style', 'icon-set', 'wrong-package',
|
|
266
|
+
'local-token-table', 'redeclared-token', 'second-publisher', 'shell-owned-chrome',
|
|
267
|
+
]
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* THE RATCHET — `hanzo-design.allow.json`, beside the code being linted.
|
|
271
|
+
*
|
|
272
|
+
* A gate that can only pass on a perfect tree cannot be turned on, and a gate
|
|
273
|
+
* nobody turns on prevents nothing. So a surface declares what it currently
|
|
274
|
+
* owes, per rule, and the number MAY ONLY SHRINK. Every new violation fails the
|
|
275
|
+
* build on the day it is written; the backlog burns down on its own schedule.
|
|
276
|
+
*
|
|
277
|
+
* { "raw-color": 412, "redeclared-token": 6 }
|
|
278
|
+
*
|
|
279
|
+
* A count that comes in UNDER its allowance fails too, and that is the ratchet
|
|
280
|
+
* rather than a threshold: the fix has to delete its own exemption in the same
|
|
281
|
+
* commit, or the next author inherits room to regress into. The same discipline
|
|
282
|
+
* hanzo.ai's `audit-catalog.mjs` holds its KNOWN_UNSERVED list to.
|
|
283
|
+
*
|
|
284
|
+
* A rule with no entry is allowed ZERO, so a new rule is live everywhere the
|
|
285
|
+
* day it ships and no surface has to opt in.
|
|
286
|
+
*
|
|
287
|
+
* It is read from the WORKING DIRECTORY, not from the paths being linted. Every
|
|
288
|
+
* real invocation is a package script (`hanzo-design-lint components app`), so
|
|
289
|
+
* the cwd is the surface's root — one place, whatever subtrees are named, and
|
|
290
|
+
* nothing to derive from a list of arguments that may be files or directories.
|
|
291
|
+
*/
|
|
292
|
+
const RATCHET = 'hanzo-design.allow.json'
|
|
293
|
+
let allow = {}
|
|
294
|
+
try { allow = JSON.parse(readFileSync(join(process.cwd(), RATCHET), 'utf8')) } catch { /* none: everything is zero */ }
|
|
295
|
+
|
|
296
|
+
const count = Object.fromEntries(RULES.map((r) => [r, findings.filter((f) => f.rule === r).length]))
|
|
297
|
+
const over = RULES.filter((r) => count[r] > (allow[r] ?? 0))
|
|
298
|
+
const under = RULES.filter((r) => count[r] < (allow[r] ?? 0))
|
|
299
|
+
|
|
210
300
|
for (const rule of RULES) {
|
|
211
301
|
const hits = findings.filter((f) => f.rule === rule)
|
|
212
302
|
if (!hits.length) continue
|
|
213
|
-
|
|
303
|
+
const owed = allow[rule] ?? 0
|
|
304
|
+
const verdict = count[rule] > owed ? `NEW — allowed ${owed}` : `at the allowance (${owed})`
|
|
305
|
+
console.log(`\n${rule} (${hits.length}, ${verdict}) — ${hits[0].fix}`)
|
|
214
306
|
for (const h of hits.slice(0, 20)) console.log(` ${h.file}:${h.line} ${h.detail}`)
|
|
215
307
|
if (hits.length > 20) console.log(` … ${hits.length - 20} more`)
|
|
216
308
|
}
|
|
309
|
+
|
|
310
|
+
if (!over.length && !under.length) {
|
|
311
|
+
const owed = Object.values(allow).reduce((a, b) => a + b, 0)
|
|
312
|
+
console.log(`hanzo-design-lint: ${scanned} files, clean${owed ? ` (${owed} allowed by ${RATCHET})` : ''}`)
|
|
313
|
+
process.exit(0)
|
|
314
|
+
}
|
|
315
|
+
for (const r of over)
|
|
316
|
+
console.log(`\n ${r}: ${count[r]} found, ${allow[r] ?? 0} allowed — a NEW violation. Fix it.`)
|
|
317
|
+
for (const r of under)
|
|
318
|
+
console.log(`\n ${r}: ${count[r]} found, ${allow[r] ?? 0} allowed — you fixed ${(allow[r] ?? 0) - count[r]}. ` +
|
|
319
|
+
`Lower it in ${RATCHET}; the allowance only shrinks.`)
|
|
217
320
|
console.log(`\n${findings.length} violation(s) across ${scanned} files`)
|
|
218
321
|
process.exit(1)
|
package/src/tokens.gen.ts
CHANGED
|
@@ -97,31 +97,31 @@ export const colors = {
|
|
|
97
97
|
/** typography tokens (from tokens/typography.css). Values are raw CSS. */
|
|
98
98
|
export const typography = {
|
|
99
99
|
'type-scale': '1',
|
|
100
|
-
'text-xs': '
|
|
101
|
-
'leading-xs': '
|
|
102
|
-
'text-sm': '
|
|
103
|
-
'leading-sm': '
|
|
104
|
-
'text-base': '
|
|
105
|
-
'leading-base': '
|
|
106
|
-
'text-lg': '
|
|
107
|
-
'leading-lg': '
|
|
108
|
-
'text-xl': '
|
|
109
|
-
'leading-xl': '
|
|
110
|
-
'text-2xl': '
|
|
111
|
-
'leading-2xl': '
|
|
112
|
-
'text-3xl': '
|
|
113
|
-
'leading-3xl': '
|
|
114
|
-
'text-4xl': '
|
|
115
|
-
'leading-4xl': '
|
|
116
|
-
'text-5xl': '
|
|
100
|
+
'text-xs': '0.6875rem',
|
|
101
|
+
'leading-xs': '1rem',
|
|
102
|
+
'text-sm': '0.8125rem',
|
|
103
|
+
'leading-sm': '1.15rem',
|
|
104
|
+
'text-base': '0.875rem',
|
|
105
|
+
'leading-base': '1.35rem',
|
|
106
|
+
'text-lg': '0.9375rem',
|
|
107
|
+
'leading-lg': '1.4rem',
|
|
108
|
+
'text-xl': '1.0625rem',
|
|
109
|
+
'leading-xl': '1.55rem',
|
|
110
|
+
'text-2xl': '1.3125rem',
|
|
111
|
+
'leading-2xl': '1.7rem',
|
|
112
|
+
'text-3xl': '1.625rem',
|
|
113
|
+
'leading-3xl': '1.95rem',
|
|
114
|
+
'text-4xl': '2rem',
|
|
115
|
+
'leading-4xl': '2.25rem',
|
|
116
|
+
'text-5xl': '2.5rem',
|
|
117
117
|
'leading-5xl': '1.05',
|
|
118
|
-
'text-6xl': '
|
|
118
|
+
'text-6xl': '3.25rem',
|
|
119
119
|
'leading-6xl': '1',
|
|
120
|
-
'text-7xl': '
|
|
120
|
+
'text-7xl': '4rem',
|
|
121
121
|
'leading-7xl': '1',
|
|
122
|
-
'text-8xl': '
|
|
122
|
+
'text-8xl': '5.25rem',
|
|
123
123
|
'leading-8xl': '1',
|
|
124
|
-
'text-9xl': '
|
|
124
|
+
'text-9xl': '7rem',
|
|
125
125
|
'leading-9xl': '1',
|
|
126
126
|
'font-size-xs': 'var(--text-xs)',
|
|
127
127
|
'font-size-sm': 'var(--text-sm)',
|
|
@@ -165,20 +165,20 @@ export const typography = {
|
|
|
165
165
|
export const spacing = {
|
|
166
166
|
'density': '1',
|
|
167
167
|
'space-0': '0',
|
|
168
|
-
'space-1': '
|
|
169
|
-
'space-2': '
|
|
170
|
-
'space-3': '
|
|
171
|
-
'space-4': '
|
|
172
|
-
'space-5': '
|
|
173
|
-
'space-6': '
|
|
174
|
-
'space-8': '
|
|
175
|
-
'space-10': '
|
|
176
|
-
'space-12': '
|
|
177
|
-
'space-14': '
|
|
178
|
-
'space-16': '
|
|
179
|
-
'space-20': '
|
|
180
|
-
'space-24': '
|
|
181
|
-
'space-32': '
|
|
168
|
+
'space-1': '0.25rem',
|
|
169
|
+
'space-2': '0.5rem',
|
|
170
|
+
'space-3': '0.75rem',
|
|
171
|
+
'space-4': '1rem',
|
|
172
|
+
'space-5': '1.25rem',
|
|
173
|
+
'space-6': '1.5rem',
|
|
174
|
+
'space-8': '2rem',
|
|
175
|
+
'space-10': '2.5rem',
|
|
176
|
+
'space-12': '3rem',
|
|
177
|
+
'space-14': '3.5rem',
|
|
178
|
+
'space-16': '4rem',
|
|
179
|
+
'space-20': '5rem',
|
|
180
|
+
'space-24': '6rem',
|
|
181
|
+
'space-32': '8rem',
|
|
182
182
|
'golden-1': '0.25rem',
|
|
183
183
|
'golden-2': '0.405rem',
|
|
184
184
|
'golden-3': '0.654rem',
|
|
@@ -400,31 +400,31 @@ export const cssVars = {
|
|
|
400
400
|
'--chrome-dot-yellow': 'rgb(234 179 8 / .6)',
|
|
401
401
|
'--chrome-dot-green': 'rgb(34 197 94 / .6)',
|
|
402
402
|
'--type-scale': '1',
|
|
403
|
-
'--text-xs': '
|
|
404
|
-
'--leading-xs': '
|
|
405
|
-
'--text-sm': '
|
|
406
|
-
'--leading-sm': '
|
|
407
|
-
'--text-base': '
|
|
408
|
-
'--leading-base': '
|
|
409
|
-
'--text-lg': '
|
|
410
|
-
'--leading-lg': '
|
|
411
|
-
'--text-xl': '
|
|
412
|
-
'--leading-xl': '
|
|
413
|
-
'--text-2xl': '
|
|
414
|
-
'--leading-2xl': '
|
|
415
|
-
'--text-3xl': '
|
|
416
|
-
'--leading-3xl': '
|
|
417
|
-
'--text-4xl': '
|
|
418
|
-
'--leading-4xl': '
|
|
419
|
-
'--text-5xl': '
|
|
403
|
+
'--text-xs': '0.6875rem',
|
|
404
|
+
'--leading-xs': '1rem',
|
|
405
|
+
'--text-sm': '0.8125rem',
|
|
406
|
+
'--leading-sm': '1.15rem',
|
|
407
|
+
'--text-base': '0.875rem',
|
|
408
|
+
'--leading-base': '1.35rem',
|
|
409
|
+
'--text-lg': '0.9375rem',
|
|
410
|
+
'--leading-lg': '1.4rem',
|
|
411
|
+
'--text-xl': '1.0625rem',
|
|
412
|
+
'--leading-xl': '1.55rem',
|
|
413
|
+
'--text-2xl': '1.3125rem',
|
|
414
|
+
'--leading-2xl': '1.7rem',
|
|
415
|
+
'--text-3xl': '1.625rem',
|
|
416
|
+
'--leading-3xl': '1.95rem',
|
|
417
|
+
'--text-4xl': '2rem',
|
|
418
|
+
'--leading-4xl': '2.25rem',
|
|
419
|
+
'--text-5xl': '2.5rem',
|
|
420
420
|
'--leading-5xl': '1.05',
|
|
421
|
-
'--text-6xl': '
|
|
421
|
+
'--text-6xl': '3.25rem',
|
|
422
422
|
'--leading-6xl': '1',
|
|
423
|
-
'--text-7xl': '
|
|
423
|
+
'--text-7xl': '4rem',
|
|
424
424
|
'--leading-7xl': '1',
|
|
425
|
-
'--text-8xl': '
|
|
425
|
+
'--text-8xl': '5.25rem',
|
|
426
426
|
'--leading-8xl': '1',
|
|
427
|
-
'--text-9xl': '
|
|
427
|
+
'--text-9xl': '7rem',
|
|
428
428
|
'--leading-9xl': '1',
|
|
429
429
|
'--font-size-xs': 'var(--text-xs)',
|
|
430
430
|
'--font-size-sm': 'var(--text-sm)',
|
|
@@ -464,20 +464,20 @@ export const cssVars = {
|
|
|
464
464
|
'--type-eyebrow': '600 0.625rem/1 var(--font-sans)',
|
|
465
465
|
'--density': '1',
|
|
466
466
|
'--space-0': '0',
|
|
467
|
-
'--space-1': '
|
|
468
|
-
'--space-2': '
|
|
469
|
-
'--space-3': '
|
|
470
|
-
'--space-4': '
|
|
471
|
-
'--space-5': '
|
|
472
|
-
'--space-6': '
|
|
473
|
-
'--space-8': '
|
|
474
|
-
'--space-10': '
|
|
475
|
-
'--space-12': '
|
|
476
|
-
'--space-14': '
|
|
477
|
-
'--space-16': '
|
|
478
|
-
'--space-20': '
|
|
479
|
-
'--space-24': '
|
|
480
|
-
'--space-32': '
|
|
467
|
+
'--space-1': '0.25rem',
|
|
468
|
+
'--space-2': '0.5rem',
|
|
469
|
+
'--space-3': '0.75rem',
|
|
470
|
+
'--space-4': '1rem',
|
|
471
|
+
'--space-5': '1.25rem',
|
|
472
|
+
'--space-6': '1.5rem',
|
|
473
|
+
'--space-8': '2rem',
|
|
474
|
+
'--space-10': '2.5rem',
|
|
475
|
+
'--space-12': '3rem',
|
|
476
|
+
'--space-14': '3.5rem',
|
|
477
|
+
'--space-16': '4rem',
|
|
478
|
+
'--space-20': '5rem',
|
|
479
|
+
'--space-24': '6rem',
|
|
480
|
+
'--space-32': '8rem',
|
|
481
481
|
'--golden-1': '0.25rem',
|
|
482
482
|
'--golden-2': '0.405rem',
|
|
483
483
|
'--golden-3': '0.654rem',
|