swatchkit 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -9
- package/build.js +167 -134
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# SwatchKit
|
|
2
2
|
|
|
3
|
-
**SwatchKit** is a lightweight tool for generating HTML pattern libraries. It acts as a **Pattern Discovery Engine**: it scans your folders for HTML components and stitches them into a documentation site using a layout you control.
|
|
3
|
+
**SwatchKit** is a lightweight tool for generating HTML pattern libraries and Design Systems. It acts as a **Pattern Discovery Engine**: it scans your folders for HTML components and stitches them into a documentation site using a layout you control.
|
|
4
4
|
|
|
5
5
|
It follows the "Magic Folder" principle: drop files in, and a library comes out.
|
|
6
6
|
|
|
@@ -16,7 +16,10 @@ npx swatchkit init
|
|
|
16
16
|
npx swatchkit
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
This will
|
|
19
|
+
This will:
|
|
20
|
+
1. Create a `swatches/` folder.
|
|
21
|
+
2. Scaffold a complete **Design System** in `src/tokens/` (Colors, Fluid Type, Spacing).
|
|
22
|
+
3. Build your site to `public/swatchkit/`.
|
|
20
23
|
|
|
21
24
|
---
|
|
22
25
|
|
|
@@ -43,13 +46,17 @@ By default, SwatchKit looks for a `swatches/` folder in your project root.
|
|
|
43
46
|
* **Single File:** Drop `card.html` into `swatches/`. It appears in the library.
|
|
44
47
|
* **Component Folder:** Drop a folder like `swatches/carousel/` containing `index.html`. It works the same way.
|
|
45
48
|
|
|
46
|
-
### 2. Design
|
|
47
|
-
SwatchKit
|
|
48
|
-
* **Source:** Edit `src/tokens/colors.json`.
|
|
49
|
-
* **Output:** SwatchKit auto-generates `src/css/tokens.css` with CSS variables.
|
|
50
|
-
* **Docs:** It creates `swatches/colors.html` to visualize your palette.
|
|
49
|
+
### 2. Design Token Engine
|
|
50
|
+
SwatchKit scaffolds a powerful, CUBE CSS-friendly design system for you. Edit the JSON files in `src/tokens/`, and SwatchKit auto-generates `src/css/tokens.css`.
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
**Supported Tokens:**
|
|
53
|
+
* **Colors** (`colors.json`): Generates palettes.
|
|
54
|
+
* **Fluid Typography** (`text-sizes.json`): Generates `clamp()` based type scales using Utopia methodology.
|
|
55
|
+
* **Fluid Spacing** (`spacing.json`): Generates `clamp()` based spacing.
|
|
56
|
+
* **Modular Leading** (`text-leading.json`): Generates line-heights using `pow()` modular scales.
|
|
57
|
+
* **Fonts & Weights**: Manages font families and weights.
|
|
58
|
+
|
|
59
|
+
Docs for these are automatically created in `swatches/tokens/*.html`.
|
|
53
60
|
|
|
54
61
|
### 3. Custom Layouts
|
|
55
62
|
When you run `swatchkit init`, we create `swatches/_layout.html`.
|
|
@@ -96,6 +103,14 @@ module.exports = {
|
|
|
96
103
|
input: './src/patterns',
|
|
97
104
|
|
|
98
105
|
// Override default output directory
|
|
99
|
-
outDir: './dist/docs'
|
|
106
|
+
outDir: './dist/docs',
|
|
107
|
+
|
|
108
|
+
// Override Token Defaults
|
|
109
|
+
tokens: {
|
|
110
|
+
leading: {
|
|
111
|
+
ratio: 1.25, // Change modular scale ratio
|
|
112
|
+
base: 1
|
|
113
|
+
}
|
|
114
|
+
}
|
|
100
115
|
};
|
|
101
116
|
```
|
package/build.js
CHANGED
|
@@ -77,7 +77,7 @@ function resolveSettings(cliOptions, fileConfig) {
|
|
|
77
77
|
if (fileConfig.input) return path.resolve(cwd, fileConfig.input);
|
|
78
78
|
|
|
79
79
|
// 2. Search candidates
|
|
80
|
-
const candidates = ["swatches", "src/swatches"
|
|
80
|
+
const candidates = ["swatches", "src/swatches"];
|
|
81
81
|
for (const cand of candidates) {
|
|
82
82
|
const absPath = path.join(cwd, cand);
|
|
83
83
|
if (fs.existsSync(absPath)) return absPath;
|
|
@@ -97,13 +97,13 @@ function resolveSettings(cliOptions, fileConfig) {
|
|
|
97
97
|
? path.resolve(cwd, fileConfig.outDir)
|
|
98
98
|
: path.join(cwd, "public/swatchkit");
|
|
99
99
|
|
|
100
|
-
// CSS Dir (Legacy support: src/css)
|
|
101
100
|
const cssDir = path.join(cwd, "src/css");
|
|
102
101
|
|
|
103
102
|
return {
|
|
104
103
|
patternsDir,
|
|
105
104
|
outDir,
|
|
106
105
|
cssDir,
|
|
106
|
+
fileConfig, // Expose config to init
|
|
107
107
|
// Internal layout template (relative to this script)
|
|
108
108
|
internalLayout: path.join(__dirname, "src/layout.html"),
|
|
109
109
|
// Project specific layout override
|
|
@@ -129,6 +129,13 @@ function runInit(settings, options) {
|
|
|
129
129
|
fs.mkdirSync(settings.patternsDir, { recursive: true });
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
// Create patterns/tokens directory
|
|
133
|
+
const patternsTokensDir = path.join(settings.patternsDir, "tokens");
|
|
134
|
+
if (!fs.existsSync(patternsTokensDir)) {
|
|
135
|
+
console.log(`Creating patterns/tokens directory: ${patternsTokensDir}`);
|
|
136
|
+
fs.mkdirSync(patternsTokensDir, { recursive: true });
|
|
137
|
+
}
|
|
138
|
+
|
|
132
139
|
// Create src/tokens directory and sample colors.json
|
|
133
140
|
const tokensDir = path.join(process.cwd(), "src/tokens");
|
|
134
141
|
if (!fs.existsSync(tokensDir)) {
|
|
@@ -136,92 +143,75 @@ function runInit(settings, options) {
|
|
|
136
143
|
fs.mkdirSync(tokensDir, { recursive: true });
|
|
137
144
|
}
|
|
138
145
|
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
{ name: "Mid", value: "#ABA9A2" },
|
|
149
|
-
{ name: "Light", value: "#ffffff" },
|
|
150
|
-
{ name: "Primary", value: "#fcad26" },
|
|
151
|
-
],
|
|
152
|
-
};
|
|
153
|
-
fs.writeFileSync(colorsFile, JSON.stringify(sampleColors, null, 2));
|
|
154
|
-
console.log(`Created sample tokens file at ${colorsFile}`);
|
|
155
|
-
}
|
|
146
|
+
const copyDefault = (srcFilename, destFilename) => {
|
|
147
|
+
const destPath = path.join(tokensDir, destFilename);
|
|
148
|
+
if (!fs.existsSync(destPath)) {
|
|
149
|
+
const srcPath = path.join(__dirname, 'src/blueprints', srcFilename);
|
|
150
|
+
const content = fs.readFileSync(srcPath, 'utf-8');
|
|
151
|
+
fs.writeFileSync(destPath, content);
|
|
152
|
+
console.log(`Created sample tokens file at ${destPath}`);
|
|
153
|
+
}
|
|
154
|
+
};
|
|
156
155
|
|
|
157
|
-
// Create
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
.swatch-info {
|
|
178
|
-
padding: 0.5rem;
|
|
179
|
-
font-family: monospace;
|
|
180
|
-
font-size: 0.9rem;
|
|
181
|
-
background: #f9f9f9;
|
|
156
|
+
// 1. Create Colors Token
|
|
157
|
+
copyDefault('colors.json', 'colors.json');
|
|
158
|
+
|
|
159
|
+
// 2. Create Text Weights Token
|
|
160
|
+
copyDefault('text-weights.json', 'text-weights.json');
|
|
161
|
+
|
|
162
|
+
// 3. Create Text Leading Token
|
|
163
|
+
const leadingFile = path.join(tokensDir, "text-leading.json");
|
|
164
|
+
if (!fs.existsSync(leadingFile)) {
|
|
165
|
+
const srcPath = path.join(__dirname, 'src/blueprints/text-leading.json');
|
|
166
|
+
let sampleLeading = JSON.parse(fs.readFileSync(srcPath, 'utf-8'));
|
|
167
|
+
|
|
168
|
+
// Get settings from config or defaults
|
|
169
|
+
const leadingConfig = settings.fileConfig?.tokens?.leading || {};
|
|
170
|
+
if (leadingConfig.base) sampleLeading.base = leadingConfig.base;
|
|
171
|
+
if (leadingConfig.ratio) sampleLeading.ratio = leadingConfig.ratio;
|
|
172
|
+
if (leadingConfig.items) sampleLeading.items = leadingConfig.items;
|
|
173
|
+
|
|
174
|
+
fs.writeFileSync(leadingFile, JSON.stringify(sampleLeading, null, 2));
|
|
175
|
+
console.log(`Created sample tokens file at ${leadingFile}`);
|
|
182
176
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
</div>
|
|
222
|
-
`;
|
|
223
|
-
fs.writeFileSync(colorsPatternFile, colorsHtml.trim());
|
|
224
|
-
console.log(`Created sample pattern at ${colorsPatternFile}`);
|
|
177
|
+
|
|
178
|
+
// 4. Create Viewports Token
|
|
179
|
+
copyDefault('viewports.json', 'viewports.json');
|
|
180
|
+
|
|
181
|
+
// 5. Create Text Sizes Token (Fluid)
|
|
182
|
+
copyDefault('text-sizes.json', 'text-sizes.json');
|
|
183
|
+
|
|
184
|
+
// 6. Create Spacing Token
|
|
185
|
+
copyDefault('spacing.json', 'spacing.json');
|
|
186
|
+
|
|
187
|
+
// 7. Create Fonts Token
|
|
188
|
+
copyDefault('fonts.json', 'fonts.json');
|
|
189
|
+
|
|
190
|
+
const copyTemplate = (srcFilename, destFilename) => {
|
|
191
|
+
const destPath = path.join(patternsTokensDir, destFilename);
|
|
192
|
+
if (!fs.existsSync(destPath)) {
|
|
193
|
+
const srcPath = path.join(__dirname, 'src/templates', srcFilename);
|
|
194
|
+
const content = fs.readFileSync(srcPath, 'utf-8');
|
|
195
|
+
fs.writeFileSync(destPath, content.trim());
|
|
196
|
+
console.log(`Created sample pattern at ${destPath}`);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
// Create sample patterns
|
|
201
|
+
copyTemplate('colors.html', 'colors.html');
|
|
202
|
+
copyTemplate('text-weights.html', 'text-weights.html');
|
|
203
|
+
copyTemplate('text-leading.html', 'text-leading.html');
|
|
204
|
+
copyTemplate('typography.html', 'typography.html');
|
|
205
|
+
copyTemplate('spacing.html', 'spacing.html');
|
|
206
|
+
copyTemplate('fonts.html', 'fonts.html');
|
|
207
|
+
|
|
208
|
+
// Create shared script for tokens
|
|
209
|
+
const tokensScriptFile = path.join(patternsTokensDir, "script.js");
|
|
210
|
+
if (!fs.existsSync(tokensScriptFile)) {
|
|
211
|
+
const srcPath = path.join(__dirname, 'src/templates/script.js');
|
|
212
|
+
const content = fs.readFileSync(srcPath, 'utf-8');
|
|
213
|
+
fs.writeFileSync(tokensScriptFile, content.trim());
|
|
214
|
+
console.log(`Created tokens script at ${tokensScriptFile}`);
|
|
225
215
|
}
|
|
226
216
|
|
|
227
217
|
// Generate initial CSS
|
|
@@ -248,54 +238,18 @@ function runInit(settings, options) {
|
|
|
248
238
|
}
|
|
249
239
|
|
|
250
240
|
// --- 5. Build Logic ---
|
|
251
|
-
function
|
|
252
|
-
console.log(`[SwatchKit] Starting build...`);
|
|
253
|
-
console.log(` Patterns: ${settings.patternsDir}`);
|
|
254
|
-
console.log(` Output: ${settings.outDir}`);
|
|
255
|
-
|
|
256
|
-
// 1. Check if patterns directory exists
|
|
257
|
-
if (!fs.existsSync(settings.patternsDir)) {
|
|
258
|
-
console.error(
|
|
259
|
-
`Error: Patterns directory not found at ${settings.patternsDir}`,
|
|
260
|
-
);
|
|
261
|
-
console.error('Run "swatchkit init" to get started.');
|
|
262
|
-
process.exit(1);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// 2. Ensure dist directories exist
|
|
266
|
-
[settings.outDir, settings.distCssDir, settings.distJsDir].forEach((dir) => {
|
|
267
|
-
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
// 2.5 Process Tokens
|
|
271
|
-
processTokens(process.cwd(), settings.cssDir);
|
|
272
|
-
|
|
273
|
-
// 3. Copy CSS files
|
|
274
|
-
if (fs.existsSync(settings.cssDir)) {
|
|
275
|
-
console.log("Copying CSS...");
|
|
276
|
-
const cssFiles = fs
|
|
277
|
-
.readdirSync(settings.cssDir)
|
|
278
|
-
.filter((file) => file.endsWith(".css"));
|
|
279
|
-
cssFiles.forEach((file) => {
|
|
280
|
-
fs.copyFileSync(
|
|
281
|
-
path.join(settings.cssDir, file),
|
|
282
|
-
path.join(settings.distCssDir, file),
|
|
283
|
-
);
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// 4. Read patterns & JS
|
|
288
|
-
console.log("Processing patterns...");
|
|
241
|
+
function scanDirectory(dir, scriptsCollector, exclude = []) {
|
|
289
242
|
const patterns = [];
|
|
290
|
-
|
|
243
|
+
if (!fs.existsSync(dir)) return patterns;
|
|
291
244
|
|
|
292
|
-
const items = fs.readdirSync(
|
|
245
|
+
const items = fs.readdirSync(dir);
|
|
293
246
|
|
|
294
247
|
items.forEach((item) => {
|
|
295
|
-
// Skip _layout.html or hidden files
|
|
248
|
+
// Skip excluded items, _layout.html, or hidden files
|
|
249
|
+
if (exclude.includes(item)) return;
|
|
296
250
|
if (item.startsWith("_") || item.startsWith(".")) return;
|
|
297
251
|
|
|
298
|
-
const itemPath = path.join(
|
|
252
|
+
const itemPath = path.join(dir, item);
|
|
299
253
|
const stat = fs.statSync(itemPath);
|
|
300
254
|
|
|
301
255
|
let name, content, id;
|
|
@@ -319,7 +273,7 @@ function build(settings) {
|
|
|
319
273
|
path.join(itemPath, jsFile),
|
|
320
274
|
"utf-8",
|
|
321
275
|
);
|
|
322
|
-
|
|
276
|
+
scriptsCollector.push(`
|
|
323
277
|
/* --- Pattern: ${name} / File: ${jsFile} --- */
|
|
324
278
|
(function() {
|
|
325
279
|
${scriptContent}
|
|
@@ -334,18 +288,98 @@ ${scriptContent}
|
|
|
334
288
|
id = name;
|
|
335
289
|
content = fs.readFileSync(itemPath, "utf-8");
|
|
336
290
|
}
|
|
291
|
+
// Handle Loose JS Files (e.g. script.js in tokens/)
|
|
292
|
+
else if (item.endsWith(".js")) {
|
|
293
|
+
const scriptContent = fs.readFileSync(itemPath, "utf-8");
|
|
294
|
+
scriptsCollector.push(`
|
|
295
|
+
/* --- File: ${item} --- */
|
|
296
|
+
(function() {
|
|
297
|
+
${scriptContent}
|
|
298
|
+
})();
|
|
299
|
+
`);
|
|
300
|
+
// Don't add to patterns list, just scripts
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
337
303
|
|
|
338
304
|
if (name && content) {
|
|
339
305
|
patterns.push({ name, id, content });
|
|
340
306
|
}
|
|
341
307
|
});
|
|
342
308
|
|
|
309
|
+
return patterns;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function build(settings) {
|
|
313
|
+
console.log(`[SwatchKit] Starting build...`);
|
|
314
|
+
console.log(` Patterns: ${settings.patternsDir}`);
|
|
315
|
+
console.log(` Output: ${settings.outDir}`);
|
|
316
|
+
|
|
317
|
+
// 1. Check if patterns directory exists
|
|
318
|
+
if (!fs.existsSync(settings.patternsDir)) {
|
|
319
|
+
console.error(
|
|
320
|
+
`Error: Patterns directory not found at ${settings.patternsDir}`,
|
|
321
|
+
);
|
|
322
|
+
console.error('Run "swatchkit init" to get started.');
|
|
323
|
+
process.exit(1);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// 2. Ensure dist directories exist
|
|
327
|
+
[settings.outDir, settings.distCssDir, settings.distJsDir].forEach((dir) => {
|
|
328
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
// 2.5 Process Tokens
|
|
332
|
+
processTokens(process.cwd(), settings.cssDir);
|
|
333
|
+
|
|
334
|
+
// 3. Copy CSS files
|
|
335
|
+
if (fs.existsSync(settings.cssDir)) {
|
|
336
|
+
console.log("Copying CSS...");
|
|
337
|
+
const cssFiles = fs
|
|
338
|
+
.readdirSync(settings.cssDir)
|
|
339
|
+
.filter((file) => file.endsWith(".css"));
|
|
340
|
+
cssFiles.forEach((file) => {
|
|
341
|
+
fs.copyFileSync(
|
|
342
|
+
path.join(settings.cssDir, file),
|
|
343
|
+
path.join(settings.distCssDir, file),
|
|
344
|
+
);
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// 4. Read patterns & JS
|
|
349
|
+
console.log("Processing patterns...");
|
|
350
|
+
const scripts = [];
|
|
351
|
+
|
|
352
|
+
// Pass 1: Tokens (in [patternsDir]/tokens/)
|
|
353
|
+
const tokensDir = path.join(settings.patternsDir, "tokens");
|
|
354
|
+
const tokenPages = scanDirectory(tokensDir, scripts);
|
|
355
|
+
|
|
356
|
+
// Pass 2: Patterns (in [patternsDir], excluding 'tokens')
|
|
357
|
+
const patternPages = scanDirectory(settings.patternsDir, scripts, ["tokens"]);
|
|
358
|
+
|
|
359
|
+
// Combine for content generation (Tokens first, then Patterns)
|
|
360
|
+
const allPatterns = [...tokenPages, ...patternPages];
|
|
361
|
+
|
|
343
362
|
// 5. Generate HTML fragments
|
|
344
|
-
const sidebarLinks = patterns
|
|
345
|
-
.map((p) => `<a href="#${p.id}">${p.name}</a>`)
|
|
346
|
-
.join("\n");
|
|
347
363
|
|
|
348
|
-
|
|
364
|
+
// Sidebar generation with grouping
|
|
365
|
+
let sidebarLinks = "";
|
|
366
|
+
|
|
367
|
+
if (tokenPages.length > 0) {
|
|
368
|
+
sidebarLinks += `<h3>Design Tokens</h3>\n`;
|
|
369
|
+
sidebarLinks += tokenPages
|
|
370
|
+
.map((p) => `<a href="#${p.id}">${p.name}</a>`)
|
|
371
|
+
.join("\n");
|
|
372
|
+
sidebarLinks += `\n`;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (patternPages.length > 0) {
|
|
376
|
+
sidebarLinks += `<h3>Patterns</h3>\n`;
|
|
377
|
+
sidebarLinks += patternPages
|
|
378
|
+
.map((p) => `<a href="#${p.id}">${p.name}</a>`)
|
|
379
|
+
.join("\n");
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const patternBlocks = allPatterns
|
|
349
383
|
.map((p) => {
|
|
350
384
|
const escapedContent = p.content
|
|
351
385
|
.replace(/&/g, "&")
|
|
@@ -419,4 +453,3 @@ try {
|
|
|
419
453
|
console.error("[SwatchKit] Error:", error.message);
|
|
420
454
|
process.exit(1);
|
|
421
455
|
}
|
|
422
|
-
|