swatchkit 0.0.10 → 0.0.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/README.md +30 -11
- package/build.js +115 -65
- package/package.json +1 -1
- package/src/layout.html +2 -2
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ my-project/
|
|
|
23
23
|
├── css/
|
|
24
24
|
│ ├── tokens.css # Generated design tokens (CSS custom properties)
|
|
25
25
|
│ └── styles.css # Starter stylesheet (imports tokens.css)
|
|
26
|
-
├──
|
|
26
|
+
├── swatchkit/
|
|
27
27
|
│ ├── _layout.html # Layout template (you own this)
|
|
28
28
|
│ └── tokens/ # Token definitions + documentation patterns
|
|
29
29
|
│ ├── colors.json
|
|
@@ -54,13 +54,32 @@ Then add it to your `package.json` scripts:
|
|
|
54
54
|
|
|
55
55
|
## Features
|
|
56
56
|
|
|
57
|
-
### 1. The Magic Folder
|
|
58
|
-
By default, SwatchKit looks for a `
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
### 1. The Magic Folder & Project Structure
|
|
58
|
+
By default, SwatchKit looks for a `swatchkit/` folder in your project root.
|
|
59
|
+
|
|
60
|
+
**Organize by Folder:**
|
|
61
|
+
SwatchKit automatically turns subfolders into sections in the documentation sidebar.
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
swatchkit/
|
|
65
|
+
├── tokens/ # Section: "Design Tokens"
|
|
66
|
+
│ └── colors.json
|
|
67
|
+
├── components/ # Section: "Components"
|
|
68
|
+
│ ├── button.html
|
|
69
|
+
│ └── card/
|
|
70
|
+
│ └── index.html
|
|
71
|
+
├── compositions/ # Section: "Compositions"
|
|
72
|
+
│ └── sidebar.html
|
|
73
|
+
└── utilities/ # Section: "Utilities"
|
|
74
|
+
└── flow.html
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
* **Files at root:** Go to the "Patterns" section.
|
|
78
|
+
* **Subfolders:** Create a new section (e.g. `utilities/` -> "Utilities").
|
|
79
|
+
* **Tokens:** Special folder for JSON definitions.
|
|
61
80
|
|
|
62
81
|
### 2. Design Token Engine
|
|
63
|
-
SwatchKit scaffolds a design system for you. Edit the JSON files in `
|
|
82
|
+
SwatchKit scaffolds a design system for you. Edit the JSON files in `swatchkit/tokens/`, and SwatchKit auto-generates `css/tokens.css`.
|
|
64
83
|
|
|
65
84
|
**Supported Tokens:**
|
|
66
85
|
* **Colors** (`colors.json`): Generates palettes.
|
|
@@ -80,7 +99,7 @@ SwatchKit can auto-calculate fluid typography and spacing scales.
|
|
|
80
99
|
* **Fluid:** Provide `min` and `max` (e.g. `16` and `18`).
|
|
81
100
|
* **Auto-Fluid:** Provide just ONE side (`min` or `max`), and SwatchKit calculates the other using a default ratio (1.125).
|
|
82
101
|
|
|
83
|
-
**Example (`
|
|
102
|
+
**Example (`swatchkit/tokens/text-sizes.json`):**
|
|
84
103
|
```json
|
|
85
104
|
{
|
|
86
105
|
"title": "Text Sizes",
|
|
@@ -110,7 +129,7 @@ SwatchKit can auto-calculate fluid typography and spacing scales.
|
|
|
110
129
|
|
|
111
130
|
You can mix modular scales with manual overrides.
|
|
112
131
|
|
|
113
|
-
**Example (`
|
|
132
|
+
**Example (`swatchkit/tokens/text-leading.json`):**
|
|
114
133
|
```json
|
|
115
134
|
{
|
|
116
135
|
"base": 1,
|
|
@@ -141,7 +160,7 @@ body {
|
|
|
141
160
|
The pattern library uses **your stylesheet**, so components render exactly as they will in your app.
|
|
142
161
|
|
|
143
162
|
### 6. Custom Layouts
|
|
144
|
-
When you run `swatchkit init`, we create `
|
|
163
|
+
When you run `swatchkit init`, we create `swatchkit/_layout.html`.
|
|
145
164
|
**You own this file.**
|
|
146
165
|
* Link to your own stylesheets.
|
|
147
166
|
* Add custom fonts, scripts, or meta tags.
|
|
@@ -151,7 +170,7 @@ SwatchKit injects content into the `<!-- PATTERNS -->`, `<!-- SIDEBAR_LINKS -->`
|
|
|
151
170
|
|
|
152
171
|
### 7. JavaScript Bundling
|
|
153
172
|
If your component needs client-side JS:
|
|
154
|
-
1. Create a folder: `
|
|
173
|
+
1. Create a folder: `swatchkit/carousel/`.
|
|
155
174
|
2. Add `index.html` (Markup).
|
|
156
175
|
3. Add `script.js` (Logic).
|
|
157
176
|
|
|
@@ -172,7 +191,7 @@ swatchkit [command] [options]
|
|
|
172
191
|
| :--- | :--- | :--- |
|
|
173
192
|
| `--watch` | `-w` | Watch files and rebuild on change. |
|
|
174
193
|
| `--config` | `-c` | Path to config file. |
|
|
175
|
-
| `--input` | `-i` | Pattern directory (Default: `
|
|
194
|
+
| `--input` | `-i` | Pattern directory (Default: `swatchkit/`). |
|
|
176
195
|
| `--outDir` | `-o` | Output directory (Default: `public/swatchkit`). |
|
|
177
196
|
| `--force` | `-f` | Overwrite layout file during init. |
|
|
178
197
|
|
package/build.js
CHANGED
|
@@ -68,7 +68,7 @@ function loadConfig(configPath) {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
// --- 2.5 Glob Matching Helper ---
|
|
71
|
-
function
|
|
71
|
+
function matchesGlob(filename, pattern) {
|
|
72
72
|
// Simple wildcard support
|
|
73
73
|
if (pattern.includes("*")) {
|
|
74
74
|
const parts = pattern.split("*");
|
|
@@ -88,28 +88,25 @@ function matchesPattern(filename, pattern) {
|
|
|
88
88
|
return filename === pattern;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
function toTitleCase(str) {
|
|
92
|
+
return str.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
|
|
93
|
+
}
|
|
94
|
+
|
|
91
95
|
// --- 3. Smart Defaults & Path Resolution ---
|
|
92
96
|
function resolveSettings(cliOptions, fileConfig) {
|
|
93
97
|
const cwd = process.cwd();
|
|
94
98
|
|
|
95
99
|
// Helper to find patterns dir
|
|
96
|
-
function
|
|
100
|
+
function findSwatchkitDir() {
|
|
97
101
|
// 1. Explicit input
|
|
98
102
|
if (cliOptions.input) return path.resolve(cwd, cliOptions.input);
|
|
99
103
|
if (fileConfig.input) return path.resolve(cwd, fileConfig.input);
|
|
100
104
|
|
|
101
|
-
// 2.
|
|
102
|
-
|
|
103
|
-
for (const cand of candidates) {
|
|
104
|
-
const absPath = path.join(cwd, cand);
|
|
105
|
-
if (fs.existsSync(absPath)) return absPath;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// 3. Fallback default (swatches)
|
|
109
|
-
return path.join(cwd, "swatches");
|
|
105
|
+
// 2. Default
|
|
106
|
+
return path.join(cwd, "swatchkit");
|
|
110
107
|
}
|
|
111
108
|
|
|
112
|
-
const
|
|
109
|
+
const swatchkitDir = findSwatchkitDir();
|
|
113
110
|
|
|
114
111
|
// Output Dir
|
|
115
112
|
// Default: public/swatchkit
|
|
@@ -129,13 +126,13 @@ function resolveSettings(cliOptions, fileConfig) {
|
|
|
129
126
|
// Default: swatches/tokens/ (not src/tokens/)
|
|
130
127
|
const tokensDir = fileConfig.tokens?.input
|
|
131
128
|
? path.resolve(cwd, fileConfig.tokens.input)
|
|
132
|
-
: path.join(
|
|
129
|
+
: path.join(swatchkitDir, "tokens");
|
|
133
130
|
|
|
134
131
|
// Exclude patterns
|
|
135
132
|
const exclude = fileConfig.exclude || [];
|
|
136
133
|
|
|
137
134
|
return {
|
|
138
|
-
|
|
135
|
+
swatchkitDir,
|
|
139
136
|
outDir,
|
|
140
137
|
cssDir,
|
|
141
138
|
tokensDir,
|
|
@@ -144,14 +141,14 @@ function resolveSettings(cliOptions, fileConfig) {
|
|
|
144
141
|
// Internal layout template (relative to this script)
|
|
145
142
|
internalLayout: path.join(__dirname, "src/layout.html"),
|
|
146
143
|
// Project specific layout override
|
|
147
|
-
projectLayout: path.join(
|
|
144
|
+
projectLayout: path.join(swatchkitDir, "_layout.html"),
|
|
148
145
|
|
|
149
146
|
// Derived paths
|
|
150
147
|
distCssDir: path.join(outDir, "css"),
|
|
151
148
|
distTokensCssFile: path.join(outDir, "css", "tokens.css"),
|
|
152
149
|
distJsDir: path.join(outDir, "js"),
|
|
153
150
|
outputFile: path.join(outDir, "index.html"),
|
|
154
|
-
outputJsFile: path.join(outDir, "js/
|
|
151
|
+
outputJsFile: path.join(outDir, "js/swatches.js"),
|
|
155
152
|
tokensCssFile: path.join(cssDir, "tokens.css"),
|
|
156
153
|
stylesCssFile: path.join(cssDir, "styles.css"),
|
|
157
154
|
};
|
|
@@ -161,10 +158,10 @@ function resolveSettings(cliOptions, fileConfig) {
|
|
|
161
158
|
function runInit(settings, options) {
|
|
162
159
|
console.log("[SwatchKit] Initializing...");
|
|
163
160
|
|
|
164
|
-
// Ensure
|
|
165
|
-
if (!fs.existsSync(settings.
|
|
166
|
-
console.log(`Creating
|
|
167
|
-
fs.mkdirSync(settings.
|
|
161
|
+
// Ensure swatchkit directory exists
|
|
162
|
+
if (!fs.existsSync(settings.swatchkitDir)) {
|
|
163
|
+
console.log(`Creating swatchkit directory: ${settings.swatchkitDir}`);
|
|
164
|
+
fs.mkdirSync(settings.swatchkitDir, { recursive: true });
|
|
168
165
|
}
|
|
169
166
|
|
|
170
167
|
// Create swatches/tokens directory (for both JSON definitions and HTML patterns)
|
|
@@ -270,15 +267,15 @@ function runInit(settings, options) {
|
|
|
270
267
|
}
|
|
271
268
|
|
|
272
269
|
// --- 5. Build Logic ---
|
|
273
|
-
function
|
|
274
|
-
const
|
|
275
|
-
if (!fs.existsSync(dir)) return
|
|
270
|
+
function scanSwatches(dir, scriptsCollector, exclude = []) {
|
|
271
|
+
const swatches = [];
|
|
272
|
+
if (!fs.existsSync(dir)) return swatches;
|
|
276
273
|
|
|
277
274
|
const items = fs.readdirSync(dir);
|
|
278
275
|
|
|
279
276
|
items.forEach((item) => {
|
|
280
277
|
// Skip excluded items
|
|
281
|
-
if (exclude.some(pattern =>
|
|
278
|
+
if (exclude.some(pattern => matchesGlob(item, pattern))) return;
|
|
282
279
|
|
|
283
280
|
// Skip _layout.html or hidden files
|
|
284
281
|
if (item.startsWith("_") || item.startsWith(".")) return;
|
|
@@ -288,7 +285,7 @@ function scanDirectory(dir, scriptsCollector, exclude = []) {
|
|
|
288
285
|
|
|
289
286
|
let name, content, id;
|
|
290
287
|
|
|
291
|
-
// Handle Directory
|
|
288
|
+
// Handle Component Directory
|
|
292
289
|
if (stat.isDirectory()) {
|
|
293
290
|
const indexFile = path.join(itemPath, "index.html");
|
|
294
291
|
|
|
@@ -308,7 +305,7 @@ function scanDirectory(dir, scriptsCollector, exclude = []) {
|
|
|
308
305
|
"utf-8",
|
|
309
306
|
);
|
|
310
307
|
scriptsCollector.push(`
|
|
311
|
-
/* ---
|
|
308
|
+
/* --- Swatch: ${name} / File: ${jsFile} --- */
|
|
312
309
|
(function() {
|
|
313
310
|
${scriptContent}
|
|
314
311
|
})();
|
|
@@ -316,7 +313,7 @@ ${scriptContent}
|
|
|
316
313
|
});
|
|
317
314
|
}
|
|
318
315
|
}
|
|
319
|
-
// Handle Single File
|
|
316
|
+
// Handle Single File
|
|
320
317
|
else if (item.endsWith(".html")) {
|
|
321
318
|
name = path.basename(item, ".html");
|
|
322
319
|
id = name;
|
|
@@ -331,27 +328,27 @@ ${scriptContent}
|
|
|
331
328
|
${scriptContent}
|
|
332
329
|
})();
|
|
333
330
|
`);
|
|
334
|
-
// Don't add to
|
|
331
|
+
// Don't add to swatches list, just scripts
|
|
335
332
|
return;
|
|
336
333
|
}
|
|
337
334
|
|
|
338
335
|
if (name && content) {
|
|
339
|
-
|
|
336
|
+
swatches.push({ name, id, content });
|
|
340
337
|
}
|
|
341
338
|
});
|
|
342
339
|
|
|
343
|
-
return
|
|
340
|
+
return swatches;
|
|
344
341
|
}
|
|
345
342
|
|
|
346
343
|
function build(settings) {
|
|
347
344
|
console.log(`[SwatchKit] Starting build...`);
|
|
348
|
-
console.log(`
|
|
345
|
+
console.log(` Source: ${settings.swatchkitDir}`);
|
|
349
346
|
console.log(` Output: ${settings.outDir}`);
|
|
350
347
|
|
|
351
|
-
// 1. Check if
|
|
352
|
-
if (!fs.existsSync(settings.
|
|
348
|
+
// 1. Check if source directory exists
|
|
349
|
+
if (!fs.existsSync(settings.swatchkitDir)) {
|
|
353
350
|
console.error(
|
|
354
|
-
`Error:
|
|
351
|
+
`Error: SwatchKit directory not found at ${settings.swatchkitDir}`,
|
|
355
352
|
);
|
|
356
353
|
console.error('Run "swatchkit init" to get started.');
|
|
357
354
|
process.exit(1);
|
|
@@ -379,43 +376,96 @@ function build(settings) {
|
|
|
379
376
|
});
|
|
380
377
|
}
|
|
381
378
|
|
|
382
|
-
// 4. Read
|
|
383
|
-
console.log("Processing
|
|
379
|
+
// 4. Read swatches & JS
|
|
380
|
+
console.log("Processing swatches...");
|
|
384
381
|
const scripts = [];
|
|
382
|
+
const sections = {}; // Map<SectionName, Array<Swatch>>
|
|
383
|
+
|
|
384
|
+
if (fs.existsSync(settings.swatchkitDir)) {
|
|
385
|
+
const items = fs.readdirSync(settings.swatchkitDir);
|
|
386
|
+
const exclude = settings.exclude || [];
|
|
387
|
+
|
|
388
|
+
// Scan subdirectories (Sections)
|
|
389
|
+
items.forEach(item => {
|
|
390
|
+
if (exclude.some(p => matchesGlob(item, p))) return;
|
|
391
|
+
if (item.startsWith(".") || item.startsWith("_")) return;
|
|
392
|
+
|
|
393
|
+
const itemPath = path.join(settings.swatchkitDir, item);
|
|
394
|
+
if (fs.lstatSync(itemPath).isDirectory()) {
|
|
395
|
+
const hasIndex = fs.existsSync(path.join(itemPath, "index.html"));
|
|
396
|
+
|
|
397
|
+
if (!hasIndex) {
|
|
398
|
+
// It is a Section Container (e.g. "Utilities")
|
|
399
|
+
const sectionName = item === 'tokens' ? 'Design Tokens' : toTitleCase(item);
|
|
400
|
+
const swatches = scanSwatches(itemPath, scripts, exclude);
|
|
401
|
+
if (swatches.length > 0) {
|
|
402
|
+
sections[sectionName] = swatches;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
});
|
|
385
407
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
408
|
+
// Scan root swatches (Files + Component Folders at root)
|
|
409
|
+
const rootSwatches = [];
|
|
410
|
+
items.forEach(item => {
|
|
411
|
+
if (exclude.some(p => matchesGlob(item, p))) return;
|
|
412
|
+
if (item.startsWith(".") || item.startsWith("_")) return;
|
|
413
|
+
|
|
414
|
+
const itemPath = path.join(settings.swatchkitDir, item);
|
|
415
|
+
const stat = fs.statSync(itemPath);
|
|
416
|
+
|
|
417
|
+
if (stat.isFile() && item.endsWith('.html')) {
|
|
418
|
+
const name = path.basename(item, ".html");
|
|
419
|
+
const content = fs.readFileSync(itemPath, "utf-8");
|
|
420
|
+
rootSwatches.push({ name, id: name, content });
|
|
421
|
+
} else if (stat.isDirectory()) {
|
|
422
|
+
const indexFile = path.join(itemPath, "index.html");
|
|
423
|
+
if (fs.existsSync(indexFile)) {
|
|
424
|
+
// Component folder swatch at root
|
|
425
|
+
const name = item;
|
|
426
|
+
const content = fs.readFileSync(indexFile, "utf-8");
|
|
427
|
+
rootSwatches.push({ name, id: name, content });
|
|
428
|
+
|
|
429
|
+
// Collect JS
|
|
430
|
+
const jsFiles = fs.readdirSync(itemPath).filter(f => f.endsWith(".js"));
|
|
431
|
+
jsFiles.forEach(jsFile => {
|
|
432
|
+
const scriptContent = fs.readFileSync(path.join(itemPath, jsFile), "utf-8");
|
|
433
|
+
scripts.push(`/* ${name}/${jsFile} */ (function(){${scriptContent}})();`);
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
if (rootSwatches.length > 0) {
|
|
440
|
+
sections["Patterns"] = rootSwatches;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
396
443
|
|
|
397
444
|
// 5. Generate HTML fragments
|
|
398
445
|
|
|
399
446
|
// Sidebar generation with grouping
|
|
400
447
|
let sidebarLinks = "";
|
|
448
|
+
let swatchBlocks = "";
|
|
449
|
+
|
|
450
|
+
// Helper to sort sections: Tokens first, then A-Z, Patterns last
|
|
451
|
+
const sortedKeys = Object.keys(sections).sort((a, b) => {
|
|
452
|
+
if (a === 'Design Tokens') return -1;
|
|
453
|
+
if (b === 'Design Tokens') return 1;
|
|
454
|
+
if (a === 'Patterns') return 1;
|
|
455
|
+
if (b === 'Patterns') return -1;
|
|
456
|
+
return a.localeCompare(b);
|
|
457
|
+
});
|
|
401
458
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
sidebarLinks +=
|
|
459
|
+
sortedKeys.forEach(section => {
|
|
460
|
+
const swatches = sections[section];
|
|
461
|
+
sidebarLinks += `<h3>${section}</h3>\n`;
|
|
462
|
+
sidebarLinks += swatches
|
|
405
463
|
.map((p) => `<a href="#${p.id}">${p.name}</a>`)
|
|
406
464
|
.join("\n");
|
|
407
465
|
sidebarLinks += `\n`;
|
|
408
|
-
}
|
|
409
466
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
sidebarLinks += patternPages
|
|
413
|
-
.map((p) => `<a href="#${p.id}">${p.name}</a>`)
|
|
414
|
-
.join("\n");
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
const patternBlocks = allPatterns
|
|
418
|
-
.map((p) => {
|
|
467
|
+
// Generate Blocks
|
|
468
|
+
swatchBlocks += swatches.map((p) => {
|
|
419
469
|
const escapedContent = p.content
|
|
420
470
|
.replace(/&/g, "&")
|
|
421
471
|
.replace(/</g, "<")
|
|
@@ -425,13 +475,13 @@ function build(settings) {
|
|
|
425
475
|
|
|
426
476
|
return `
|
|
427
477
|
<section id="${p.id}">
|
|
428
|
-
<h2>${p.name}</h2>
|
|
478
|
+
<h2>${p.name} <small style="font-weight: normal; opacity: 0.6; font-size: 0.7em">(${section})</small></h2>
|
|
429
479
|
<div class="preview">${p.content}</div>
|
|
430
480
|
<pre><code>${escapedContent}</code></pre>
|
|
431
481
|
</section>
|
|
432
482
|
`;
|
|
433
|
-
})
|
|
434
|
-
|
|
483
|
+
}).join("\n");
|
|
484
|
+
});
|
|
435
485
|
|
|
436
486
|
// 6. Write JS Bundle
|
|
437
487
|
if (scripts.length > 0) {
|
|
@@ -440,7 +490,7 @@ function build(settings) {
|
|
|
440
490
|
`Bundled ${scripts.length} scripts to ${settings.outputJsFile}`,
|
|
441
491
|
);
|
|
442
492
|
} else {
|
|
443
|
-
fs.writeFileSync(settings.outputJsFile, "// No
|
|
493
|
+
fs.writeFileSync(settings.outputJsFile, "// No swatch scripts found");
|
|
444
494
|
}
|
|
445
495
|
|
|
446
496
|
// 7. Load Layout
|
|
@@ -458,7 +508,7 @@ function build(settings) {
|
|
|
458
508
|
|
|
459
509
|
const finalHtml = layoutContent
|
|
460
510
|
.replace("<!-- SIDEBAR_LINKS -->", sidebarLinks)
|
|
461
|
-
.replace("<!--
|
|
511
|
+
.replace("<!-- SWATCHES -->", swatchBlocks)
|
|
462
512
|
.replace("<!-- HEAD_EXTRAS -->", headExtras);
|
|
463
513
|
|
|
464
514
|
// 8. Write output
|
|
@@ -470,7 +520,7 @@ function build(settings) {
|
|
|
470
520
|
// --- 6. Watch Logic ---
|
|
471
521
|
function watch(settings) {
|
|
472
522
|
const watchPaths = [
|
|
473
|
-
settings.
|
|
523
|
+
settings.swatchkitDir,
|
|
474
524
|
settings.tokensDir,
|
|
475
525
|
settings.projectLayout,
|
|
476
526
|
settings.stylesCssFile
|
package/package.json
CHANGED