@hanzo/design 0.5.11 → 0.5.12
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/preference.d.ts +30 -0
- package/dist/preference.d.ts.map +1 -1
- package/dist/preference.js +57 -0
- package/dist/tokens.gen.d.ts +4 -0
- package/dist/tokens.gen.d.ts.map +1 -1
- package/dist/tokens.gen.js +4 -0
- package/package.json +1 -1
- package/scripts/check-preference.mjs +45 -0
- package/scripts/gen-tokens.mjs +14 -2
- package/src/preference.ts +65 -0
- package/src/tokens.gen.ts +4 -0
- package/styles.css +30 -18
- package/tailwind.css +30 -18
- package/tokens/typography.css +30 -18
package/dist/preference.d.ts
CHANGED
|
@@ -42,6 +42,11 @@ export interface Preference {
|
|
|
42
42
|
* above 1 opens the display end and tightens the small one.
|
|
43
43
|
*/
|
|
44
44
|
ratio?: number;
|
|
45
|
+
/**
|
|
46
|
+
* A modular scale for the display rungs — 1.618 for golden, 1.25 for a major
|
|
47
|
+
* third. Absent means the authored ramp, which is the tuned default.
|
|
48
|
+
*/
|
|
49
|
+
modular?: number;
|
|
45
50
|
density?: Density;
|
|
46
51
|
/** Which face the page is set in. */
|
|
47
52
|
font?: Face;
|
|
@@ -74,6 +79,31 @@ export declare const TYPE_MAX = 1.4;
|
|
|
74
79
|
*/
|
|
75
80
|
export declare const RATIO_MIN = 0.75;
|
|
76
81
|
export declare const RATIO_MAX = 1.5;
|
|
82
|
+
/**
|
|
83
|
+
* A MODULAR scale — the classical one, where each display rung is the one below
|
|
84
|
+
* it times a fixed ratio. Golden is 1.618; the musical intervals designers name
|
|
85
|
+
* are 1.2 (minor third), 1.25 (major third), 1.333 (perfect fourth) and 1.5
|
|
86
|
+
* (perfect fifth).
|
|
87
|
+
*
|
|
88
|
+
* This is a different RULE from `ratio`, not another dial on it. `ratio` tunes
|
|
89
|
+
* the contrast of the ramp `tokens/typography.css` authored; this REPLACES that
|
|
90
|
+
* ramp's display half with a geometric one. Naming a golden-ratio preset as a
|
|
91
|
+
* contrast value would have been the dishonest version of this feature.
|
|
92
|
+
*
|
|
93
|
+
* Bounded below 1.05 because a ratio at 1 is not a scale — every display rung
|
|
94
|
+
* collapses onto the one before it — and above 2 because doubling every step
|
|
95
|
+
* puts the fourth rung past a phone's whole width.
|
|
96
|
+
*
|
|
97
|
+
* Golden is exactly 1.618 here and is NOT clamped down to something tamer,
|
|
98
|
+
* because a scale that quietly gives you not-golden when you asked for golden is
|
|
99
|
+
* worse than one that refuses. What makes that safe is the ceiling below: this
|
|
100
|
+
* ramp has EIGHT display rungs and a classical modular scale is used with about
|
|
101
|
+
* four, so at 1.618 the eighth would be 799px. Measured, --text-8xl and
|
|
102
|
+
* --text-9xl are referenced in 15 files across the fleet, so that is a broken
|
|
103
|
+
* page and not a hypothetical.
|
|
104
|
+
*/
|
|
105
|
+
export declare const MODULAR_MIN = 1.05;
|
|
106
|
+
export declare const MODULAR_MAX = 2;
|
|
77
107
|
/**
|
|
78
108
|
* Is this a colour, or is it something being smuggled into a style attribute?
|
|
79
109
|
*
|
package/dist/preference.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preference.d.ts","sourceRoot":"","sources":["../src/preference.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,MAAM,MAAM,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,CAAC;AAE5D;;;GAGG;AACH,MAAM,MAAM,IAAI,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;AAE3D,8CAA8C;AAC9C,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;AAEpD,MAAM,WAAW,UAAU;IACzB,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,qCAAqC;IACrC,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,4DAA4D;IAC5D,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,wEAAwE;IACxE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,QAAQ,OAAO,CAAC;AAC7B,eAAO,MAAM,QAAQ,MAAM,CAAC;AAE5B;;;;;;;;;;GAUG;AACH,eAAO,MAAM,SAAS,OAAO,CAAC;AAC9B,eAAO,MAAM,SAAS,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"preference.d.ts","sourceRoot":"","sources":["../src/preference.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,MAAM,MAAM,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,CAAC;AAE5D;;;GAGG;AACH,MAAM,MAAM,IAAI,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;AAE3D,8CAA8C;AAC9C,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;AAEpD,MAAM,WAAW,UAAU;IACzB,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,qCAAqC;IACrC,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,4DAA4D;IAC5D,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,wEAAwE;IACxE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,QAAQ,OAAO,CAAC;AAC7B,eAAO,MAAM,QAAQ,MAAM,CAAC;AAE5B;;;;;;;;;;GAUG;AACH,eAAO,MAAM,SAAS,OAAO,CAAC;AAC9B,eAAO,MAAM,SAAS,MAAM,CAAC;AAE7B;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,WAAW,OAAO,CAAC;AAChC,eAAO,MAAM,WAAW,IAAI,CAAC;AAmE7B;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAS1C;AAED;;;;;GAKG;AACH,wBAAgB,IAAI,CAAC,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA0D1D;AAED,2EAA2E;AAC3E,wBAAgB,GAAG,CAAC,CAAC,EAAE,UAAU,EAAE,QAAQ,SAAc,GAAG,MAAM,CAKjE"}
|
package/dist/preference.js
CHANGED
|
@@ -49,6 +49,43 @@ export const TYPE_MAX = 1.4;
|
|
|
49
49
|
*/
|
|
50
50
|
export const RATIO_MIN = 0.75;
|
|
51
51
|
export const RATIO_MAX = 1.5;
|
|
52
|
+
/**
|
|
53
|
+
* A MODULAR scale — the classical one, where each display rung is the one below
|
|
54
|
+
* it times a fixed ratio. Golden is 1.618; the musical intervals designers name
|
|
55
|
+
* are 1.2 (minor third), 1.25 (major third), 1.333 (perfect fourth) and 1.5
|
|
56
|
+
* (perfect fifth).
|
|
57
|
+
*
|
|
58
|
+
* This is a different RULE from `ratio`, not another dial on it. `ratio` tunes
|
|
59
|
+
* the contrast of the ramp `tokens/typography.css` authored; this REPLACES that
|
|
60
|
+
* ramp's display half with a geometric one. Naming a golden-ratio preset as a
|
|
61
|
+
* contrast value would have been the dishonest version of this feature.
|
|
62
|
+
*
|
|
63
|
+
* Bounded below 1.05 because a ratio at 1 is not a scale — every display rung
|
|
64
|
+
* collapses onto the one before it — and above 2 because doubling every step
|
|
65
|
+
* puts the fourth rung past a phone's whole width.
|
|
66
|
+
*
|
|
67
|
+
* Golden is exactly 1.618 here and is NOT clamped down to something tamer,
|
|
68
|
+
* because a scale that quietly gives you not-golden when you asked for golden is
|
|
69
|
+
* worse than one that refuses. What makes that safe is the ceiling below: this
|
|
70
|
+
* ramp has EIGHT display rungs and a classical modular scale is used with about
|
|
71
|
+
* four, so at 1.618 the eighth would be 799px. Measured, --text-8xl and
|
|
72
|
+
* --text-9xl are referenced in 15 files across the fleet, so that is a broken
|
|
73
|
+
* page and not a hypothetical.
|
|
74
|
+
*/
|
|
75
|
+
export const MODULAR_MIN = 1.05;
|
|
76
|
+
export const MODULAR_MAX = 2;
|
|
77
|
+
/**
|
|
78
|
+
* The rungs a modular scale REGENERATES, in order, and the one it starts from.
|
|
79
|
+
*
|
|
80
|
+
* It deliberately stops at the display register. A geometric scale through the
|
|
81
|
+
* interface rungs is unusable at any real ratio: at 1.25 from a 14px base the
|
|
82
|
+
* next rungs are 17.5 and 21.9, so the 13px nav label and the 15px lead — the
|
|
83
|
+
* near-linear steps a dense interface is built on — do not exist. `xl` is the
|
|
84
|
+
* anchor because it is the last interface rung, so the display half continues
|
|
85
|
+
* from where the interface ends rather than restarting under it.
|
|
86
|
+
*/
|
|
87
|
+
const DISPLAY_RUNGS = ["2xl", "3xl", "4xl", "5xl", "6xl", "7xl", "8xl", "9xl"];
|
|
88
|
+
const MODULAR_ANCHOR_REM = 1.0625; // --text-xl, 17px
|
|
52
89
|
/**
|
|
53
90
|
* Density moves SPACING only, and its range is much tighter than type's.
|
|
54
91
|
*
|
|
@@ -128,6 +165,26 @@ export function vars(p) {
|
|
|
128
165
|
if (typeof p.ratio === "number" && Number.isFinite(p.ratio)) {
|
|
129
166
|
out["--type-ratio"] = round(clamp(p.ratio, RATIO_MIN, RATIO_MAX));
|
|
130
167
|
}
|
|
168
|
+
if (typeof p.modular === "number" && Number.isFinite(p.modular)) {
|
|
169
|
+
// Each display rung is the anchor times the ratio to its step, emitted as an
|
|
170
|
+
// explicit property. An INLINE custom property on :root outranks the
|
|
171
|
+
// stylesheet, so these simply replace the authored rungs — nothing has to be
|
|
172
|
+
// unset first, and the interface rungs the sheet declares are untouched
|
|
173
|
+
// because none is named here. It multiplies out with `--type-scale` for free,
|
|
174
|
+
// since every rung is still read through that ramp's own calc.
|
|
175
|
+
const r = clamp(p.modular, MODULAR_MIN, MODULAR_MAX);
|
|
176
|
+
DISPLAY_RUNGS.forEach((rung, i) => {
|
|
177
|
+
const rem = MODULAR_ANCHOR_REM * Math.pow(r, i + 1);
|
|
178
|
+
// THE SAME clamp the authored ramp carries, read from the same two names.
|
|
179
|
+
// A regenerated rung and an authored one must be bounded identically or
|
|
180
|
+
// the bound is a property of which code path produced the value, which is
|
|
181
|
+
// exactly the kind of "it depends" a design system exists to delete. The
|
|
182
|
+
// bounds are declared once, in tokens/typography.css, and referenced here
|
|
183
|
+
// — never restated, so moving one moves both.
|
|
184
|
+
out[`--text-${rung}`] =
|
|
185
|
+
`clamp(var(--text-floor), calc(${round(rem)}rem * var(--type-scale, 1)), var(--text-ceiling))`;
|
|
186
|
+
});
|
|
187
|
+
}
|
|
131
188
|
if (p.density && p.density in DENSITY) {
|
|
132
189
|
out["--density"] = round(DENSITY[p.density]);
|
|
133
190
|
}
|
package/dist/tokens.gen.d.ts
CHANGED
|
@@ -93,6 +93,8 @@ export declare const colors: {
|
|
|
93
93
|
export declare const typography: {
|
|
94
94
|
readonly 'type-scale': "1";
|
|
95
95
|
readonly 'type-ratio': "1";
|
|
96
|
+
readonly 'text-floor': "0.625rem";
|
|
97
|
+
readonly 'text-ceiling': "30vw";
|
|
96
98
|
readonly 'text-xs': "0.6875rem";
|
|
97
99
|
readonly 'leading-xs': "1rem";
|
|
98
100
|
readonly 'text-sm': "0.8125rem";
|
|
@@ -386,6 +388,8 @@ export declare const cssVars: {
|
|
|
386
388
|
readonly '--chrome-dot-green': "rgb(34 197 94 / .6)";
|
|
387
389
|
readonly '--type-scale': "1";
|
|
388
390
|
readonly '--type-ratio': "1";
|
|
391
|
+
readonly '--text-floor': "0.625rem";
|
|
392
|
+
readonly '--text-ceiling': "30vw";
|
|
389
393
|
readonly '--text-xs': "0.6875rem";
|
|
390
394
|
readonly '--leading-xs': "1rem";
|
|
391
395
|
readonly '--text-sm': "0.8125rem";
|
package/dist/tokens.gen.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.gen.d.ts","sourceRoot":"","sources":["../src/tokens.gen.ts"],"names":[],"mappings":"AAIA,kEAAkE;AAClE,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyFT,CAAA;AAEV,0EAA0E;AAC1E,eAAO,MAAM,UAAU
|
|
1
|
+
{"version":3,"file":"tokens.gen.d.ts","sourceRoot":"","sources":["../src/tokens.gen.ts"],"names":[],"mappings":"AAIA,kEAAkE;AAClE,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyFT,CAAA;AAEV,0EAA0E;AAC1E,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmEb,CAAA;AAEV,oEAAoE;AACpE,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2CV,CAAA;AAEV,8DAA8D;AAC9D,eAAO,MAAM,IAAI;;;;;;;;;;;;CAYP,CAAA;AAEV,kEAAkE;AAClE,eAAO,MAAM,MAAM;;;;;;;;;CAST,CAAA;AAEV,wEAAwE;AACxE,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BZ,CAAA;AAEV,kEAAkE;AAClE,eAAO,MAAM,MAAM;;;;;;;;;;;;;CAaT,CAAA;AAEV,6DAA6D;AAC7D,eAAO,MAAM,MAAM;;;;;;;;;;;;CAYT,CAAA;AAEV,gEAAgE;AAChE,eAAO,MAAM,KAAK;;;;;;CAMR,CAAA;AAEV,8DAA8D;AAC9D,eAAO,MAAM,IAAI,IAEP,CAAA;AAEV,sFAAsF;AACtF,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8QV,CAAA;AAEV,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,OAAO,CAAA"}
|
package/dist/tokens.gen.js
CHANGED
|
@@ -96,6 +96,8 @@ export const colors = {
|
|
|
96
96
|
export const typography = {
|
|
97
97
|
'type-scale': '1',
|
|
98
98
|
'type-ratio': '1',
|
|
99
|
+
'text-floor': '0.625rem',
|
|
100
|
+
'text-ceiling': '30vw',
|
|
99
101
|
'text-xs': '0.6875rem',
|
|
100
102
|
'leading-xs': '1rem',
|
|
101
103
|
'text-sm': '0.8125rem',
|
|
@@ -389,6 +391,8 @@ export const cssVars = {
|
|
|
389
391
|
'--chrome-dot-green': 'rgb(34 197 94 / .6)',
|
|
390
392
|
'--type-scale': '1',
|
|
391
393
|
'--type-ratio': '1',
|
|
394
|
+
'--text-floor': '0.625rem',
|
|
395
|
+
'--text-ceiling': '30vw',
|
|
392
396
|
'--text-xs': '0.6875rem',
|
|
393
397
|
'--leading-xs': '1rem',
|
|
394
398
|
'--text-sm': '0.8125rem',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzo/design",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.12",
|
|
4
4
|
"packageManager": "pnpm@11.17.0",
|
|
5
5
|
"description": "Hanzo Design System — 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",
|
|
@@ -102,6 +102,12 @@ check("EVERY ramp rung carries its multiplier — no rung can opt out", () => {
|
|
|
102
102
|
const value = m[1].trim();
|
|
103
103
|
if (value === "0") continue; // zero times anything is zero
|
|
104
104
|
if (value.startsWith("var(")) continue; // an alias inherits its target's calc
|
|
105
|
+
// --text-floor / --text-ceiling are the BOUNDS every rung is clamped
|
|
106
|
+
// between, not rungs. A bound that scaled with the knob would move with
|
|
107
|
+
// the value it exists to bound and could never bind — which is the whole
|
|
108
|
+
// reason a floor is stated in absolute units and a ceiling in the
|
|
109
|
+
// viewport's.
|
|
110
|
+
if (/^--text-(floor|ceiling):/.test(m[0])) continue;
|
|
105
111
|
if (!value.includes(`var(${knob}`)) bare.push(`${file}: ${m[0].trim()}`);
|
|
106
112
|
}
|
|
107
113
|
}
|
|
@@ -144,6 +150,45 @@ check("an unknown stored value is refused, not passed through", () => {
|
|
|
144
150
|
eq(vars({ width: "enormous" }), {});
|
|
145
151
|
});
|
|
146
152
|
|
|
153
|
+
check("a modular scale regenerates the DISPLAY rungs, geometrically", () => {
|
|
154
|
+
const v = vars({ modular: 1.618 });
|
|
155
|
+
const names = Object.keys(v);
|
|
156
|
+
eq(names.length, 8, "a modular scale emits the eight display rungs:");
|
|
157
|
+
// Each rung is the one below it times the ratio — that IS the scale.
|
|
158
|
+
const rem = (n) => parseFloat(v[`--text-${n}`].match(/([\d.]+)rem/)[1]);
|
|
159
|
+
const rungs = ["2xl","3xl","4xl","5xl","6xl","7xl","8xl","9xl"];
|
|
160
|
+
for (let i = 1; i < rungs.length; i++) {
|
|
161
|
+
const r = rem(rungs[i]) / rem(rungs[i - 1]);
|
|
162
|
+
ok(Math.abs(r - 1.618) < 0.001, `${rungs[i]}/${rungs[i-1]} = ${r}, want the ratio`);
|
|
163
|
+
}
|
|
164
|
+
// Anchored on xl (17px), so the display half continues the interface rather
|
|
165
|
+
// than restarting under it.
|
|
166
|
+
ok(Math.abs(rem("2xl") - 1.0625 * 1.618) < 0.001, "2xl must be xl x ratio");
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
check("a modular scale leaves the INTERFACE rungs alone", () => {
|
|
170
|
+
// A geometric scale through xs..xl is unusable at any real ratio: the 13px nav
|
|
171
|
+
// label and the 15px lead simply do not exist on one.
|
|
172
|
+
const emitted = Object.keys(vars({ modular: 1.618 }));
|
|
173
|
+
for (const n of ["--text-xs", "--text-sm", "--text-base", "--text-lg", "--text-xl"]) {
|
|
174
|
+
ok(!emitted.includes(n), `${n} is the interface register and must not be regenerated`);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
check("a modular scale still answers to the size knob", () => {
|
|
179
|
+
// It multiplies out with --type-scale rather than freezing a px value.
|
|
180
|
+
ok(vars({ modular: 1.5 })["--text-4xl"].includes("var(--type-scale, 1)"),
|
|
181
|
+
"a regenerated rung must carry the size knob");
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
check("the modular ratio is CLAMPED to something that is still a scale", () => {
|
|
185
|
+
// At 1 every display rung collapses onto the one below it.
|
|
186
|
+
const flat = vars({ modular: 1 });
|
|
187
|
+
const rem = (v, n) => parseFloat(v[`--text-${n}`].match(/([\d.]+)rem/)[1]);
|
|
188
|
+
ok(rem(flat, "3xl") > rem(flat, "2xl"), "a clamped ratio must still ascend");
|
|
189
|
+
eq(vars({ modular: 99 })["--text-2xl"], vars({ modular: 2 })["--text-2xl"], "max:");
|
|
190
|
+
});
|
|
191
|
+
|
|
147
192
|
check("a colour lands on both --primary and --accent", () => {
|
|
148
193
|
const v = vars({ accent: "#808000" });
|
|
149
194
|
eq(v["--primary"], "#808000");
|
package/scripts/gen-tokens.mjs
CHANGED
|
@@ -64,12 +64,24 @@ function parse(css) {
|
|
|
64
64
|
* against a knob the table does not have.
|
|
65
65
|
*/
|
|
66
66
|
const KNOB = /^calc\(\s*([^*]+?)\s*\*\s*var\(--(?:type-scale|density),\s*1\)\s*\)$/
|
|
67
|
-
|
|
67
|
+
// The BOUNDS peel first. Every rung is `clamp(floor, value, ceiling)` — one
|
|
68
|
+
// rule at both ends — and the table carries the VALUE: a bound is a rendering
|
|
69
|
+
// decision the browser makes against a live viewport, so a JS table cannot hold
|
|
70
|
+
// one and must not pretend to. `max(floor, value)` is the older one-ended form,
|
|
71
|
+
// kept so a token file mid-migration still generates.
|
|
72
|
+
const BOUNDS = [
|
|
73
|
+
/^clamp\(\s*[^,]+,\s*(calc\(.*\))\s*,\s*[^,]+\)$/,
|
|
74
|
+
/^max\(\s*[\d.]+[a-z%]*\s*,\s*(calc\(.+\))\s*\)$/,
|
|
75
|
+
]
|
|
68
76
|
const RUNG =
|
|
69
77
|
/^calc\(\(\s*([\d.]+)(r?em|px)\s*([+-])\s*([\d.]+)(?:r?em|px)\s*\*\s*var\(--type-ratio,\s*1\)\s*\)\s*\*\s*var\(--type-scale,\s*1\)\s*\)$/
|
|
70
78
|
|
|
71
79
|
const scale = (v) => {
|
|
72
|
-
|
|
80
|
+
let inner = v
|
|
81
|
+
for (const b of BOUNDS) {
|
|
82
|
+
const m = inner.match(b)
|
|
83
|
+
if (m) { inner = m[1]; break }
|
|
84
|
+
}
|
|
73
85
|
const r = inner.match(RUNG)
|
|
74
86
|
if (r) {
|
|
75
87
|
const [, anchor, unit, sign, delta] = r
|
package/src/preference.ts
CHANGED
|
@@ -46,6 +46,11 @@ export interface Preference {
|
|
|
46
46
|
* above 1 opens the display end and tightens the small one.
|
|
47
47
|
*/
|
|
48
48
|
ratio?: number;
|
|
49
|
+
/**
|
|
50
|
+
* A modular scale for the display rungs — 1.618 for golden, 1.25 for a major
|
|
51
|
+
* third. Absent means the authored ramp, which is the tuned default.
|
|
52
|
+
*/
|
|
53
|
+
modular?: number;
|
|
49
54
|
density?: Density;
|
|
50
55
|
/** Which face the page is set in. */
|
|
51
56
|
font?: Face;
|
|
@@ -81,6 +86,45 @@ export const TYPE_MAX = 1.4;
|
|
|
81
86
|
export const RATIO_MIN = 0.75;
|
|
82
87
|
export const RATIO_MAX = 1.5;
|
|
83
88
|
|
|
89
|
+
/**
|
|
90
|
+
* A MODULAR scale — the classical one, where each display rung is the one below
|
|
91
|
+
* it times a fixed ratio. Golden is 1.618; the musical intervals designers name
|
|
92
|
+
* are 1.2 (minor third), 1.25 (major third), 1.333 (perfect fourth) and 1.5
|
|
93
|
+
* (perfect fifth).
|
|
94
|
+
*
|
|
95
|
+
* This is a different RULE from `ratio`, not another dial on it. `ratio` tunes
|
|
96
|
+
* the contrast of the ramp `tokens/typography.css` authored; this REPLACES that
|
|
97
|
+
* ramp's display half with a geometric one. Naming a golden-ratio preset as a
|
|
98
|
+
* contrast value would have been the dishonest version of this feature.
|
|
99
|
+
*
|
|
100
|
+
* Bounded below 1.05 because a ratio at 1 is not a scale — every display rung
|
|
101
|
+
* collapses onto the one before it — and above 2 because doubling every step
|
|
102
|
+
* puts the fourth rung past a phone's whole width.
|
|
103
|
+
*
|
|
104
|
+
* Golden is exactly 1.618 here and is NOT clamped down to something tamer,
|
|
105
|
+
* because a scale that quietly gives you not-golden when you asked for golden is
|
|
106
|
+
* worse than one that refuses. What makes that safe is the ceiling below: this
|
|
107
|
+
* ramp has EIGHT display rungs and a classical modular scale is used with about
|
|
108
|
+
* four, so at 1.618 the eighth would be 799px. Measured, --text-8xl and
|
|
109
|
+
* --text-9xl are referenced in 15 files across the fleet, so that is a broken
|
|
110
|
+
* page and not a hypothetical.
|
|
111
|
+
*/
|
|
112
|
+
export const MODULAR_MIN = 1.05;
|
|
113
|
+
export const MODULAR_MAX = 2;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* The rungs a modular scale REGENERATES, in order, and the one it starts from.
|
|
117
|
+
*
|
|
118
|
+
* It deliberately stops at the display register. A geometric scale through the
|
|
119
|
+
* interface rungs is unusable at any real ratio: at 1.25 from a 14px base the
|
|
120
|
+
* next rungs are 17.5 and 21.9, so the 13px nav label and the 15px lead — the
|
|
121
|
+
* near-linear steps a dense interface is built on — do not exist. `xl` is the
|
|
122
|
+
* anchor because it is the last interface rung, so the display half continues
|
|
123
|
+
* from where the interface ends rather than restarting under it.
|
|
124
|
+
*/
|
|
125
|
+
const DISPLAY_RUNGS = ["2xl", "3xl", "4xl", "5xl", "6xl", "7xl", "8xl", "9xl"];
|
|
126
|
+
const MODULAR_ANCHOR_REM = 1.0625; // --text-xl, 17px
|
|
127
|
+
|
|
84
128
|
/**
|
|
85
129
|
* Density moves SPACING only, and its range is much tighter than type's.
|
|
86
130
|
*
|
|
@@ -169,6 +213,27 @@ export function vars(p: Preference): Record<string, string> {
|
|
|
169
213
|
out["--type-ratio"] = round(clamp(p.ratio, RATIO_MIN, RATIO_MAX));
|
|
170
214
|
}
|
|
171
215
|
|
|
216
|
+
if (typeof p.modular === "number" && Number.isFinite(p.modular)) {
|
|
217
|
+
// Each display rung is the anchor times the ratio to its step, emitted as an
|
|
218
|
+
// explicit property. An INLINE custom property on :root outranks the
|
|
219
|
+
// stylesheet, so these simply replace the authored rungs — nothing has to be
|
|
220
|
+
// unset first, and the interface rungs the sheet declares are untouched
|
|
221
|
+
// because none is named here. It multiplies out with `--type-scale` for free,
|
|
222
|
+
// since every rung is still read through that ramp's own calc.
|
|
223
|
+
const r = clamp(p.modular, MODULAR_MIN, MODULAR_MAX);
|
|
224
|
+
DISPLAY_RUNGS.forEach((rung, i) => {
|
|
225
|
+
const rem = MODULAR_ANCHOR_REM * Math.pow(r, i + 1);
|
|
226
|
+
// THE SAME clamp the authored ramp carries, read from the same two names.
|
|
227
|
+
// A regenerated rung and an authored one must be bounded identically or
|
|
228
|
+
// the bound is a property of which code path produced the value, which is
|
|
229
|
+
// exactly the kind of "it depends" a design system exists to delete. The
|
|
230
|
+
// bounds are declared once, in tokens/typography.css, and referenced here
|
|
231
|
+
// — never restated, so moving one moves both.
|
|
232
|
+
out[`--text-${rung}`] =
|
|
233
|
+
`clamp(var(--text-floor), calc(${round(rem)}rem * var(--type-scale, 1)), var(--text-ceiling))`;
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
172
237
|
if (p.density && p.density in DENSITY) {
|
|
173
238
|
out["--density"] = round(DENSITY[p.density]);
|
|
174
239
|
}
|
package/src/tokens.gen.ts
CHANGED
|
@@ -98,6 +98,8 @@ export const colors = {
|
|
|
98
98
|
export const typography = {
|
|
99
99
|
'type-scale': '1',
|
|
100
100
|
'type-ratio': '1',
|
|
101
|
+
'text-floor': '0.625rem',
|
|
102
|
+
'text-ceiling': '30vw',
|
|
101
103
|
'text-xs': '0.6875rem',
|
|
102
104
|
'leading-xs': '1rem',
|
|
103
105
|
'text-sm': '0.8125rem',
|
|
@@ -402,6 +404,8 @@ export const cssVars = {
|
|
|
402
404
|
'--chrome-dot-green': 'rgb(34 197 94 / .6)',
|
|
403
405
|
'--type-scale': '1',
|
|
404
406
|
'--type-ratio': '1',
|
|
407
|
+
'--text-floor': '0.625rem',
|
|
408
|
+
'--text-ceiling': '30vw',
|
|
405
409
|
'--text-xs': '0.6875rem',
|
|
406
410
|
'--leading-xs': '1rem',
|
|
407
411
|
'--text-sm': '0.8125rem',
|
package/styles.css
CHANGED
|
@@ -316,24 +316,36 @@
|
|
|
316
316
|
A rung is one place, so a rung added later needs a delta and nothing else. */
|
|
317
317
|
--type-ratio:1;
|
|
318
318
|
|
|
319
|
-
/*
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
--text-
|
|
335
|
-
--text-
|
|
336
|
-
--text-
|
|
319
|
+
/* EVERY rung is bounded at BOTH ends, by one rule, because the knobs COMPOUND
|
|
320
|
+
and they compound in both directions. Clamping each knob's range separately
|
|
321
|
+
cannot fix either end — 0.85 x 0.9 is inside both bounds and lands at 8.5px
|
|
322
|
+
— so the RESULT is what carries the bound:
|
|
323
|
+
|
|
324
|
+
--text-floor 10px below this a label is not small, it is unreadable
|
|
325
|
+
--text-ceiling 30vw above this a heading is not type, it is a texture
|
|
326
|
+
|
|
327
|
+
The ceiling is in the VIEWPORT's units because that is what "too big" means:
|
|
328
|
+
measured, the tallest rung (--text-9xl, 112px) is 28.7vw on a 390px phone
|
|
329
|
+
and 7.8vw at 1440, so 30vw binds no published value at any real screen and
|
|
330
|
+
catches only a scale that has run away. Both bounds are stated once here and
|
|
331
|
+
`vars()` emits the same clamp for a modular scale, so a regenerated rung and
|
|
332
|
+
an authored one are bounded identically — the rule does not depend on which
|
|
333
|
+
produced the value. */
|
|
334
|
+
--text-floor:0.625rem;
|
|
335
|
+
--text-ceiling:30vw;
|
|
336
|
+
--text-xs:clamp(var(--text-floor), calc((0.875rem - 0.1875rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-xs:calc((1.35rem - 0.35rem * var(--type-ratio, 1)) * var(--type-scale, 1)); /* 11px — eyebrows / section labels */
|
|
337
|
+
--text-sm:clamp(var(--text-floor), calc((0.875rem - 0.0625rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-sm:calc((1.35rem - 0.2rem * var(--type-ratio, 1)) * var(--type-scale, 1)); /* 13px — nav labels, dense body */
|
|
338
|
+
--text-base:clamp(var(--text-floor), calc(0.875rem * var(--type-scale, 1)), var(--text-ceiling)); --leading-base:calc(1.35rem * var(--type-scale, 1)); /* 14px — the ANCHOR: the rung the ratio pivots on, so it carries no ratio term */
|
|
339
|
+
--text-lg:clamp(var(--text-floor), calc((0.875rem + 0.0625rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-lg:calc((1.35rem + 0.05rem * var(--type-ratio, 1)) * var(--type-scale, 1)); /* 15px */
|
|
340
|
+
--text-xl:clamp(var(--text-floor), calc((0.875rem + 0.1875rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-xl:calc((1.35rem + 0.2rem * var(--type-ratio, 1)) * var(--type-scale, 1)); /* 17px */
|
|
341
|
+
--text-2xl:clamp(var(--text-floor), calc((0.875rem + 0.4375rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-2xl:calc((1.35rem + 0.35rem * var(--type-ratio, 1)) * var(--type-scale, 1)); /* 21px */
|
|
342
|
+
--text-3xl:clamp(var(--text-floor), calc((0.875rem + 0.75rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-3xl:calc((1.35rem + 0.6rem * var(--type-ratio, 1)) * var(--type-scale, 1)); /* 26px */
|
|
343
|
+
--text-4xl:clamp(var(--text-floor), calc((0.875rem + 1.125rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-4xl:calc((1.35rem + 0.9rem * var(--type-ratio, 1)) * var(--type-scale, 1)); /* 32px */
|
|
344
|
+
--text-5xl:clamp(var(--text-floor), calc((0.875rem + 1.625rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-5xl:1.05; /* 40px — display leading is unitless, so it tracks its own size */
|
|
345
|
+
--text-6xl:clamp(var(--text-floor), calc((0.875rem + 2.375rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-6xl:1; /* 52px */
|
|
346
|
+
--text-7xl:clamp(var(--text-floor), calc((0.875rem + 3.125rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-7xl:1; /* 64px */
|
|
347
|
+
--text-8xl:clamp(var(--text-floor), calc((0.875rem + 4.375rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-8xl:1; /* 84px */
|
|
348
|
+
--text-9xl:clamp(var(--text-floor), calc((0.875rem + 6.125rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-9xl:1; /* 112px */
|
|
337
349
|
|
|
338
350
|
/* The SAME scale under @hanzo/brand's spelling. @hanzo/gui's shell theme and
|
|
339
351
|
Hanzo Studio address the ramp as --font-size-*; both names are one value, so
|
package/tailwind.css
CHANGED
|
@@ -339,24 +339,36 @@
|
|
|
339
339
|
A rung is one place, so a rung added later needs a delta and nothing else. */
|
|
340
340
|
--type-ratio:1;
|
|
341
341
|
|
|
342
|
-
/*
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
--text-
|
|
358
|
-
--text-
|
|
359
|
-
--text-
|
|
342
|
+
/* EVERY rung is bounded at BOTH ends, by one rule, because the knobs COMPOUND
|
|
343
|
+
and they compound in both directions. Clamping each knob's range separately
|
|
344
|
+
cannot fix either end — 0.85 x 0.9 is inside both bounds and lands at 8.5px
|
|
345
|
+
— so the RESULT is what carries the bound:
|
|
346
|
+
|
|
347
|
+
--text-floor 10px below this a label is not small, it is unreadable
|
|
348
|
+
--text-ceiling 30vw above this a heading is not type, it is a texture
|
|
349
|
+
|
|
350
|
+
The ceiling is in the VIEWPORT's units because that is what "too big" means:
|
|
351
|
+
measured, the tallest rung (--text-9xl, 112px) is 28.7vw on a 390px phone
|
|
352
|
+
and 7.8vw at 1440, so 30vw binds no published value at any real screen and
|
|
353
|
+
catches only a scale that has run away. Both bounds are stated once here and
|
|
354
|
+
`vars()` emits the same clamp for a modular scale, so a regenerated rung and
|
|
355
|
+
an authored one are bounded identically — the rule does not depend on which
|
|
356
|
+
produced the value. */
|
|
357
|
+
--text-floor:0.625rem;
|
|
358
|
+
--text-ceiling:30vw;
|
|
359
|
+
--text-xs:clamp(var(--text-floor), calc((0.875rem - 0.1875rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-xs:calc((1.35rem - 0.35rem * var(--type-ratio, 1)) * var(--type-scale, 1)); /* 11px — eyebrows / section labels */
|
|
360
|
+
--text-sm:clamp(var(--text-floor), calc((0.875rem - 0.0625rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-sm:calc((1.35rem - 0.2rem * var(--type-ratio, 1)) * var(--type-scale, 1)); /* 13px — nav labels, dense body */
|
|
361
|
+
--text-base:clamp(var(--text-floor), calc(0.875rem * var(--type-scale, 1)), var(--text-ceiling)); --leading-base:calc(1.35rem * var(--type-scale, 1)); /* 14px — the ANCHOR: the rung the ratio pivots on, so it carries no ratio term */
|
|
362
|
+
--text-lg:clamp(var(--text-floor), calc((0.875rem + 0.0625rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-lg:calc((1.35rem + 0.05rem * var(--type-ratio, 1)) * var(--type-scale, 1)); /* 15px */
|
|
363
|
+
--text-xl:clamp(var(--text-floor), calc((0.875rem + 0.1875rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-xl:calc((1.35rem + 0.2rem * var(--type-ratio, 1)) * var(--type-scale, 1)); /* 17px */
|
|
364
|
+
--text-2xl:clamp(var(--text-floor), calc((0.875rem + 0.4375rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-2xl:calc((1.35rem + 0.35rem * var(--type-ratio, 1)) * var(--type-scale, 1)); /* 21px */
|
|
365
|
+
--text-3xl:clamp(var(--text-floor), calc((0.875rem + 0.75rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-3xl:calc((1.35rem + 0.6rem * var(--type-ratio, 1)) * var(--type-scale, 1)); /* 26px */
|
|
366
|
+
--text-4xl:clamp(var(--text-floor), calc((0.875rem + 1.125rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-4xl:calc((1.35rem + 0.9rem * var(--type-ratio, 1)) * var(--type-scale, 1)); /* 32px */
|
|
367
|
+
--text-5xl:clamp(var(--text-floor), calc((0.875rem + 1.625rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-5xl:1.05; /* 40px — display leading is unitless, so it tracks its own size */
|
|
368
|
+
--text-6xl:clamp(var(--text-floor), calc((0.875rem + 2.375rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-6xl:1; /* 52px */
|
|
369
|
+
--text-7xl:clamp(var(--text-floor), calc((0.875rem + 3.125rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-7xl:1; /* 64px */
|
|
370
|
+
--text-8xl:clamp(var(--text-floor), calc((0.875rem + 4.375rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-8xl:1; /* 84px */
|
|
371
|
+
--text-9xl:clamp(var(--text-floor), calc((0.875rem + 6.125rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-9xl:1; /* 112px */
|
|
360
372
|
|
|
361
373
|
/* The SAME scale under @hanzo/brand's spelling. @hanzo/gui's shell theme and
|
|
362
374
|
Hanzo Studio address the ramp as --font-size-*; both names are one value, so
|
package/tokens/typography.css
CHANGED
|
@@ -31,24 +31,36 @@
|
|
|
31
31
|
A rung is one place, so a rung added later needs a delta and nothing else. */
|
|
32
32
|
--type-ratio:1;
|
|
33
33
|
|
|
34
|
-
/*
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
--text-
|
|
50
|
-
--text-
|
|
51
|
-
--text-
|
|
34
|
+
/* EVERY rung is bounded at BOTH ends, by one rule, because the knobs COMPOUND
|
|
35
|
+
and they compound in both directions. Clamping each knob's range separately
|
|
36
|
+
cannot fix either end — 0.85 x 0.9 is inside both bounds and lands at 8.5px
|
|
37
|
+
— so the RESULT is what carries the bound:
|
|
38
|
+
|
|
39
|
+
--text-floor 10px below this a label is not small, it is unreadable
|
|
40
|
+
--text-ceiling 30vw above this a heading is not type, it is a texture
|
|
41
|
+
|
|
42
|
+
The ceiling is in the VIEWPORT's units because that is what "too big" means:
|
|
43
|
+
measured, the tallest rung (--text-9xl, 112px) is 28.7vw on a 390px phone
|
|
44
|
+
and 7.8vw at 1440, so 30vw binds no published value at any real screen and
|
|
45
|
+
catches only a scale that has run away. Both bounds are stated once here and
|
|
46
|
+
`vars()` emits the same clamp for a modular scale, so a regenerated rung and
|
|
47
|
+
an authored one are bounded identically — the rule does not depend on which
|
|
48
|
+
produced the value. */
|
|
49
|
+
--text-floor:0.625rem;
|
|
50
|
+
--text-ceiling:30vw;
|
|
51
|
+
--text-xs:clamp(var(--text-floor), calc((0.875rem - 0.1875rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-xs:calc((1.35rem - 0.35rem * var(--type-ratio, 1)) * var(--type-scale, 1)); /* 11px — eyebrows / section labels */
|
|
52
|
+
--text-sm:clamp(var(--text-floor), calc((0.875rem - 0.0625rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-sm:calc((1.35rem - 0.2rem * var(--type-ratio, 1)) * var(--type-scale, 1)); /* 13px — nav labels, dense body */
|
|
53
|
+
--text-base:clamp(var(--text-floor), calc(0.875rem * var(--type-scale, 1)), var(--text-ceiling)); --leading-base:calc(1.35rem * var(--type-scale, 1)); /* 14px — the ANCHOR: the rung the ratio pivots on, so it carries no ratio term */
|
|
54
|
+
--text-lg:clamp(var(--text-floor), calc((0.875rem + 0.0625rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-lg:calc((1.35rem + 0.05rem * var(--type-ratio, 1)) * var(--type-scale, 1)); /* 15px */
|
|
55
|
+
--text-xl:clamp(var(--text-floor), calc((0.875rem + 0.1875rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-xl:calc((1.35rem + 0.2rem * var(--type-ratio, 1)) * var(--type-scale, 1)); /* 17px */
|
|
56
|
+
--text-2xl:clamp(var(--text-floor), calc((0.875rem + 0.4375rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-2xl:calc((1.35rem + 0.35rem * var(--type-ratio, 1)) * var(--type-scale, 1)); /* 21px */
|
|
57
|
+
--text-3xl:clamp(var(--text-floor), calc((0.875rem + 0.75rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-3xl:calc((1.35rem + 0.6rem * var(--type-ratio, 1)) * var(--type-scale, 1)); /* 26px */
|
|
58
|
+
--text-4xl:clamp(var(--text-floor), calc((0.875rem + 1.125rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-4xl:calc((1.35rem + 0.9rem * var(--type-ratio, 1)) * var(--type-scale, 1)); /* 32px */
|
|
59
|
+
--text-5xl:clamp(var(--text-floor), calc((0.875rem + 1.625rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-5xl:1.05; /* 40px — display leading is unitless, so it tracks its own size */
|
|
60
|
+
--text-6xl:clamp(var(--text-floor), calc((0.875rem + 2.375rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-6xl:1; /* 52px */
|
|
61
|
+
--text-7xl:clamp(var(--text-floor), calc((0.875rem + 3.125rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-7xl:1; /* 64px */
|
|
62
|
+
--text-8xl:clamp(var(--text-floor), calc((0.875rem + 4.375rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-8xl:1; /* 84px */
|
|
63
|
+
--text-9xl:clamp(var(--text-floor), calc((0.875rem + 6.125rem * var(--type-ratio, 1)) * var(--type-scale, 1)), var(--text-ceiling)); --leading-9xl:1; /* 112px */
|
|
52
64
|
|
|
53
65
|
/* The SAME scale under @hanzo/brand's spelling. @hanzo/gui's shell theme and
|
|
54
66
|
Hanzo Studio address the ramp as --font-size-*; both names are one value, so
|