swatchkit 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/blueprints/colors.json +11 -0
- package/src/blueprints/compositions/cluster.css +20 -0
- package/src/blueprints/compositions/flow.css +10 -0
- package/src/blueprints/compositions/grid.css +36 -0
- package/src/blueprints/compositions/index.css +12 -0
- package/src/blueprints/compositions/prose.css +89 -0
- package/src/blueprints/compositions/repel.css +23 -0
- package/src/blueprints/compositions/sidebar.css +44 -0
- package/src/blueprints/compositions/switcher.css +32 -0
- package/src/blueprints/compositions/wrapper.css +14 -0
- package/src/blueprints/fonts.json +24 -0
- package/src/blueprints/global/elements.css +609 -0
- package/src/blueprints/global/index.css +13 -0
- package/src/blueprints/global/reset.css +91 -0
- package/src/blueprints/global/variables.css +58 -0
- package/src/blueprints/main.css +27 -0
- package/src/blueprints/spacing.json +19 -0
- package/src/blueprints/swatchkit-ui.css +79 -0
- package/src/blueprints/text-leading.json +13 -0
- package/src/blueprints/text-sizes.json +21 -0
- package/src/blueprints/text-weights.json +11 -0
- package/src/blueprints/utilities/index.css +9 -0
- package/src/blueprints/utilities/region.css +12 -0
- package/src/blueprints/utilities/visually-hidden.css +18 -0
- package/src/blueprints/viewports.json +10 -0
- package/src/generators/index.js +401 -0
- package/src/preview-layout.html +14 -0
- package/src/templates/compositions/cluster/description.html +7 -0
- package/src/templates/compositions/cluster/index.html +7 -0
- package/src/templates/compositions/flow/description.html +8 -0
- package/src/templates/compositions/flow/index.html +6 -0
- package/src/templates/compositions/grid/description.html +12 -0
- package/src/templates/compositions/grid/index.html +7 -0
- package/src/templates/compositions/prose/description.html +8 -0
- package/src/templates/compositions/prose/index.html +6 -0
- package/src/templates/compositions/repel/description.html +9 -0
- package/src/templates/compositions/repel/index.html +4 -0
- package/src/templates/compositions/sidebar/description.html +13 -0
- package/src/templates/compositions/sidebar/index.html +4 -0
- package/src/templates/compositions/switcher/description.html +8 -0
- package/src/templates/compositions/switcher/index.html +29 -0
- package/src/templates/compositions/wrapper/description.html +8 -0
- package/src/templates/compositions/wrapper/index.html +5 -0
- package/src/templates/prose.html +366 -0
- package/src/templates/script.js +44 -0
- package/src/templates/utilities/region/description.html +8 -0
- package/src/templates/utilities/region/index.html +5 -0
- package/src/templates/utilities/visually-hidden/description.html +7 -0
- package/src/templates/utilities/visually-hidden/index.html +7 -0
- package/src/tokens.js +428 -0
- package/src/utils/clamp-generator.js +38 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SwatchKit Variables
|
|
3
|
+
*
|
|
4
|
+
* Utility custom properties that extend your design tokens.
|
|
5
|
+
* These provide consistent values for layouts, interactions, and UI details.
|
|
6
|
+
* This file is yours to edit — add your own variables here freely.
|
|
7
|
+
*
|
|
8
|
+
* Variable references use the default token names from tokens/*.json.
|
|
9
|
+
* If you have renamed any tokens, update the var() references below to match.
|
|
10
|
+
*
|
|
11
|
+
* Based on Andy Bell's Complete CSS course methodology.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
:root {
|
|
15
|
+
/**
|
|
16
|
+
* Gutter: Used by all layout compositions for consistent spacing.
|
|
17
|
+
* Assigns a fluid spacing token so gutters scale with viewport.
|
|
18
|
+
*/
|
|
19
|
+
--gutter: var(--space-m-l);
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Measure: Maximum line length for optimal readability.
|
|
23
|
+
* 65 characters is widely considered ideal for body text.
|
|
24
|
+
*/
|
|
25
|
+
--measure: 65ch;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Transitions: Specific timing for different interaction types.
|
|
29
|
+
* Movement should be snappy and linear; fades need time to be visible;
|
|
30
|
+
* bounce adds playful emphasis for special interactions.
|
|
31
|
+
*/
|
|
32
|
+
--transition-base: 250ms ease;
|
|
33
|
+
--transition-movement: 200ms linear;
|
|
34
|
+
--transition-fade: 300ms ease;
|
|
35
|
+
--transition-bounce: 500ms cubic-bezier(0.5, 0.05, 0.2, 1.5);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Strokes: Consistent border values throughout the UI.
|
|
39
|
+
* Having both dashed and solid variants covers most use cases.
|
|
40
|
+
*/
|
|
41
|
+
--stroke-width: 1px;
|
|
42
|
+
--stroke: var(--stroke-width) dashed var(--color-mid);
|
|
43
|
+
--stroke-solid: var(--stroke-width) solid var(--color-mid);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Border Radius: Using rem (not em) prevents radius from varying
|
|
47
|
+
* based on element font-size, keeping corners consistent site-wide.
|
|
48
|
+
*/
|
|
49
|
+
--radius-s: 0.2rem;
|
|
50
|
+
--radius-m: 0.75rem;
|
|
51
|
+
--radius-l: 2.5rem;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Uppercase Kerning: Uppercase text looks squished without extra
|
|
55
|
+
* letter-spacing. This variable fixes that consistently site-wide.
|
|
56
|
+
*/
|
|
57
|
+
--uppercase-kerning: 0.04ch;
|
|
58
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SwatchKit Main Stylesheet
|
|
3
|
+
*
|
|
4
|
+
* This is your app's CSS entry point. It imports design tokens, reset,
|
|
5
|
+
* global styles, and compositions. Edit freely — this file is yours.
|
|
6
|
+
* Note: swatchkit init --force will overwrite blueprint files (with .bak backups).
|
|
7
|
+
* The only truly auto-generated files are css/global/tokens.css and
|
|
8
|
+
* css/utilities/tokens.css — do not edit those manually.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/*
|
|
12
|
+
* Import Global Styles (Reset, Tokens, Variables, Elements)
|
|
13
|
+
*
|
|
14
|
+
* NOTE: Some global styles (variables & elements) are DISABLED by default.
|
|
15
|
+
* You must open `css/global/index.css` to verify variable names and enable them.
|
|
16
|
+
*/
|
|
17
|
+
@import "global/index.css";
|
|
18
|
+
|
|
19
|
+
/* Import layout compositions */
|
|
20
|
+
@import "compositions/index.css";
|
|
21
|
+
|
|
22
|
+
/* Import utilities */
|
|
23
|
+
@import "utilities/index.css";
|
|
24
|
+
|
|
25
|
+
/* === Your App Styles === */
|
|
26
|
+
|
|
27
|
+
/* Add your component styles below */
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Spacing",
|
|
3
|
+
"description": "Consistent spacing sizes, based on a ratio, with min and max sizes. This allows you to set spacing based on the context size. For example, min for mobile and max for desktop browsers.",
|
|
4
|
+
"items": [
|
|
5
|
+
{ "name": "space-xs", "min": 7, "max": 8 },
|
|
6
|
+
{ "name": "space-s", "min": 14, "max": 16 },
|
|
7
|
+
{ "name": "space-m", "min": 21, "max": 24 },
|
|
8
|
+
{ "name": "space-l", "min": 35, "max": 40 },
|
|
9
|
+
{ "name": "space-xl", "min": 49, "max": 56 },
|
|
10
|
+
{ "name": "space-2xl", "min": 56, "max": 64 },
|
|
11
|
+
{ "name": "space-3xl", "min": 70, "max": 80 },
|
|
12
|
+
{ "name": "space-4xl", "min": 105, "max": 120 },
|
|
13
|
+
{ "name": "space-s-l", "min": 14, "max": 40 },
|
|
14
|
+
{ "name": "space-xs-l", "min": 7, "max": 40 },
|
|
15
|
+
{ "name": "space-m-l", "min": 21, "max": 40 },
|
|
16
|
+
{ "name": "space-s-m", "min": 14, "max": 24 },
|
|
17
|
+
{ "name": "space-2xl-4xl", "min": 56, "max": 120 }
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/* SwatchKit UI Styles */
|
|
2
|
+
|
|
3
|
+
:root {
|
|
4
|
+
/* trigger intrinsic sizing by invalidating the flex-basis value */
|
|
5
|
+
--sidebar-target-width: -1rem;
|
|
6
|
+
--sidebar-content-min-width: 66%;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
body {
|
|
10
|
+
margin: 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/* nav { */
|
|
14
|
+
/* /* TODO: consider a utility class here */
|
|
15
|
+
*/
|
|
16
|
+
/* padding-block-start: var(--space-l); */
|
|
17
|
+
/* outline: 2px solid var(--color-dark-glare); */
|
|
18
|
+
/* background-color: var(--color-mid); */
|
|
19
|
+
/* } */
|
|
20
|
+
|
|
21
|
+
main {
|
|
22
|
+
/* TODO: consider a utility class here */
|
|
23
|
+
padding-block-start: var(--space-l);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* Sidebar Navigation */
|
|
27
|
+
/* .swatchkit-nav { */
|
|
28
|
+
/* background-color: var(--color-light); */
|
|
29
|
+
/* border-right: 1px solid var(--color-mid); */
|
|
30
|
+
/* } */
|
|
31
|
+
|
|
32
|
+
.swatchkit-nav h2 {
|
|
33
|
+
font-size: var(--text-size-1, 1.25rem);
|
|
34
|
+
font-weight: var(--font-weight-bold, 700);
|
|
35
|
+
color: var(--color-dark);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.swatchkit-nav a {
|
|
39
|
+
display: block;
|
|
40
|
+
text-decoration: none;
|
|
41
|
+
color: var(--color-dark-glare, #333);
|
|
42
|
+
padding-block: var(--space-xs, 0.5rem);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.swatchkit-nav a:hover {
|
|
46
|
+
color: var(--color-primary);
|
|
47
|
+
text-decoration: underline;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* Pattern Preview Blocks */
|
|
51
|
+
.swatchkit-pattern {
|
|
52
|
+
margin-bottom: var(--space-2xl);
|
|
53
|
+
border-bottom: 1px solid var(--color-mid);
|
|
54
|
+
padding-bottom: var(--space-l);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.swatchkit-pattern h2 {
|
|
58
|
+
margin-top: 0;
|
|
59
|
+
font-size: var(--text-size-2, 1.5rem);
|
|
60
|
+
color: var(--color-dark);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.swatchkit-preview {
|
|
64
|
+
border: 1px solid var(--color-mid);
|
|
65
|
+
padding: var(--space-l);
|
|
66
|
+
border-radius: 4px;
|
|
67
|
+
background: var(--color-light);
|
|
68
|
+
margin-bottom: var(--space-m);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.swatchkit-code {
|
|
72
|
+
background: var(--color-dark);
|
|
73
|
+
color: var(--color-light);
|
|
74
|
+
padding: var(--space-m);
|
|
75
|
+
border-radius: 4px;
|
|
76
|
+
overflow-x: auto;
|
|
77
|
+
font-family: var(--font-mono, monospace);
|
|
78
|
+
font-size: 0.875rem;
|
|
79
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Text Leading",
|
|
3
|
+
"description": "Modular scale for line-heights",
|
|
4
|
+
"base": 1,
|
|
5
|
+
"ratio": 1.2,
|
|
6
|
+
"items": [
|
|
7
|
+
{ "name": "leading-micro", "step": -1 },
|
|
8
|
+
{ "name": "leading-flat", "step": 0 },
|
|
9
|
+
{ "name": "leading-fine", "step": 1 },
|
|
10
|
+
{ "name": "leading-standard", "step": 2 },
|
|
11
|
+
{ "name": "leading-loose", "step": 3 }
|
|
12
|
+
]
|
|
13
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Text Sizes",
|
|
3
|
+
"description": "Fluid typography steps generated using Utopia clamp() methodology.",
|
|
4
|
+
"items": [
|
|
5
|
+
{ "name": "step--2", "min": 11.85, "max": 12.5 },
|
|
6
|
+
{ "name": "step--1", "min": 13.33, "max": 15 },
|
|
7
|
+
{ "name": "step-0", "min": 15, "max": 18 },
|
|
8
|
+
{ "name": "step-1", "min": 16.88, "max": 21.6 },
|
|
9
|
+
{ "name": "step-2", "min": 18.98, "max": 25.92 },
|
|
10
|
+
{ "name": "step-3", "min": 21.36, "max": 31.1 },
|
|
11
|
+
{ "name": "step-4", "min": 24.03, "max": 37.32 },
|
|
12
|
+
{ "name": "step-5", "min": 27.03, "max": 44.79 },
|
|
13
|
+
{ "name": "step-6", "min": 30.41, "max": 53.75 },
|
|
14
|
+
{ "name": "step-7", "min": 34.21, "max": 64.5 },
|
|
15
|
+
{ "name": "step-8", "min": 38.49, "max": 77.4 },
|
|
16
|
+
{ "name": "step-9", "min": 43.3, "max": 92.88 },
|
|
17
|
+
{ "name": "step-10", "min": 48.5, "max": 111.45 },
|
|
18
|
+
{ "name": "step-11", "min": 60.62, "max": 167.17 },
|
|
19
|
+
{ "name": "step-12", "min": 60.62, "max": 230 }
|
|
20
|
+
]
|
|
21
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Text Weights",
|
|
3
|
+
"description": "Helper classes and custom properties for common font weights",
|
|
4
|
+
"meta": {},
|
|
5
|
+
"items": [
|
|
6
|
+
{ "name": "weight-regular", "value": 400 },
|
|
7
|
+
{ "name": "weight-medium", "value": 500 },
|
|
8
|
+
{ "name": "weight-bold", "value": 700 },
|
|
9
|
+
{ "name": "weight-black", "value": 900 }
|
|
10
|
+
]
|
|
11
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/*
|
|
2
|
+
A container that provides consistent vertical spacing for
|
|
3
|
+
chunks/sections/regions of content. Also a relative position container.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
.region {
|
|
7
|
+
padding-block: var(--region-space, var(--space-m));
|
|
8
|
+
position: relative;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.region[data-disable-space-top] {
|
|
12
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Visually Hidden Utility
|
|
3
|
+
|
|
4
|
+
Hides content visually while keeping it accessible to assistive technologies
|
|
5
|
+
(like screen readers). Use this instead of `display: none` when you want
|
|
6
|
+
content to be available to users who rely on non-visual browsing.
|
|
7
|
+
*/
|
|
8
|
+
.visually-hidden {
|
|
9
|
+
border: 0;
|
|
10
|
+
clip: rect(0 0 0 0);
|
|
11
|
+
height: 0;
|
|
12
|
+
margin: 0;
|
|
13
|
+
overflow: hidden;
|
|
14
|
+
padding: 0;
|
|
15
|
+
position: absolute;
|
|
16
|
+
width: 1px;
|
|
17
|
+
white-space: nowrap;
|
|
18
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Viewports",
|
|
3
|
+
"description": "The min and maximum viewports used to generate fluid type and space scales.",
|
|
4
|
+
"items": [
|
|
5
|
+
{ "name": "viewport-min", "value": 330 },
|
|
6
|
+
{ "name": "viewport-mid", "value": 760 },
|
|
7
|
+
{ "name": "viewport-large", "value": 940 },
|
|
8
|
+
{ "name": "viewport-max", "value": 1350 }
|
|
9
|
+
]
|
|
10
|
+
}
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token HTML Generators
|
|
3
|
+
*
|
|
4
|
+
* These functions read JSON token files and generate HTML
|
|
5
|
+
* for the SwatchKit UI that references the actual token names.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Read and parse a JSON token file
|
|
13
|
+
*/
|
|
14
|
+
function readTokenFile(tokensDir, filename) {
|
|
15
|
+
const filePath = path.join(tokensDir, filename);
|
|
16
|
+
if (!fs.existsSync(filePath)) return null;
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
20
|
+
return JSON.parse(content);
|
|
21
|
+
} catch (error) {
|
|
22
|
+
console.error(`[SwatchKit] Error reading ${filename}:`, error.message);
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Generate colors.html from colors.json
|
|
29
|
+
*/
|
|
30
|
+
function generateColors(tokensDir) {
|
|
31
|
+
const data = readTokenFile(tokensDir, 'colors.json');
|
|
32
|
+
if (!data || !data.items) return null;
|
|
33
|
+
|
|
34
|
+
const rows = data.items.map(item => {
|
|
35
|
+
const varName = `--${item.name}`;
|
|
36
|
+
return ` <tr>
|
|
37
|
+
<td>
|
|
38
|
+
<div style="display: flex; align-items: center; gap: 0.5rem; flex-wrap: nowrap;">
|
|
39
|
+
<div style="background: ${item.value}; width: 2rem; height: 2rem; border-radius: 4px; border: 1px solid rgba(0,0,0,0.1); flex-shrink: 0;" role="presentation"></div>
|
|
40
|
+
<strong>${item.name}</strong>
|
|
41
|
+
</div>
|
|
42
|
+
</td>
|
|
43
|
+
<td><code>${item.value}</code></td>
|
|
44
|
+
<td><code>var(${varName})</code></td>
|
|
45
|
+
<td><code>.color:${item.name}</code></td>
|
|
46
|
+
<td><code>.background-color:${item.name}</code></td>
|
|
47
|
+
</tr>`;
|
|
48
|
+
}).join('\n');
|
|
49
|
+
|
|
50
|
+
return `<style>
|
|
51
|
+
.color-table {
|
|
52
|
+
width: 100%;
|
|
53
|
+
border-collapse: collapse;
|
|
54
|
+
font-size: 0.9rem;
|
|
55
|
+
}
|
|
56
|
+
.color-table th,
|
|
57
|
+
.color-table td {
|
|
58
|
+
text-align: left;
|
|
59
|
+
padding: 0.5rem 0.75rem;
|
|
60
|
+
border-bottom: 1px solid #ddd;
|
|
61
|
+
vertical-align: middle;
|
|
62
|
+
}
|
|
63
|
+
.color-table th {
|
|
64
|
+
font-weight: bold;
|
|
65
|
+
background: #f5f5f5;
|
|
66
|
+
}
|
|
67
|
+
.color-table code {
|
|
68
|
+
font-family: monospace;
|
|
69
|
+
}
|
|
70
|
+
</style>
|
|
71
|
+
|
|
72
|
+
<table class="color-table">
|
|
73
|
+
<thead>
|
|
74
|
+
<tr>
|
|
75
|
+
<th>Name</th>
|
|
76
|
+
<th>Hex code</th>
|
|
77
|
+
<th>Custom Property</th>
|
|
78
|
+
<th>Color Utility Class</th>
|
|
79
|
+
<th>BG Utility Class</th>
|
|
80
|
+
</tr>
|
|
81
|
+
</thead>
|
|
82
|
+
<tbody>
|
|
83
|
+
${rows}
|
|
84
|
+
</tbody>
|
|
85
|
+
</table>`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Generate typography.html from text-sizes.json
|
|
90
|
+
*/
|
|
91
|
+
function generateTypography(tokensDir) {
|
|
92
|
+
const data = readTokenFile(tokensDir, 'text-sizes.json');
|
|
93
|
+
if (!data || !data.items) return null;
|
|
94
|
+
|
|
95
|
+
const steps = data.items.map(item => {
|
|
96
|
+
const varName = `--${item.name}`;
|
|
97
|
+
return ` <div style="font-size: var(${varName})">${item.name} <span class="token-value" data-var="${varName}"></span></div>`;
|
|
98
|
+
}).join('\n');
|
|
99
|
+
|
|
100
|
+
return `<style>
|
|
101
|
+
.type-ladder > * {
|
|
102
|
+
margin-bottom: 1.5rem;
|
|
103
|
+
line-height: 1.2;
|
|
104
|
+
}
|
|
105
|
+
.type-ladder .token-value {
|
|
106
|
+
display: block;
|
|
107
|
+
font-size: 0.875rem;
|
|
108
|
+
font-weight: normal;
|
|
109
|
+
color: #666;
|
|
110
|
+
margin-top: 0.25rem;
|
|
111
|
+
font-family: monospace;
|
|
112
|
+
}
|
|
113
|
+
</style>
|
|
114
|
+
<div class="type-ladder">
|
|
115
|
+
${steps}
|
|
116
|
+
</div>`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Generate spacing.html from spacing.json
|
|
121
|
+
*/
|
|
122
|
+
function generateSpacing(tokensDir) {
|
|
123
|
+
const data = readTokenFile(tokensDir, 'spacing.json');
|
|
124
|
+
if (!data || !data.items) return null;
|
|
125
|
+
|
|
126
|
+
const scaleRows = data.items.map(item => {
|
|
127
|
+
const varName = `--${item.name}`;
|
|
128
|
+
const minVal = item.min !== undefined ? `${item.min}px` : (item.value || '—');
|
|
129
|
+
const maxVal = item.max !== undefined ? `${item.max}px` : (item.value || '—');
|
|
130
|
+
return ` <tr>
|
|
131
|
+
<td>
|
|
132
|
+
<div class="spacing-swatch" style="height: var(${varName});"></div>
|
|
133
|
+
</td>
|
|
134
|
+
<td><strong>${item.name}</strong></td>
|
|
135
|
+
<td><code>var(${varName})</code></td>
|
|
136
|
+
<td>${minVal}</td>
|
|
137
|
+
<td>${maxVal}</td>
|
|
138
|
+
</tr>`;
|
|
139
|
+
}).join('\n');
|
|
140
|
+
|
|
141
|
+
const usageRows = data.items.map(item => {
|
|
142
|
+
const slug = item.name;
|
|
143
|
+
return ` <tr>
|
|
144
|
+
<td><code>var(--${slug})</code></td>
|
|
145
|
+
<td><code>.padding-block:${slug}</code></td>
|
|
146
|
+
<td><code>.padding-inline:${slug}</code></td>
|
|
147
|
+
<td><code>.margin-block:${slug}</code></td>
|
|
148
|
+
<td><code>.flow-space:${slug}</code></td>
|
|
149
|
+
<td><code>.gutter:${slug}</code></td>
|
|
150
|
+
<td><code>.region-space:${slug}</code></td>
|
|
151
|
+
</tr>`;
|
|
152
|
+
}).join('\n');
|
|
153
|
+
|
|
154
|
+
return `<style>
|
|
155
|
+
.spacing-table {
|
|
156
|
+
width: 100%;
|
|
157
|
+
border-collapse: collapse;
|
|
158
|
+
font-size: 0.9rem;
|
|
159
|
+
margin-bottom: 2rem;
|
|
160
|
+
}
|
|
161
|
+
.spacing-table th,
|
|
162
|
+
.spacing-table td {
|
|
163
|
+
text-align: left;
|
|
164
|
+
padding: 0.5rem 0.75rem;
|
|
165
|
+
border-bottom: 1px solid #ddd;
|
|
166
|
+
vertical-align: middle;
|
|
167
|
+
}
|
|
168
|
+
.spacing-table th {
|
|
169
|
+
font-weight: bold;
|
|
170
|
+
background: #f5f5f5;
|
|
171
|
+
}
|
|
172
|
+
.spacing-table code {
|
|
173
|
+
font-family: monospace;
|
|
174
|
+
}
|
|
175
|
+
.spacing-swatch {
|
|
176
|
+
background: var(--color-primary, #666);
|
|
177
|
+
min-height: 4px;
|
|
178
|
+
width: 100%;
|
|
179
|
+
min-width: 4px;
|
|
180
|
+
border-radius: 2px;
|
|
181
|
+
}
|
|
182
|
+
</style>
|
|
183
|
+
|
|
184
|
+
<h2 style="font-family: monospace; margin-bottom: 0.5rem;">Scale</h2>
|
|
185
|
+
<table class="spacing-table">
|
|
186
|
+
<thead>
|
|
187
|
+
<tr>
|
|
188
|
+
<th width="120px"></th>
|
|
189
|
+
<th>Name</th>
|
|
190
|
+
<th>Custom Property</th>
|
|
191
|
+
<th>Min</th>
|
|
192
|
+
<th>Max</th>
|
|
193
|
+
</tr>
|
|
194
|
+
</thead>
|
|
195
|
+
<tbody>
|
|
196
|
+
${scaleRows}
|
|
197
|
+
</tbody>
|
|
198
|
+
</table>
|
|
199
|
+
|
|
200
|
+
<h2 style="font-family: monospace; margin-bottom: 0.5rem;">Usage</h2>
|
|
201
|
+
<table class="spacing-table">
|
|
202
|
+
<thead>
|
|
203
|
+
<tr>
|
|
204
|
+
<th>Custom Property</th>
|
|
205
|
+
<th>Padding Block</th>
|
|
206
|
+
<th>Padding Inline</th>
|
|
207
|
+
<th>Margin Block</th>
|
|
208
|
+
<th>Flow Space</th>
|
|
209
|
+
<th>Gutter</th>
|
|
210
|
+
<th>Region Space</th>
|
|
211
|
+
</tr>
|
|
212
|
+
</thead>
|
|
213
|
+
<tbody>
|
|
214
|
+
${usageRows}
|
|
215
|
+
</tbody>
|
|
216
|
+
</table>`;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Generate fonts.html from fonts.json
|
|
221
|
+
*/
|
|
222
|
+
function generateFonts(tokensDir) {
|
|
223
|
+
const data = readTokenFile(tokensDir, 'fonts.json');
|
|
224
|
+
if (!data || !data.items) return null;
|
|
225
|
+
|
|
226
|
+
const stacks = data.items.map(item => {
|
|
227
|
+
const varName = `--${item.name}`;
|
|
228
|
+
return ` <div style="font-family: var(${varName}); margin-bottom: 2rem;">
|
|
229
|
+
<strong>${item.name}</strong> <code class="token-value" data-var="${varName}">var(${varName})</code>
|
|
230
|
+
<p>The quick brown fox jumps over the lazy dog.</p>
|
|
231
|
+
</div>`;
|
|
232
|
+
}).join('\n');
|
|
233
|
+
|
|
234
|
+
return `<div class="font-stack">
|
|
235
|
+
${stacks}
|
|
236
|
+
</div>`;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Generate text-weights.html from text-weights.json
|
|
241
|
+
*/
|
|
242
|
+
function generateTextWeights(tokensDir) {
|
|
243
|
+
const data = readTokenFile(tokensDir, 'text-weights.json');
|
|
244
|
+
if (!data || !data.items) return null;
|
|
245
|
+
|
|
246
|
+
const weights = data.items.map(item => {
|
|
247
|
+
const varName = `--${item.name}`;
|
|
248
|
+
return ` <div style="font-weight: var(${varName})">
|
|
249
|
+
${item.name} (${item.value}) - The quick brown fox jumps over the lazy dog.
|
|
250
|
+
</div>`;
|
|
251
|
+
}).join('\n');
|
|
252
|
+
|
|
253
|
+
return `<div style="font-family: sans-serif; display: grid; gap: 1rem;">
|
|
254
|
+
${weights}
|
|
255
|
+
</div>`;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Generate text-leading.html from text-leading.json
|
|
260
|
+
*/
|
|
261
|
+
function generateTextLeading(tokensDir) {
|
|
262
|
+
const data = readTokenFile(tokensDir, 'text-leading.json');
|
|
263
|
+
if (!data || !data.items) return null;
|
|
264
|
+
|
|
265
|
+
const loremText = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.`;
|
|
266
|
+
|
|
267
|
+
const leadings = data.items.map(item => {
|
|
268
|
+
const varName = `--${item.name}`;
|
|
269
|
+
return ` <div style="line-height: var(${varName})">
|
|
270
|
+
<div class="meta">
|
|
271
|
+
<strong>${item.name}</strong>
|
|
272
|
+
<code>var(${varName})</code>
|
|
273
|
+
<span class="token-value" data-var="${varName}"></span>
|
|
274
|
+
</div>
|
|
275
|
+
<p>${loremText}</p>
|
|
276
|
+
</div>`;
|
|
277
|
+
}).join('\n\n');
|
|
278
|
+
|
|
279
|
+
return `<div class="flow">
|
|
280
|
+
${leadings}
|
|
281
|
+
</div>
|
|
282
|
+
|
|
283
|
+
<style>
|
|
284
|
+
.meta {
|
|
285
|
+
font-family: monospace;
|
|
286
|
+
font-size: 0.85rem;
|
|
287
|
+
margin-bottom: 0.5rem;
|
|
288
|
+
background: #eee;
|
|
289
|
+
padding: 0.25rem 0.5rem;
|
|
290
|
+
display: inline-block;
|
|
291
|
+
border-radius: 4px;
|
|
292
|
+
}
|
|
293
|
+
.token-value {
|
|
294
|
+
color: #666;
|
|
295
|
+
margin-left: 0.5rem;
|
|
296
|
+
}
|
|
297
|
+
</style>`;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Generate viewports.html from viewports.json
|
|
302
|
+
*/
|
|
303
|
+
function generateViewports(tokensDir) {
|
|
304
|
+
const data = readTokenFile(tokensDir, 'viewports.json');
|
|
305
|
+
if (!data || !data.items) return null;
|
|
306
|
+
|
|
307
|
+
const items = data.items.map(item => {
|
|
308
|
+
const varName = `--${item.name}`;
|
|
309
|
+
const displayValue = typeof item.value === 'number' ? `${item.value}px` : item.value;
|
|
310
|
+
const barWidth = typeof item.value === 'number' ? `${item.value}px` : '100%';
|
|
311
|
+
return ` <div class="viewport-item">
|
|
312
|
+
<div class="viewport-bar" style="width: ${barWidth}">
|
|
313
|
+
<span class="viewport-meta">
|
|
314
|
+
<strong>${item.name}</strong>
|
|
315
|
+
<code>var(${varName})</code>
|
|
316
|
+
<span class="viewport-value">${displayValue}</span>
|
|
317
|
+
</span>
|
|
318
|
+
</div>
|
|
319
|
+
</div>`;
|
|
320
|
+
}).join('\n');
|
|
321
|
+
|
|
322
|
+
return `<style>
|
|
323
|
+
.viewport-list {
|
|
324
|
+
display: flex;
|
|
325
|
+
flex-direction: column;
|
|
326
|
+
gap: 0.5rem;
|
|
327
|
+
font-family: monospace;
|
|
328
|
+
}
|
|
329
|
+
.viewport-item {
|
|
330
|
+
display: flex;
|
|
331
|
+
}
|
|
332
|
+
.viewport-bar {
|
|
333
|
+
background: var(--color-primary, #666);
|
|
334
|
+
border-radius: 4px;
|
|
335
|
+
padding: 0.5rem 0.75rem;
|
|
336
|
+
min-width: max-content;
|
|
337
|
+
flex-shrink: 0;
|
|
338
|
+
}
|
|
339
|
+
.viewport-meta {
|
|
340
|
+
display: flex;
|
|
341
|
+
align-items: center;
|
|
342
|
+
gap: 1rem;
|
|
343
|
+
font-size: 0.9rem;
|
|
344
|
+
color: var(--color-light, #fff);
|
|
345
|
+
white-space: nowrap;
|
|
346
|
+
}
|
|
347
|
+
.viewport-meta code {
|
|
348
|
+
opacity: 0.75;
|
|
349
|
+
}
|
|
350
|
+
.viewport-value {
|
|
351
|
+
font-weight: bold;
|
|
352
|
+
margin-left: auto;
|
|
353
|
+
padding-left: 2rem;
|
|
354
|
+
}
|
|
355
|
+
</style>
|
|
356
|
+
<div class="viewport-list">
|
|
357
|
+
${items}
|
|
358
|
+
</div>`;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Generate all token HTML files and write them to the tokens UI directory
|
|
363
|
+
*/
|
|
364
|
+
function generateTokenSwatches(tokensDir, tokensUiDir) {
|
|
365
|
+
const generators = [
|
|
366
|
+
{ filename: 'colors.html', fn: generateColors },
|
|
367
|
+
{ filename: 'typography.html', fn: generateTypography },
|
|
368
|
+
{ filename: 'spacing.html', fn: generateSpacing },
|
|
369
|
+
{ filename: 'fonts.html', fn: generateFonts },
|
|
370
|
+
{ filename: 'text-weights.html', fn: generateTextWeights },
|
|
371
|
+
{ filename: 'text-leading.html', fn: generateTextLeading },
|
|
372
|
+
{ filename: 'viewports.html', fn: generateViewports },
|
|
373
|
+
];
|
|
374
|
+
|
|
375
|
+
let generated = 0;
|
|
376
|
+
|
|
377
|
+
generators.forEach(({ filename, fn }) => {
|
|
378
|
+
const html = fn(tokensDir);
|
|
379
|
+
if (html) {
|
|
380
|
+
const destPath = path.join(tokensUiDir, filename);
|
|
381
|
+
const existing = fs.existsSync(destPath) ? fs.readFileSync(destPath, 'utf-8') : null;
|
|
382
|
+
if (existing !== html) {
|
|
383
|
+
fs.writeFileSync(destPath, html);
|
|
384
|
+
generated++;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
return generated;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
module.exports = {
|
|
393
|
+
generateColors,
|
|
394
|
+
generateTypography,
|
|
395
|
+
generateSpacing,
|
|
396
|
+
generateFonts,
|
|
397
|
+
generateTextWeights,
|
|
398
|
+
generateTextLeading,
|
|
399
|
+
generateViewports,
|
|
400
|
+
generateTokenSwatches,
|
|
401
|
+
};
|