swatchkit 1.1.0 → 2.0.0
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/build.js +82 -90
- 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/swatches/hello.css +11 -0
- package/src/blueprints/swatches/index.css +7 -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 +437 -0
- package/src/layout.html +0 -1
- package/src/preview-layout.html +13 -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/hello/README.md +83 -0
- package/src/templates/hello/index.html +1 -0
- package/src/templates/prose.html +366 -0
- package/src/templates/script.js +41 -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
package/build.js
CHANGED
|
@@ -175,10 +175,8 @@ function resolveSettings(cliOptions, fileConfig) {
|
|
|
175
175
|
// Derived paths
|
|
176
176
|
distCssDir: path.join(outDir, "css"),
|
|
177
177
|
distTokensCssFile: path.join(outDir, "css", "tokens.css"),
|
|
178
|
-
distJsDir: path.join(outDir, "js"),
|
|
179
178
|
distPreviewDir: path.join(outDir, "preview"),
|
|
180
179
|
outputFile: path.join(outDir, "index.html"),
|
|
181
|
-
outputJsFile: path.join(outDir, "js/swatches.js"),
|
|
182
180
|
tokensCssFile: path.join(cssDir, "global", "tokens.css"),
|
|
183
181
|
mainCssFile: path.join(cssDir, "main.css"),
|
|
184
182
|
};
|
|
@@ -231,6 +229,22 @@ function buildInitManifest(settings) {
|
|
|
231
229
|
});
|
|
232
230
|
}
|
|
233
231
|
|
|
232
|
+
// Hello swatch (default example in swatchkit/swatches/hello/)
|
|
233
|
+
for (const file of ["index.html", "README.md"]) {
|
|
234
|
+
manifest.push({
|
|
235
|
+
src: path.join(templatesDir, "hello", file),
|
|
236
|
+
dest: path.join(settings.swatchkitDir, "swatches", "hello", file),
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Swatches CSS folder (css/swatches/)
|
|
241
|
+
for (const file of ["index.css", "hello.css"]) {
|
|
242
|
+
manifest.push({
|
|
243
|
+
src: path.join(blueprintsDir, "swatches", file),
|
|
244
|
+
dest: path.join(settings.cssDir, "swatches", file),
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
234
248
|
// Utility and composition display templates — walk each subfolder
|
|
235
249
|
for (const section of ["utilities", "compositions"]) {
|
|
236
250
|
const sectionSrc = path.join(templatesDir, section);
|
|
@@ -301,8 +315,11 @@ function getInitDirs(settings) {
|
|
|
301
315
|
path.join(settings.swatchkitDir, "tokens"),
|
|
302
316
|
path.join(settings.swatchkitDir, "utilities"),
|
|
303
317
|
path.join(settings.swatchkitDir, "compositions"),
|
|
318
|
+
path.join(settings.swatchkitDir, "swatches"),
|
|
319
|
+
path.join(settings.swatchkitDir, "swatches", "hello"),
|
|
304
320
|
settings.cssDir,
|
|
305
321
|
path.join(settings.cssDir, "global"),
|
|
322
|
+
path.join(settings.cssDir, "swatches"),
|
|
306
323
|
];
|
|
307
324
|
}
|
|
308
325
|
|
|
@@ -550,7 +567,26 @@ function copyDir(src, dest, force = false) {
|
|
|
550
567
|
}
|
|
551
568
|
}
|
|
552
569
|
|
|
553
|
-
|
|
570
|
+
// Special filenames that SwatchKit reads and treats differently — not copied verbatim.
|
|
571
|
+
const SWATCH_SPECIAL_FILES = new Set(["index.html", "description.html"]);
|
|
572
|
+
|
|
573
|
+
// Recursively copy a swatch's assets (all files and subdirs except index.html,
|
|
574
|
+
// description.html, and anything prefixed with _ or .) to destDir.
|
|
575
|
+
function copySwatchAssets(srcDir, destDir) {
|
|
576
|
+
if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });
|
|
577
|
+
for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {
|
|
578
|
+
if (entry.name.startsWith("_") || entry.name.startsWith(".")) continue;
|
|
579
|
+
const src = path.join(srcDir, entry.name);
|
|
580
|
+
const dest = path.join(destDir, entry.name);
|
|
581
|
+
if (entry.isDirectory()) {
|
|
582
|
+
copySwatchAssets(src, dest);
|
|
583
|
+
} else if (!SWATCH_SPECIAL_FILES.has(entry.name)) {
|
|
584
|
+
fs.copyFileSync(src, dest);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
function scanSwatches(dir, destDir, exclude = []) {
|
|
554
590
|
const swatches = [];
|
|
555
591
|
if (!fs.existsSync(dir)) return swatches;
|
|
556
592
|
|
|
@@ -583,23 +619,14 @@ function scanSwatches(dir, scriptsCollector, exclude = []) {
|
|
|
583
619
|
description = fs.readFileSync(descriptionFile, "utf-8");
|
|
584
620
|
}
|
|
585
621
|
|
|
586
|
-
//
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
"utf-8",
|
|
595
|
-
);
|
|
596
|
-
scriptsCollector.push(`
|
|
597
|
-
/* --- Swatch: ${name} / File: ${jsFile} --- */
|
|
598
|
-
(function() {
|
|
599
|
-
${scriptContent}
|
|
600
|
-
})();
|
|
601
|
-
`);
|
|
602
|
-
});
|
|
622
|
+
// Copy all non-special files and subdirectories from the component
|
|
623
|
+
// folder into its preview output directory so index.html can reference
|
|
624
|
+
// them with relative paths (e.g. ./styles.css, ./script.js, ./img/).
|
|
625
|
+
// Files and directories prefixed with _ or . are skipped.
|
|
626
|
+
if (destDir) {
|
|
627
|
+
const swatchDestDir = path.join(destDir, item);
|
|
628
|
+
copySwatchAssets(itemPath, swatchDestDir);
|
|
629
|
+
}
|
|
603
630
|
}
|
|
604
631
|
}
|
|
605
632
|
// Handle Single File
|
|
@@ -608,18 +635,6 @@ ${scriptContent}
|
|
|
608
635
|
id = name;
|
|
609
636
|
content = fs.readFileSync(itemPath, "utf-8");
|
|
610
637
|
}
|
|
611
|
-
// Handle Loose JS Files (e.g. script.js in tokens/)
|
|
612
|
-
else if (item.endsWith(".js")) {
|
|
613
|
-
const scriptContent = fs.readFileSync(itemPath, "utf-8");
|
|
614
|
-
scriptsCollector.push(`
|
|
615
|
-
/* --- File: ${item} --- */
|
|
616
|
-
(function() {
|
|
617
|
-
${scriptContent}
|
|
618
|
-
})();
|
|
619
|
-
`);
|
|
620
|
-
// Don't add to swatches list, just scripts
|
|
621
|
-
return;
|
|
622
|
-
}
|
|
623
638
|
|
|
624
639
|
if (name && content) {
|
|
625
640
|
swatches.push({ name, id, content, description: description || null });
|
|
@@ -668,7 +683,7 @@ function build(settings) {
|
|
|
668
683
|
}
|
|
669
684
|
|
|
670
685
|
// 3. Ensure dist directories exist
|
|
671
|
-
const distDirs = [settings.outDir
|
|
686
|
+
const distDirs = [settings.outDir];
|
|
672
687
|
if (settings.cssCopy) distDirs.push(settings.distCssDir);
|
|
673
688
|
distDirs.forEach((dir) => {
|
|
674
689
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
@@ -701,9 +716,8 @@ function build(settings) {
|
|
|
701
716
|
console.log(`Skipping CSS copy (cssCopy: false). CSS referenced at: ${settings.cssPath}`);
|
|
702
717
|
}
|
|
703
718
|
|
|
704
|
-
// 4. Read swatches
|
|
719
|
+
// 4. Read swatches
|
|
705
720
|
console.log("Scanning HTML patterns (swatchkit/**/*.html)...");
|
|
706
|
-
const scripts = [];
|
|
707
721
|
const sections = {}; // Map<SectionName, Array<Swatch>>
|
|
708
722
|
|
|
709
723
|
if (fs.existsSync(settings.swatchkitDir)) {
|
|
@@ -723,7 +737,9 @@ function build(settings) {
|
|
|
723
737
|
// It is a Section Container (e.g. "Utilities")
|
|
724
738
|
const sectionName =
|
|
725
739
|
item === "tokens" ? "Design Tokens" : toTitleCase(item);
|
|
726
|
-
|
|
740
|
+
// Pass the preview dest dir so extra files get copied alongside each swatch
|
|
741
|
+
const sectionDestDir = path.join(settings.distPreviewDir, item);
|
|
742
|
+
const swatches = scanSwatches(itemPath, sectionDestDir, exclude);
|
|
727
743
|
// Tag each swatch with its section slug for preview paths
|
|
728
744
|
swatches.forEach((s) => (s.sectionSlug = item));
|
|
729
745
|
if (swatches.length > 0) {
|
|
@@ -749,24 +765,19 @@ function build(settings) {
|
|
|
749
765
|
} else if (stat.isDirectory()) {
|
|
750
766
|
const indexFile = path.join(itemPath, "index.html");
|
|
751
767
|
if (fs.existsSync(indexFile)) {
|
|
752
|
-
// Component folder swatch at root
|
|
768
|
+
// Component folder swatch at root — copy extra files to preview dest
|
|
753
769
|
const name = item;
|
|
754
770
|
const content = fs.readFileSync(indexFile, "utf-8");
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
);
|
|
766
|
-
scripts.push(
|
|
767
|
-
`/* ${name}/${jsFile} */ (function(){${scriptContent}})();`,
|
|
768
|
-
);
|
|
769
|
-
});
|
|
771
|
+
const descriptionFile = path.join(itemPath, "description.html");
|
|
772
|
+
const description = fs.existsSync(descriptionFile)
|
|
773
|
+
? fs.readFileSync(descriptionFile, "utf-8")
|
|
774
|
+
: null;
|
|
775
|
+
|
|
776
|
+
// Copy extra files into preview/id/
|
|
777
|
+
const swatchDestDir = path.join(settings.distPreviewDir, name);
|
|
778
|
+
copySwatchAssets(itemPath, swatchDestDir);
|
|
779
|
+
|
|
780
|
+
rootSwatches.push({ name, id: name, content, description, sectionSlug: null });
|
|
770
781
|
}
|
|
771
782
|
}
|
|
772
783
|
});
|
|
@@ -810,11 +821,13 @@ function build(settings) {
|
|
|
810
821
|
.replace(/"/g, """)
|
|
811
822
|
.replace(/'/g, "'");
|
|
812
823
|
|
|
813
|
-
// Build preview path: preview/{section}/{
|
|
824
|
+
// Build preview path: preview/{section}/{id}/ or preview/{id}/
|
|
825
|
+
// Each swatch is a directory with its own index.html so sibling assets
|
|
826
|
+
// (css, js, images, etc.) can be referenced with relative paths.
|
|
814
827
|
const previewPath = p.sectionSlug
|
|
815
|
-
? `preview/${p.sectionSlug}/${p.id}
|
|
816
|
-
: `preview/${p.id}
|
|
817
|
-
const previewLink = previewPath
|
|
828
|
+
? `preview/${p.sectionSlug}/${p.id}/`
|
|
829
|
+
: `preview/${p.id}/`;
|
|
830
|
+
const previewLink = previewPath;
|
|
818
831
|
|
|
819
832
|
return `
|
|
820
833
|
<section id="${p.id}" class="region flow">
|
|
@@ -832,23 +845,9 @@ function build(settings) {
|
|
|
832
845
|
.join("\n");
|
|
833
846
|
});
|
|
834
847
|
|
|
835
|
-
// 6.
|
|
836
|
-
//
|
|
837
|
-
//
|
|
838
|
-
const tokenDisplayScript = fs.readFileSync(
|
|
839
|
-
path.join(__dirname, "src/templates/script.js"),
|
|
840
|
-
"utf-8",
|
|
841
|
-
);
|
|
842
|
-
const internalScript = `/* --- SwatchKit: token display --- */\n(function() {\n${tokenDisplayScript}\n})();`;
|
|
843
|
-
const allScripts = [internalScript, ...scripts];
|
|
844
|
-
fs.writeFileSync(settings.outputJsFile, allScripts.join("\n"));
|
|
845
|
-
if (scripts.length > 0) {
|
|
846
|
-
console.log(
|
|
847
|
-
`Bundled ${scripts.length} swatch scripts to ${settings.outputJsFile}`,
|
|
848
|
-
);
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
// 7. Generate preview pages (standalone full-screen view of each swatch)
|
|
848
|
+
// 6. Generate preview pages (standalone full-screen view of each swatch)
|
|
849
|
+
// Each swatch gets its own directory: preview/{section}/{id}/index.html
|
|
850
|
+
// This allows index.html to reference sibling assets with relative paths.
|
|
852
851
|
let previewLayoutContent;
|
|
853
852
|
if (fs.existsSync(settings.projectPreviewLayout)) {
|
|
854
853
|
previewLayoutContent = fs.readFileSync(
|
|
@@ -867,32 +866,25 @@ function build(settings) {
|
|
|
867
866
|
sortedKeys.forEach((section) => {
|
|
868
867
|
const swatches = sections[section];
|
|
869
868
|
swatches.forEach((p) => {
|
|
870
|
-
//
|
|
871
|
-
//
|
|
872
|
-
//
|
|
873
|
-
let
|
|
869
|
+
// Each swatch is output as a directory with index.html inside.
|
|
870
|
+
// preview/{section}/{id}/index.html — depth: 3 levels from outDir
|
|
871
|
+
// preview/{id}/index.html — depth: 2 levels from outDir
|
|
872
|
+
let swatchDir, cssPath;
|
|
874
873
|
if (p.sectionSlug) {
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
fs.mkdirSync(sectionDir, { recursive: true });
|
|
878
|
-
previewFile = path.join(sectionDir, `${p.id}.html`);
|
|
879
|
-
cssPath = "../../" + settings.cssPath; // preview/section/file.html -> ../../ + cssPath
|
|
874
|
+
swatchDir = path.join(settings.distPreviewDir, p.sectionSlug, p.id);
|
|
875
|
+
cssPath = "../../../" + settings.cssPath; // preview/section/id/index.html -> ../../../ + cssPath
|
|
880
876
|
} else {
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
previewFile = path.join(settings.distPreviewDir, `${p.id}.html`);
|
|
884
|
-
cssPath = "../" + settings.cssPath; // preview/file.html -> ../ + cssPath
|
|
877
|
+
swatchDir = path.join(settings.distPreviewDir, p.id);
|
|
878
|
+
cssPath = "../../" + settings.cssPath; // preview/id/index.html -> ../../ + cssPath
|
|
885
879
|
}
|
|
886
880
|
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
const jsPath = p.sectionSlug ? "../../" : "../";
|
|
881
|
+
if (!fs.existsSync(swatchDir)) fs.mkdirSync(swatchDir, { recursive: true });
|
|
882
|
+
const previewFile = path.join(swatchDir, "index.html");
|
|
890
883
|
|
|
891
884
|
const previewHtml = previewLayoutContent
|
|
892
885
|
.replace("<!-- PREVIEW_TITLE -->", p.name)
|
|
893
886
|
.replace("<!-- PREVIEW_CONTENT -->", p.content)
|
|
894
887
|
.replaceAll("<!-- CSS_PATH -->", cssPath)
|
|
895
|
-
.replaceAll("<!-- JS_PATH -->", jsPath)
|
|
896
888
|
.replace("<!-- HEAD_EXTRAS -->", "");
|
|
897
889
|
|
|
898
890
|
fs.writeFileSync(previewFile, previewHtml);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "swatchkit",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
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/
|
|
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,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
|
+
}
|