swatchkit 1.0.1 → 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.
Files changed (53) hide show
  1. package/build.js +31 -2
  2. package/package.json +2 -2
  3. package/src/blueprints/colors.json +11 -0
  4. package/src/blueprints/compositions/cluster.css +20 -0
  5. package/src/blueprints/compositions/flow.css +10 -0
  6. package/src/blueprints/compositions/grid.css +36 -0
  7. package/src/blueprints/compositions/index.css +12 -0
  8. package/src/blueprints/compositions/prose.css +89 -0
  9. package/src/blueprints/compositions/repel.css +23 -0
  10. package/src/blueprints/compositions/sidebar.css +44 -0
  11. package/src/blueprints/compositions/switcher.css +32 -0
  12. package/src/blueprints/compositions/wrapper.css +14 -0
  13. package/src/blueprints/fonts.json +24 -0
  14. package/src/blueprints/global/elements.css +609 -0
  15. package/src/blueprints/global/index.css +13 -0
  16. package/src/blueprints/global/reset.css +91 -0
  17. package/src/blueprints/global/variables.css +58 -0
  18. package/src/blueprints/main.css +27 -0
  19. package/src/blueprints/spacing.json +19 -0
  20. package/src/blueprints/swatchkit-ui.css +79 -0
  21. package/src/blueprints/text-leading.json +13 -0
  22. package/src/blueprints/text-sizes.json +21 -0
  23. package/src/blueprints/text-weights.json +11 -0
  24. package/src/blueprints/utilities/index.css +9 -0
  25. package/src/blueprints/utilities/region.css +12 -0
  26. package/src/blueprints/utilities/visually-hidden.css +18 -0
  27. package/src/blueprints/viewports.json +10 -0
  28. package/src/generators/index.js +401 -0
  29. package/src/preview-layout.html +14 -0
  30. package/src/templates/compositions/cluster/description.html +7 -0
  31. package/src/templates/compositions/cluster/index.html +7 -0
  32. package/src/templates/compositions/flow/description.html +8 -0
  33. package/src/templates/compositions/flow/index.html +6 -0
  34. package/src/templates/compositions/grid/description.html +12 -0
  35. package/src/templates/compositions/grid/index.html +7 -0
  36. package/src/templates/compositions/prose/description.html +8 -0
  37. package/src/templates/compositions/prose/index.html +6 -0
  38. package/src/templates/compositions/repel/description.html +9 -0
  39. package/src/templates/compositions/repel/index.html +4 -0
  40. package/src/templates/compositions/sidebar/description.html +13 -0
  41. package/src/templates/compositions/sidebar/index.html +4 -0
  42. package/src/templates/compositions/switcher/description.html +8 -0
  43. package/src/templates/compositions/switcher/index.html +29 -0
  44. package/src/templates/compositions/wrapper/description.html +8 -0
  45. package/src/templates/compositions/wrapper/index.html +5 -0
  46. package/src/templates/prose.html +366 -0
  47. package/src/templates/script.js +44 -0
  48. package/src/templates/utilities/region/description.html +8 -0
  49. package/src/templates/utilities/region/index.html +5 -0
  50. package/src/templates/utilities/visually-hidden/description.html +7 -0
  51. package/src/templates/utilities/visually-hidden/index.html +7 -0
  52. package/src/tokens.js +428 -0
  53. package/src/utils/clamp-generator.js +38 -0
package/build.js CHANGED
@@ -231,6 +231,26 @@ function buildInitManifest(settings) {
231
231
  });
232
232
  }
233
233
 
