swatchkit 0.0.4 → 0.0.6

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.
Files changed (3) hide show
  1. package/README.md +24 -9
  2. package/build.js +167 -134
  3. 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 create a `swatches/` folder, generate a `src/tokens/colors.json` file, and build your site to `public/swatchkit/`.
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 Tokens (Colors)
47
- SwatchKit includes a basic **Token Engine**.
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
- *(More token types like Typography and Spacing coming soon!)*
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", "src/patterns"];
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 colorsFile = path.join(tokensDir, "colors.json");
140
- if (!fs.existsSync(colorsFile)) {
141
- const sampleColors = {
142
- title: "Colors",
143
- description:
144
- "Hex color codes that can be shared, cross-platform. They can be converted at point of usage, such as HSL for web or CMYK for print.",
145
- items: [
146
- { name: "Dark", value: "#171406" },
147
- { name: "Dark Glare", value: "#2d2816" },
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 sample colors.html pattern
158
- const colorsPatternFile = path.join(settings.patternsDir, "colors.html");
159
- if (!fs.existsSync(colorsPatternFile)) {
160
- const colorsHtml = `
161
- <style>
162
- .swatch-grid {
163
- display: grid;
164
- grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
165
- gap: 1rem;
166
- padding: 1rem;
167
- }
168
- .swatch {
169
- border: 1px solid #ddd;
170
- border-radius: 8px;
171
- overflow: hidden;
172
- }
173
- .swatch-color {
174
- height: 100px;
175
- width: 100%;
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
- </style>
184
-
185
- <div class="swatch-grid">
186
- <div class="swatch">
187
- <div class="swatch-color" style="background-color: var(--color-dark);"></div>
188
- <div class="swatch-info">
189
- <strong>Dark</strong><br>
190
- var(--color-dark)
191
- </div>
192
- </div>
193
- <div class="swatch">
194
- <div class="swatch-color" style="background-color: var(--color-dark-glare);"></div>
195
- <div class="swatch-info">
196
- <strong>Dark Glare</strong><br>
197
- var(--color-dark-glare)
198
- </div>
199
- </div>
200
- <div class="swatch">
201
- <div class="swatch-color" style="background-color: var(--color-mid);"></div>
202
- <div class="swatch-info">
203
- <strong>Mid</strong><br>
204
- var(--color-mid)
205
- </div>
206
- </div>
207
- <div class="swatch">
208
- <div class="swatch-color" style="background-color: var(--color-light);"></div>
209
- <div class="swatch-info">
210
- <strong>Light</strong><br>
211
- var(--color-light)
212
- </div>
213
- </div>
214
- <div class="swatch">
215
- <div class="swatch-color" style="background-color: var(--color-primary);"></div>
216
- <div class="swatch-info">
217
- <strong>Primary</strong><br>
218
- var(--color-primary)
219
- </div>
220
- </div>
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 build(settings) {
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
- const scripts = [];
243
+ if (!fs.existsSync(dir)) return patterns;
291
244
 
292
- const items = fs.readdirSync(settings.patternsDir);
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(settings.patternsDir, item);
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
- scripts.push(`
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
- const patternBlocks = patterns
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, "&amp;")
@@ -419,4 +453,3 @@ try {
419
453
  console.error("[SwatchKit] Error:", error.message);
420
454
  process.exit(1);
421
455
  }
422
-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "swatchkit",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "A lightweight tool for creating HTML pattern libraries.",
5
5
  "main": "build.js",
6
6
  "bin": {