@dreamtree-org/twreact-ui 1.2.1 → 1.3.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/DESIGN.md +118 -0
- package/README.md +52 -34
- package/ai-skills/dreamtree-ui.md +27 -0
- package/dist/components/blocks/AppShell.d.ts +4 -0
- package/dist/components/blocks/AppShell.d.ts.map +1 -0
- package/dist/components/blocks/ConfirmDialog.d.ts +4 -0
- package/dist/components/blocks/ConfirmDialog.d.ts.map +1 -0
- package/dist/components/blocks/EmptyState.d.ts +4 -0
- package/dist/components/blocks/EmptyState.d.ts.map +1 -0
- package/dist/components/blocks/ErrorState.d.ts +4 -0
- package/dist/components/blocks/ErrorState.d.ts.map +1 -0
- package/dist/components/blocks/LoadingState.d.ts +9 -0
- package/dist/components/blocks/LoadingState.d.ts.map +1 -0
- package/dist/components/blocks/PageHeader.d.ts +4 -0
- package/dist/components/blocks/PageHeader.d.ts.map +1 -0
- package/dist/components/blocks/StatCard.d.ts +4 -0
- package/dist/components/blocks/StatCard.d.ts.map +1 -0
- package/dist/components/blocks/StatusPill.d.ts +5 -0
- package/dist/components/blocks/StatusPill.d.ts.map +1 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +48 -38
- package/dist/index.esm.css +1 -1
- package/dist/index.esm.js +958 -268
- package/dist/index.js +965 -267
- package/dist/tokens/brands/default.css +1 -0
- package/dist/tokens/brands/default.json +5 -0
- package/dist/tokens/brands/practicepro.css +17 -0
- package/dist/tokens/brands/practicepro.json +27 -0
- package/dist/tokens/tailwind3-preset.cjs +214 -0
- package/dist/tokens/theme.css +193 -0
- package/dist/tokens/tokens.json +373 -0
- package/dist/tokens/vars.css +98 -0
- package/dist/types/blocks.d.ts +109 -0
- package/dist/types/blocks.d.ts.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/doc/README.md +19 -0
- package/mcp/catalog.snapshot.json +398 -3
- package/package.json +11 -5
- package/tokens/brands/default.json +5 -0
- package/tokens/brands/practicepro.json +27 -0
- package/tokens/lib.cjs +128 -0
- package/tokens/tokens.json +133 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dreamtree-org/twreact-ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "A comprehensive React + Tailwind components library for building modern web apps",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Partha Preetham Krishna",
|
|
@@ -21,14 +21,20 @@
|
|
|
21
21
|
"mcp/catalog-core.mjs",
|
|
22
22
|
"mcp/catalog-snapshot.mjs",
|
|
23
23
|
"mcp/catalog.snapshot.json",
|
|
24
|
+
"tokens/tokens.json",
|
|
25
|
+
"tokens/lib.cjs",
|
|
26
|
+
"tokens/brands",
|
|
27
|
+
"DESIGN.md",
|
|
24
28
|
"ai-skills",
|
|
25
29
|
"doc/assets/upi-qr.png",
|
|
26
30
|
"package.json",
|
|
27
31
|
"README.md"
|
|
28
32
|
],
|
|
29
33
|
"scripts": {
|
|
30
|
-
"build": "npm run build:types && npm run build:js && npm run gen:ai-schema",
|
|
31
|
-
"build:types": "tsc --project tsconfig.build.json --emitDeclarationOnly --outDir dist",
|
|
34
|
+
"build": "npm run build:types && npm run build:tokens && npm run build:js && node scripts/gen-typed-dts.mjs && npm run gen:ai-schema",
|
|
35
|
+
"build:types": "tsc --project tsconfig.build.json --emitDeclarationOnly --outDir dist && node scripts/gen-typed-dts.mjs",
|
|
36
|
+
"build:tokens": "node scripts/build-tokens.mjs",
|
|
37
|
+
"tokens:check": "node scripts/build-tokens.mjs --check",
|
|
32
38
|
"build:js": "node scripts/version-bump.js && cross-env NODE_ENV=production rollup -c --no-sourcemap",
|
|
33
39
|
"gen:ai-schema": "node scripts/gen-ai-schema.mjs",
|
|
34
40
|
"dev": "cross-env NODE_ENV=development rollup -c -w",
|
|
@@ -93,8 +99,8 @@
|
|
|
93
99
|
"node": ">=20.0.0"
|
|
94
100
|
},
|
|
95
101
|
"peerDependencies": {
|
|
96
|
-
"react": "^18.3.1",
|
|
97
|
-
"react-dom": "^18.3.1"
|
|
102
|
+
"react": "^18.3.1 || ^19.0.0",
|
|
103
|
+
"react-dom": "^18.3.1 || ^19.0.0"
|
|
98
104
|
},
|
|
99
105
|
"dependencies": {
|
|
100
106
|
"@hookform/resolvers": "^3.10.0",
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "practicepro",
|
|
3
|
+
"label": "PracticePro",
|
|
4
|
+
"$description": "Teal primary and navy shell, taken from PracticePro/app/globals.css (brand-50/100/500/600/700, shell, shell-active). Steps PracticePro does not define are interpolated on the same hue. PracticePro's own muted grey (#64748b) is not carried over: on the #f6f8fb background it is about 4.4:1, below WCAG AA, so the shared muted token (#475569, 7.5:1) is used.",
|
|
5
|
+
"color": {
|
|
6
|
+
"primary": {
|
|
7
|
+
"50": "#ecfbf8",
|
|
8
|
+
"100": "#d1f3ee",
|
|
9
|
+
"200": "#a6e6dc",
|
|
10
|
+
"300": "#6fd1c4",
|
|
11
|
+
"400": "#34b3a6",
|
|
12
|
+
"500": "#0f8a8a",
|
|
13
|
+
"600": "#0d7377",
|
|
14
|
+
"700": "#0b5d60",
|
|
15
|
+
"800": "#0b4a4d",
|
|
16
|
+
"900": "#0a3d40",
|
|
17
|
+
"950": "#052426"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"role": {
|
|
21
|
+
"background": "#f6f8fb",
|
|
22
|
+
"foreground": "#0f172a",
|
|
23
|
+
"line": "#e2e8f0",
|
|
24
|
+
"shell": "#0f2b3d",
|
|
25
|
+
"shell-active": "#13606a"
|
|
26
|
+
}
|
|
27
|
+
}
|
package/tokens/lib.cjs
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// Shared token helpers for tailwind.config.js (CJS) and scripts/build-tokens.mjs (ESM).
|
|
2
|
+
// tokens.json is the W3C DTCG source of truth; this file only reads and reshapes it.
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
const TOKENS_PATH = path.join(__dirname, 'tokens.json');
|
|
7
|
+
const BRANDS_DIR = path.join(__dirname, 'brands');
|
|
8
|
+
const DARK_KEY = 'org.dreamtree.dark';
|
|
9
|
+
const SCALE_STEPS = ['50', '100', '200', '300', '400', '500', '600', '700', '800', '900', '950'];
|
|
10
|
+
|
|
11
|
+
function loadTokens() {
|
|
12
|
+
return JSON.parse(fs.readFileSync(TOKENS_PATH, 'utf8'));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function listBrands() {
|
|
16
|
+
if (!fs.existsSync(BRANDS_DIR)) return [];
|
|
17
|
+
return fs.readdirSync(BRANDS_DIR).filter((f) => f.endsWith('.json')).map((f) => f.slice(0, -5)).sort();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function loadBrand(name) {
|
|
21
|
+
const file = path.join(BRANDS_DIR, `${name}.json`);
|
|
22
|
+
if (!fs.existsSync(file)) {
|
|
23
|
+
throw new Error(`Unknown brand "${name}". Available brands: ${listBrands().join(', ') || '(none)'}. Add tokens/brands/${name}.json to create one.`);
|
|
24
|
+
}
|
|
25
|
+
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function hexToChannels(hex) {
|
|
29
|
+
const m = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(String(hex).trim());
|
|
30
|
+
if (!m) throw new Error(`Token value "${hex}" is not a hex colour (#rgb or #rrggbb).`);
|
|
31
|
+
let h = m[1];
|
|
32
|
+
if (h.length === 3) h = h.split('').map((c) => c + c).join('');
|
|
33
|
+
const n = parseInt(h, 16);
|
|
34
|
+
return `${(n >> 16) & 255} ${(n >> 8) & 255} ${n & 255}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Palette scales (primary, secondary, …) as { name: { step: hex } }. */
|
|
38
|
+
function scales(tokens) {
|
|
39
|
+
const out = {};
|
|
40
|
+
for (const [name, group] of Object.entries(tokens.color)) {
|
|
41
|
+
if (name.startsWith('$')) continue;
|
|
42
|
+
out[name] = {};
|
|
43
|
+
for (const step of SCALE_STEPS) {
|
|
44
|
+
if (!group[step]) throw new Error(`color.${name} is missing step ${step}.`);
|
|
45
|
+
out[name][step] = group[step].$value;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Semantic roles as { name: { light, dark, description } }. */
|
|
52
|
+
function roles(tokens) {
|
|
53
|
+
const out = {};
|
|
54
|
+
for (const [name, t] of Object.entries(tokens.role)) {
|
|
55
|
+
if (name.startsWith('$')) continue;
|
|
56
|
+
out[name] = { light: t.$value, dark: (t.$extensions && t.$extensions[DARK_KEY]) || t.$value, description: t.$description || '' };
|
|
57
|
+
}
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Merge a brand file over the tokens: brand.color.<scale>.<step> and brand.role.<name> (string or {light,dark}). */
|
|
62
|
+
function applyBrand(tokens, brand) {
|
|
63
|
+
if (!brand) return tokens;
|
|
64
|
+
const next = JSON.parse(JSON.stringify(tokens));
|
|
65
|
+
for (const [scale, steps] of Object.entries(brand.color || {})) {
|
|
66
|
+
if (!next.color[scale]) throw new Error(`Brand "${brand.name}" sets unknown colour scale "${scale}". Known: ${Object.keys(scales(tokens)).join(', ')}.`);
|
|
67
|
+
for (const [step, hex] of Object.entries(steps)) {
|
|
68
|
+
if (!SCALE_STEPS.includes(step)) throw new Error(`Brand "${brand.name}" sets ${scale}.${step}; steps are ${SCALE_STEPS.join(', ')}.`);
|
|
69
|
+
next.color[scale][step] = { $value: hex };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
for (const [name, value] of Object.entries(brand.role || {})) {
|
|
73
|
+
if (!next.role[name]) throw new Error(`Brand "${brand.name}" sets unknown role "${name}". Known: ${Object.keys(roles(tokens)).join(', ')}.`);
|
|
74
|
+
// A plain string overrides light only; dark keeps the shared value unless {light, dark} is given.
|
|
75
|
+
const current = next.role[name];
|
|
76
|
+
const light = typeof value === 'string' ? value : value.light || current.$value;
|
|
77
|
+
const dark = typeof value === 'string' ? roles({ role: { [name]: current } })[name].dark : value.dark || roles({ role: { [name]: current } })[name].dark;
|
|
78
|
+
next.role[name] = { ...current, $value: light, $extensions: { [DARK_KEY]: dark } };
|
|
79
|
+
}
|
|
80
|
+
if (brand.font && brand.font.sans) next.font.family.sans = { $value: brand.font.sans };
|
|
81
|
+
return next;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const varName = (name, step) => (step ? `--dt-${name}-${step}` : `--dt-${name}`);
|
|
85
|
+
const colorRef = (name, step) => `rgb(var(${varName(name, step)}) / <alpha-value>)`;
|
|
86
|
+
|
|
87
|
+
/** Tailwind 3 `colors` object whose values point at the --dt-* variables. */
|
|
88
|
+
function tailwindColors(tokens) {
|
|
89
|
+
const out = {};
|
|
90
|
+
for (const name of Object.keys(scales(tokens))) {
|
|
91
|
+
out[name] = Object.fromEntries(SCALE_STEPS.map((s) => [s, colorRef(name, s)]));
|
|
92
|
+
}
|
|
93
|
+
for (const name of Object.keys(roles(tokens))) out[name] = colorRef(name);
|
|
94
|
+
return out;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** CSS declarations for :root (light) and the dark selectors, as { light: {var: channels}, dark: {...} }. */
|
|
98
|
+
function cssVariables(tokens, { includeScales = true } = {}) {
|
|
99
|
+
const light = {};
|
|
100
|
+
const dark = {};
|
|
101
|
+
if (includeScales) {
|
|
102
|
+
for (const [name, steps] of Object.entries(scales(tokens))) {
|
|
103
|
+
for (const [step, hex] of Object.entries(steps)) light[varName(name, step)] = hexToChannels(hex);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
for (const [name, r] of Object.entries(roles(tokens))) {
|
|
107
|
+
light[varName(name)] = hexToChannels(r.light);
|
|
108
|
+
dark[varName(name)] = hexToChannels(r.dark);
|
|
109
|
+
}
|
|
110
|
+
return { light, dark };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const DARK_SELECTOR = '.dark, [data-theme="dark"]';
|
|
114
|
+
|
|
115
|
+
module.exports = {
|
|
116
|
+
SCALE_STEPS,
|
|
117
|
+
DARK_SELECTOR,
|
|
118
|
+
loadTokens,
|
|
119
|
+
loadBrand,
|
|
120
|
+
listBrands,
|
|
121
|
+
applyBrand,
|
|
122
|
+
hexToChannels,
|
|
123
|
+
scales,
|
|
124
|
+
roles,
|
|
125
|
+
tailwindColors,
|
|
126
|
+
cssVariables,
|
|
127
|
+
varName,
|
|
128
|
+
};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$description": "Dreamtree design tokens (W3C Design Tokens Community Group format, 2025.10). Source of truth for twreact-ui's CSS variables, the Tailwind 3 preset, the Tailwind 4 theme and DESIGN.md. Products override only a brand file in tokens/brands/. Dark values live in $extensions[\"org.dreamtree.dark\"].",
|
|
3
|
+
"color": {
|
|
4
|
+
"$type": "color",
|
|
5
|
+
"primary": {
|
|
6
|
+
"$description": "The one accent: primary actions, active navigation, current step, progress, links and the focus ring. Default is Tailwind blue, which twreact-ui has always shipped.",
|
|
7
|
+
"50": { "$value": "#eff6ff" },
|
|
8
|
+
"100": { "$value": "#dbeafe" },
|
|
9
|
+
"200": { "$value": "#bfdbfe" },
|
|
10
|
+
"300": { "$value": "#93c5fd" },
|
|
11
|
+
"400": { "$value": "#60a5fa" },
|
|
12
|
+
"500": { "$value": "#3b82f6" },
|
|
13
|
+
"600": { "$value": "#2563eb" },
|
|
14
|
+
"700": { "$value": "#1d4ed8" },
|
|
15
|
+
"800": { "$value": "#1e40af" },
|
|
16
|
+
"900": { "$value": "#1e3a8a" },
|
|
17
|
+
"950": { "$value": "#172554" }
|
|
18
|
+
},
|
|
19
|
+
"secondary": {
|
|
20
|
+
"$description": "Neutral slate for secondary buttons, muted text and the default app shell.",
|
|
21
|
+
"50": { "$value": "#f8fafc" },
|
|
22
|
+
"100": { "$value": "#f1f5f9" },
|
|
23
|
+
"200": { "$value": "#e2e8f0" },
|
|
24
|
+
"300": { "$value": "#cbd5e1" },
|
|
25
|
+
"400": { "$value": "#94a3b8" },
|
|
26
|
+
"500": { "$value": "#64748b" },
|
|
27
|
+
"600": { "$value": "#475569" },
|
|
28
|
+
"700": { "$value": "#334155" },
|
|
29
|
+
"800": { "$value": "#1e293b" },
|
|
30
|
+
"900": { "$value": "#0f172a" },
|
|
31
|
+
"950": { "$value": "#020617" }
|
|
32
|
+
},
|
|
33
|
+
"success": {
|
|
34
|
+
"$description": "Completed, filed, accepted, healthy. Badges use the 100 fill with 800 text.",
|
|
35
|
+
"50": { "$value": "#f0fdf4" },
|
|
36
|
+
"100": { "$value": "#dcfce7" },
|
|
37
|
+
"200": { "$value": "#bbf7d0" },
|
|
38
|
+
"300": { "$value": "#86efac" },
|
|
39
|
+
"400": { "$value": "#4ade80" },
|
|
40
|
+
"500": { "$value": "#22c55e" },
|
|
41
|
+
"600": { "$value": "#16a34a" },
|
|
42
|
+
"700": { "$value": "#15803d" },
|
|
43
|
+
"800": { "$value": "#166534" },
|
|
44
|
+
"900": { "$value": "#14532d" },
|
|
45
|
+
"950": { "$value": "#052e16" }
|
|
46
|
+
},
|
|
47
|
+
"warning": {
|
|
48
|
+
"$description": "Needs attention soon: at risk, pending, in review.",
|
|
49
|
+
"50": { "$value": "#fffbeb" },
|
|
50
|
+
"100": { "$value": "#fef3c7" },
|
|
51
|
+
"200": { "$value": "#fde68a" },
|
|
52
|
+
"300": { "$value": "#fcd34d" },
|
|
53
|
+
"400": { "$value": "#fbbf24" },
|
|
54
|
+
"500": { "$value": "#f59e0b" },
|
|
55
|
+
"600": { "$value": "#d97706" },
|
|
56
|
+
"700": { "$value": "#b45309" },
|
|
57
|
+
"800": { "$value": "#92400e" },
|
|
58
|
+
"900": { "$value": "#78350f" },
|
|
59
|
+
"950": { "$value": "#451a03" }
|
|
60
|
+
},
|
|
61
|
+
"error": {
|
|
62
|
+
"$description": "Errors, overdue and destructive actions only. Never decoration.",
|
|
63
|
+
"50": { "$value": "#fef2f2" },
|
|
64
|
+
"100": { "$value": "#fee2e2" },
|
|
65
|
+
"200": { "$value": "#fecaca" },
|
|
66
|
+
"300": { "$value": "#fca5a5" },
|
|
67
|
+
"400": { "$value": "#f87171" },
|
|
68
|
+
"500": { "$value": "#ef4444" },
|
|
69
|
+
"600": { "$value": "#dc2626" },
|
|
70
|
+
"700": { "$value": "#b91c1c" },
|
|
71
|
+
"800": { "$value": "#991b1b" },
|
|
72
|
+
"900": { "$value": "#7f1d1d" },
|
|
73
|
+
"950": { "$value": "#450a0a" }
|
|
74
|
+
},
|
|
75
|
+
"info": {
|
|
76
|
+
"$description": "Informational and scheduled states that are not the brand accent.",
|
|
77
|
+
"50": { "$value": "#f0f9ff" },
|
|
78
|
+
"100": { "$value": "#e0f2fe" },
|
|
79
|
+
"200": { "$value": "#bae6fd" },
|
|
80
|
+
"300": { "$value": "#7dd3fc" },
|
|
81
|
+
"400": { "$value": "#38bdf8" },
|
|
82
|
+
"500": { "$value": "#0ea5e9" },
|
|
83
|
+
"600": { "$value": "#0284c7" },
|
|
84
|
+
"700": { "$value": "#0369a1" },
|
|
85
|
+
"800": { "$value": "#075985" },
|
|
86
|
+
"900": { "$value": "#0c4a6e" },
|
|
87
|
+
"950": { "$value": "#082f49" }
|
|
88
|
+
},
|
|
89
|
+
"gray": {
|
|
90
|
+
"$description": "Borders, dividers and quiet fills. Matches Tailwind gray so existing twreact-ui classes keep their look.",
|
|
91
|
+
"50": { "$value": "#f9fafb" },
|
|
92
|
+
"100": { "$value": "#f3f4f6" },
|
|
93
|
+
"200": { "$value": "#e5e7eb" },
|
|
94
|
+
"300": { "$value": "#d1d5db" },
|
|
95
|
+
"400": { "$value": "#9ca3af" },
|
|
96
|
+
"500": { "$value": "#6b7280" },
|
|
97
|
+
"600": { "$value": "#4b5563" },
|
|
98
|
+
"700": { "$value": "#374151" },
|
|
99
|
+
"800": { "$value": "#1f2937" },
|
|
100
|
+
"900": { "$value": "#111827" },
|
|
101
|
+
"950": { "$value": "#030712" }
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
"role": {
|
|
105
|
+
"$type": "color",
|
|
106
|
+
"$description": "Semantic surface and text roles. Screens use these (bg-surface, text-foreground, border-line) instead of picking palette steps.",
|
|
107
|
+
"background": { "$value": "#f8fafc", "$description": "Page background.", "$extensions": { "org.dreamtree.dark": "#0b1120" } },
|
|
108
|
+
"surface": { "$value": "#ffffff", "$description": "Cards, dialogs, menus.", "$extensions": { "org.dreamtree.dark": "#111827" } },
|
|
109
|
+
"foreground": { "$value": "#0f172a", "$description": "Body text and titles.", "$extensions": { "org.dreamtree.dark": "#e5e7eb" } },
|
|
110
|
+
"muted": { "$value": "#475569", "$description": "Subtitles, labels, captions (7.5:1 on white).", "$extensions": { "org.dreamtree.dark": "#94a3b8" } },
|
|
111
|
+
"line": { "$value": "#e2e8f0", "$description": "Card and divider borders.", "$extensions": { "org.dreamtree.dark": "#1f2937" } },
|
|
112
|
+
"shell": { "$value": "#0f172a", "$description": "App shell sidebar background.", "$extensions": { "org.dreamtree.dark": "#020617" } },
|
|
113
|
+
"shell-foreground": { "$value": "#cbd5e1", "$description": "Sidebar text.", "$extensions": { "org.dreamtree.dark": "#cbd5e1" } },
|
|
114
|
+
"shell-active": { "$value": "#1e293b", "$description": "Active sidebar item fill.", "$extensions": { "org.dreamtree.dark": "#1e293b" } }
|
|
115
|
+
},
|
|
116
|
+
"font": {
|
|
117
|
+
"family": {
|
|
118
|
+
"$type": "fontFamily",
|
|
119
|
+
"sans": { "$value": ["Inter", "system-ui", "-apple-system", "Segoe UI", "Roboto", "Arial", "sans-serif"] },
|
|
120
|
+
"mono": { "$value": ["JetBrains Mono", "ui-monospace", "SFMono-Regular", "Menlo", "monospace"] }
|
|
121
|
+
},
|
|
122
|
+
"size": {
|
|
123
|
+
"$type": "dimension",
|
|
124
|
+
"$description": "Type scale. micro replaces the 1,445 hand-written text-[10px]/text-[11px] found across products.",
|
|
125
|
+
"micro": { "$value": { "value": 0.6875, "unit": "rem" }, "$extensions": { "org.dreamtree.lineHeight": "1rem" } }
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
"radius": {
|
|
129
|
+
"$type": "dimension",
|
|
130
|
+
"control": { "$value": { "value": 0.5, "unit": "rem" }, "$description": "Buttons, inputs, menus." },
|
|
131
|
+
"card": { "$value": { "value": 0.75, "unit": "rem" }, "$description": "Cards and dialogs." }
|
|
132
|
+
}
|
|
133
|
+
}
|