234
+ // Utility and composition display templates — walk each subfolder
235
+ for (const section of ["utilities", "compositions"]) {
236
+ const sectionSrc = path.join(templatesDir, section);
237
+ if (!fs.existsSync(sectionSrc)) continue;
238
+ const folders = fs.readdirSync(sectionSrc).filter(f =>
239
+ fs.statSync(path.join(sectionSrc, f)).isDirectory()
240
+ );
241
+ for (const folder of folders) {
242
+ const folderSrc = path.join(sectionSrc, folder);
243
+ const files = fs.readdirSync(folderSrc).filter(f => f.endsWith(".html"));
244
+ for (const file of files) {
245
+ manifest.push({
246
+ src: path.join(folderSrc, file),
247
+ dest: path.join(settings.swatchkitDir, section, folder, file),
248
+ transform: (content) => content.trim(),
249
+ });
250
+ }
251
+ }
252
+ }
253
+
234
254
  // CSS entry point
235
255
  manifest.push({
236
256
  src: path.join(blueprintsDir, "main.css"),
@@ -279,6 +299,8 @@ function getInitDirs(settings) {
279
299
  settings.swatchkitDir,
280
300
  settings.tokensDir,
281
301
  path.join(settings.swatchkitDir, "tokens"),
302
+ path.join(settings.swatchkitDir, "utilities"),
303
+ path.join(settings.swatchkitDir, "compositions"),
282
304
  settings.cssDir,
283
305
  path.join(settings.cssDir, "global"),
284
306
  ];
@@ -544,7 +566,7 @@ function scanSwatches(dir, scriptsCollector, exclude = []) {
544
566
  const itemPath = path.join(dir, item);
545
567
  const stat = fs.statSync(itemPath);
546
568
 
547
- let name, content, id;
569
+ let name, content, id, description;
548
570
 
549
571
  // Handle Component Directory
550
572
  if (stat.isDirectory()) {
@@ -555,6 +577,12 @@ function scanSwatches(dir, scriptsCollector, exclude = []) {
555
577
  id = item;
556
578
  content = fs.readFileSync(indexFile, "utf-8");
557
579
 
580
+ // Optional description shown above the iframe in the main UI
581
+ const descriptionFile = path.join(itemPath, "description.html");
582
+ if (fs.existsSync(descriptionFile)) {
583
+ description = fs.readFileSync(descriptionFile, "utf-8");
584
+ }
585
+
558
586
  // Find all .js files
559
587
  const jsFiles = fs
560
588
  .readdirSync(itemPath)
@@ -594,7 +622,7 @@ ${scriptContent}
594
622
  }
595
623
 
596
624
  if (name && content) {
597
- swatches.push({ name, id, content });
625
+ swatches.push({ name, id, content, description: description || null });
598
626
  }
599
627
  });
600
628
 
@@ -791,6 +819,7 @@ function build(settings) {
791
819
  return `
792
820
  <section id="${p.id}" class="region flow">
793
821
  <h2>${p.name} <small style="font-weight: normal; opacity: 0.6; font-size: 0.7em">(${section})</small></h2>
822
+ ${p.description ? `<div class="swatch-description">${p.description}</div>` : ''}
794
823
  <iframe src="${previewPath}" style="width: 100%; border: var(--stroke); min-height: 25rem; resize: vertical; overflow: auto;"></iframe>
795
824
  <div class="swatchkit-preview-link"><a href="${previewLink}">View full screen</a></div>
796
825
  <details>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "swatchkit",
3
- "version": "1.0.1",
3
+ "version": "1.1.1",
4
4
  "description": "A lightweight tool for creating HTML pattern libraries.",
5
5
  "main": "build.js",
6
6
  "bin": {
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "files": [
10
10
  "build.js",
11
- "src/layout.html"
11
+ "src/"
12
12
  ],
13
13
  "scripts": {
14
14
  "start": "node build.js",
@@ -0,0 +1,11 @@
1
+ {
2
+ "title": "Colors",
3
+ "description": "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.",
4
+ "items": [
5
+ { "name": "color-dark", "value": "#1a2420" },
6
+ { "name": "color-dark-glare", "value": "#2d3830" },
7
+ { "name": "color-mid", "value": "#8a9a8e" },
8
+ { "name": "color-light", "value": "#f8faf7" },
9
+ { "name": "color-primary", "value": "#e07a5f" }
10
+ ]
11
+ }
@@ -0,0 +1,20 @@
1
+ /*
2
+ CLUSTER
3
+ Distributes items with consistent spacing, regardless of their size.
4
+
5
+ Credit: Adapted from Every Layout (The Cluster)
6
+ Docs: https://every-layout.dev/layouts/cluster/
7
+
8
+ CUSTOM PROPERTIES:
9
+ --gutter: Space between items (default: --space-m)
10
+ --cluster-horizontal-alignment: Horizontal alignment (default: flex-start)
11
+ --cluster-vertical-alignment: Vertical alignment (default: center)
12
+ */
13
+
14
+ .cluster {
15
+ display: flex;
16
+ flex-wrap: wrap;
17
+ gap: var(--gutter, var(--space-m));
18
+ justify-content: var(--cluster-horizontal-alignment, flex-start);
19
+ align-items: var(--cluster-vertical-alignment, center);
20
+ }
@@ -0,0 +1,10 @@
1
+ /*
2
+ FLOW
3
+ Adds vertical rhythm between direct siblings.
4
+
5
+ Credit: Adapted from Every Layout (The Stack)
6
+ Docs: https://every-layout.dev/layouts/stack/
7
+ */
8
+ .flow > * + * {
9
+ margin-block-start: var(--flow-space, 1em);
10
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Auto Grid Swatch
3
+ *
4
+ * A flexible layout that creates an auto-filling grid with configurable item sizes.
5
+ * Implements the "RAM" (Repeat, Auto, Minmax) pattern to adapt without media queries.
6
+ *
7
+ * Related: https://every-layout.dev/layouts/grid/
8
+ *
9
+ * Usage: <div class="grid">...</div>
10
+ */
11
+
12
+ .grid {
13
+ /*
14
+ * --gutter: Space between items
15
+ * --grid-min-item-size: Minimum width of an item before wrapping
16
+ * --grid-placement: auto-fill (default) or auto-fit
17
+ */
18
+ display: grid;
19
+ grid-template-columns: repeat(
20
+ var(--grid-placement, auto-fill),
21
+ minmax(var(--grid-min-item-size, 16rem), 1fr)
22
+ );
23
+ gap: var(--gutter, var(--space-l));
24
+ }
25
+
26
+ /* A split 50/50 layout */
27
+ .grid[data-layout="50-50"] {
28
+ --grid-placement: auto-fit;
29
+ --grid-min-item-size: clamp(16rem, 50vw, 33rem);
30
+ }
31
+
32
+ /* Three column grid layout */
33
+ .grid[data-layout="thirds"] {
34
+ --grid-placement: auto-fit;
35
+ --grid-min-item-size: clamp(16rem, 33%, 20rem);
36
+ }
@@ -0,0 +1,12 @@
1
+ /* === CSS Compositions === */
2
+
3
+ /* Layout primitives based on CUBE CSS / Every Layout */
4
+
5
+ @import "flow.css";
6
+ @import "sidebar.css";
7
+ @import "wrapper.css";
8
+ @import "cluster.css";
9
+ @import "repel.css";
10
+ @import "switcher.css";
11
+ @import "prose.css";
12
+ @import "grid.css";
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Prose Swatch
3
+ *
4
+ * A CUBE CSS "Block" designed to optimize the reading experience for long-form content.
5
+ * It manages spacing, line length, and typography for a comfortable rhythm.
6
+ *
7
+ * Usage: <article class="prose flow">...</article>
8
+ */
9
+
10
+ .prose {
11
+ /* Increase flow spacing for better reading rhythm */
12
+ --flow-space: var(--space-l);
13
+ }
14
+
15
+ /**
16
+ * Prevent large headings from breaking layout on small screens.
17
+ * hyphenation and break-word handling ensures text wraps gracefully.
18
+ */
19
+ .prose :is(h1, h2, h3) {
20
+ overflow-wrap: anywhere;
21
+ hyphens: auto;
22
+ }
23
+
24
+ /**
25
+ * Constrain line length for optimal readability (measure).
26
+ * text-wrap: pretty prevents orphans and awkward breaks.
27
+ */
28
+ .prose :is(p, li, dl, figcaption, blockquote) {
29
+ max-width: 60ch;
30
+ text-wrap: pretty;
31
+ }
32
+
33
+ /**
34
+ * Reduce spacing after headings to visually group them with their content.
35
+ */
36
+ .prose :is(h1, h2, h3, h4) + *:not([class]) {
37
+ --flow-space: var(--space-m);
38
+ }
39
+
40
+ /**
41
+ * Add more breathing room around heavy elements like figures and tables.
42
+ */
43
+ .prose :is(figure, table),
44
+ .prose :is(figure, table) + * {
45
+ --flow-space: var(--space-2xl);
46
+ }
47
+
48
+ /**
49
+ * Add extra space before headings to clearly separate sections.
50
+ */
51
+ .prose * + :is(h1, h2, h3, h4):not([class]) {
52
+ --flow-space: var(--space-xl);
53
+ }
54
+
55
+ /**
56
+ * Tighter spacing for list items to keep lists cohesive.
57
+ */
58
+ .prose :is(ul, ol):not([class]) li + li,
59
+ .prose :is(ul, ol):not([class]) li > :is(ol, ul) {
60
+ --flow-space: var(--space-xs);
61
+ }
62
+
63
+ /**
64
+ * Horizontal rules act as major section breaks, so give them plenty of space.
65
+ */
66
+ .prose hr {
67
+ --flow-space: var(--space-2xl);
68
+ }
69
+
70
+ /**
71
+ * Add a border to media elements to prevent them from blending into the background,
72
+ * especially in light mode.
73
+ */
74
+ .prose :is(img, picture, video) {
75
+ border: var(--stroke-solid);
76
+ }
77
+
78
+ /**
79
+ * On larger screens, relax the aggressive wrapping rules for headings.
80
+ * 47.5em matches 760px, the default 'viewport-mid' token.
81
+ * Preferring em here to catch the case where a user adjusts their browser default font size.
82
+ * Note: CSS variables cannot be used in media queries in standard CSS.
83
+ */
84
+ @media (min-width: 47.5em) {
85
+ .prose :is(h1, h2, h3) {
86
+ overflow-wrap: unset;
87
+ hyphens: unset;
88
+ }
89
+ }
@@ -0,0 +1,23 @@
1
+ /*
2
+ REPEL
3
+ Pushes items away from each other where space allows, stacking on small viewports.
4
+
5
+ Credit: Adapted from Every Layout (The Cluster)
6
+ Docs: https://every-layout.dev/layouts/cluster/
7
+
8
+ CUSTOM PROPERTIES:
9
+ --gutter: Space between items (default: --space-m)
10
+ --repel-vertical-alignment: Vertical alignment (default: center)
11
+ */
12
+
13
+ .repel {
14
+ display: flex;
15
+ flex-wrap: wrap;
16
+ justify-content: space-between;
17
+ align-items: var(--repel-vertical-alignment, center);
18
+ gap: var(--gutter, var(--space-m));
19
+ }
20
+
21
+ .repel[data-nowrap] {
22
+ flex-wrap: nowrap;
23
+ }
@@ -0,0 +1,44 @@
1
+ /*
2
+ SIDEBAR
3
+ Layout with a flexible main content area and a fixed-width sidebar.
4
+
5
+ Credit: Adapted from Every Layout (The Sidebar)
6
+ Docs: https://every-layout.dev/layouts/sidebar/
7
+
8
+ CUSTOM PROPERTIES:
9
+ --gutter: Space between sidebar and content (default: --space-s-l)
10
+ --sidebar-target-width: Target width of sidebar (default: 20rem)
11
+ --sidebar-content-min-width: Min width of content before stacking (default: 50%)
12
+ */
13
+
14
+ .sidebar {
15
+ display: flex;
16
+ flex-wrap: wrap;
17
+ gap: var(--gutter, var(--space-s-l));
18
+ }
19
+
20
+ .sidebar:not([data-direction]) > :first-child {
21
+ flex-basis: var(--sidebar-target-width, 20rem);
22
+ flex-grow: 1;
23
+ }
24
+
25
+ .sidebar:not([data-direction]) > :last-child {
26
+ flex-basis: 0;
27
+ flex-grow: 999;
28
+ min-width: var(--sidebar-content-min-width, 50%);
29
+ }
30
+
31
+ .sidebar[data-reversed] {
32
+ flex-direction: row-reverse;
33
+ }
34
+
35
+ .sidebar[data-direction="rtl"] > :last-child {
36
+ flex-basis: var(--sidebar-target-width, 20rem);
37
+ flex-grow: 1;
38
+ }
39
+
40
+ .sidebar[data-direction="rtl"] > :first-child {
41
+ flex-basis: 0;
42
+ flex-grow: 999;
43
+ min-width: var(--sidebar-content-min-width, 50%);
44
+ }
@@ -0,0 +1,32 @@
1
+ /*
2
+ SWITCHER
3
+ Lays out items horizontally until there is insufficient space, then stacks them.
4
+ Optimized for 3 items.
5
+
6
+ Credit: Adapted from Every Layout (The Switcher)
7
+ Docs: https://every-layout.dev/layouts/switcher/
8
+
9
+ CUSTOM PROPERTIES:
10
+ --gutter: Space between items (default: --space-l)
11
+ --switcher-target-container-width: Container breakpoint width (default: 40rem)
12
+ --switcher-vertical-alignment: Vertical alignment (default: flex-start)
13
+ */
14
+
15
+ .switcher {
16
+ display: flex;
17
+ flex-wrap: wrap;
18
+ gap: var(--gutter, var(--space-l));
19
+ align-items: var(--switcher-vertical-alignment, flex-start);
20
+ }
21
+
22
+ .switcher > * {
23
+ flex-grow: 1;
24
+ flex-basis: calc(
25
+ (var(--switcher-target-container-width, 40rem) - 100%) * 999
26
+ );
27
+ }
28
+
29
+ /* Max 3 items, so anything greater than 3 is full width */
30
+ .switcher > :nth-child(n + 4) {
31
+ flex-basis: 100%;
32
+ }
@@ -0,0 +1,14 @@
1
+ /*
2
+ WRAPPER
3
+
4
+ Credit: More or less, adapted from Every Layout (The Center)
5
+ Docs: https://every-layout.dev/layouts/center/
6
+ */
7
+
8
+ .wrapper {
9
+ margin-inline: auto;
10
+ max-inline-size: var(--wrapper-max-width, 1360px);
11
+ padding-inline-start: var(--gutter);
12
+ padding-inline-end: var(--gutter);
13
+ position: relative;
14
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "title": "Fonts",
3
+ "description": "Each array of fonts creates a priority-based order. The first font in the array should be the ideal font, followed by sensible, web-safe fallbacks",
4
+ "items": [
5
+ {
6
+ "name": "font-base",
7
+ "description": "System fonts for body copy and globally set text.",
8
+ "value": [
9
+ "Satoshi",
10
+ "Inter",
11
+ "Segoe UI",
12
+ "Roboto",
13
+ "Helvetica Neue",
14
+ "Arial",
15
+ "sans-serif"
16
+ ]
17
+ },
18
+ {
19
+ "name": "font-mono",
20
+ "description": "Monospace font for code samples etc",
21
+ "value": ["DM Mono", "monospace"]
22
+ }
23
+ ]
24
+ }