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.
It follows the "Magic Folder" principle: drop files in, and a library comes out.
Try it instantly in any project:
# 1. Initialize the layout and design tokens
npx swatchkit init
# 2. Build the library
npx swatchkitThis will create:
my-project/
βββ tokens/ # Design token definitions (you edit these)
β βββ colors.json
β βββ fonts.json
β βββ spacing.json
β βββ ...
βββ css/
β βββ compositions/ # Layout primitives (flow, sidebar, etc.)
β βββ tokens.css # Generated from tokens/*.json
β βββ main.css # Main stylesheet (imports tokens + compositions)
β βββ swatchkit-ui.css # UI styles for the documentation sidebar
βββ swatchkit/
β βββ _layout.html # Layout template (you own this)
β βββ tokens/ # Visual documentation for design tokens
β βββ colors.html
β βββ typography.html
β βββ ...
βββ dist/
βββ swatchkit/ # Built pattern library
βββ index.html
βββ js/
β βββ swatches.js # Bundled swatch scripts
βββ preview/ # Full-screen preview pages
For real projects, install SwatchKit as a development dependency to lock the version.
npm install -D swatchkitThen add it to your package.json scripts:
"scripts": {
"patterns": "swatchkit"
}By default, SwatchKit looks for a swatchkit/ folder in your project root.
Organize by Folder: SwatchKit automatically turns subfolders into sections in the documentation sidebar.
swatchkit/
βββ tokens/ # Section: "Design Tokens" (visual previews)
β βββ colors.html
β βββ typography.html
βββ components/ # Section: "Components"
β βββ button.html
β βββ card/
β βββ index.html
βββ compositions/ # Section: "Compositions"
β βββ sidebar.html
βββ utilities/ # Section: "Utilities"
βββ flow.html
- Files at root: Go to the "Patterns" section.
- Subfolders: Create a new section (e.g.
utilities/-> "Utilities").
SwatchKit scaffolds a design system for you. Edit the JSON files in tokens/, and SwatchKit auto-generates css/tokens.css.
Supported Tokens:
- Colors (
colors.json): Generates palettes. - Fluid Typography (
text-sizes.json): Generatesclamp()based type scales using Utopia methodology. - Fluid Spacing (
spacing.json): Generatesclamp()based spacing. - Modular Leading (
text-leading.json): Generates line-heights usingpow()modular scales. - Fonts & Weights: Manages font families and weights.
Visual documentation patterns for these tokens live in swatchkit/tokens/ and are created during init.
SwatchKit can auto-calculate fluid typography and spacing scales.
Static vs Fluid:
- Static: Provide a
value(e.g."16px"). - Fluid: Provide
minandmax(e.g.16and18). - Auto-Fluid: Provide just ONE side (
minormax), and SwatchKit calculates the other using a default ratio (1.125).
Example (tokens/text-sizes.json):
{
"title": "Text Sizes",
"fluidRatio": 1.25,
"items": [
{ "name": "base", "value": "1rem" }, // Static: 1rem always
{ "name": "md", "min": 16, "max": 20 }, // Fluid: 16px -> 20px
{ "name": "lg", "max": 24 }, // Auto: 19.2px -> 24px (24 / 1.25)
{ "name": "xl", "min": 32 }, // Auto: 32px -> 40px (32 * 1.25)
{ "name": "jumbo", "max": 64, "fluidRatio": 1.5 } // Auto: 42.6px -> 64px (64 / 1.5)
]
}Generated CSS:
:root {
--s-base: 1rem;
--s-md: clamp(1rem, ..., 1.25rem);
--s-lg: clamp(1.2rem, ..., 1.5rem);
--s-xl: clamp(2rem, ..., 2.5rem);
--s-jumbo: clamp(2.66rem, ..., 4rem);
}You can mix modular scales with manual overrides.
Example (tokens/text-leading.json):
{
"base": 1,
"ratio": 1.2,
"items": [
{ "name": "tight", "step": -1 }, // Modular: 1 * (1.2 ^ -1)
{ "name": "flat", "value": 1 }, // Manual: 1
{ "name": "loose", "step": 1 } // Modular: 1 * (1.2 ^ 1)
]
}SwatchKit generates css/tokens.css with your design tokens. Your css/main.css imports this file along with layout primitives:
@import 'tokens.css';
@import 'compositions/index.css';
body {
font-family: var(--font-base);
color: var(--color-dark);
}The pattern library uses your stylesheet (main.css), so components render exactly as they will in your app.
Documentation Styling:
The sidebar and documentation layout are styled by css/swatchkit-ui.css. This file is separate from your app styles so you can customize the docs UI without affecting your production CSS.
When you run swatchkit init, we create swatchkit/_layout.html.
You own this file.
- Link to your own stylesheets.
- Add custom fonts, scripts, or meta tags.
- Change the HTML structure, logo, or classes.
SwatchKit injects content into the <!-- PATTERNS -->, <!-- SIDEBAR_LINKS -->, and <!-- HEAD_EXTRAS --> placeholders.
If your component needs client-side JS:
- Create a folder:
swatchkit/carousel/. - Add
index.html(Markup). - Add
script.js(Logic).
SwatchKit automatically bundles your JS files, wraps them in a safety scope (IIFE), and injects them into the final build.
Understanding the build pipeline helps you know which files to edit and which are generated.
Copies "blueprints" into your project to get you started. Init tracks a manifest of every file it manages (token JSONs, CSS blueprints, layout templates) so it can report what's new, changed, or up to date.
- Fresh project: Creates directories and copies all blueprint files.
- Already initialized: Prints a status report comparing your files to the latest blueprints. Suggests
--forceif anything has changed. --force: Overwrites all init-managed files with the latest blueprints. Your custom swatch HTML files and any CSS files without blueprint counterparts are never touched.--dry-run: Shows what would happen without writing anything.
Files created:
tokens/*.json: These are your Source of Truth. You edit these files.css/: Copies static CSS files (main.css,reset.css, compositions, etc.). You own these files.swatchkit/: Sets up the documentation structure, layout templates, and token display patterns.
Compiles your documentation site into dist/swatchkit/.
- Reads JSON Tokens: Scans
tokens/*.jsonand calculates fluid typography/spacing. - Generates CSS: Creates
css/tokens.css. Do not edit this file; it is overwritten every build. - Copies Assets: Copies your
css/folder (including your manually editedglobal-styles.cssandmain.css) to the output folder. - Scans Patterns: Finds all HTML files in
swatchkit/and stitches them into the documentation site. - Bundles JavaScript: Collects any
.jsfiles from swatch folders (e.g.,swatchkit/carousel/script.js) and section directories into a singlejs/swatches.jsbundle. The default token display script (swatchkit/tokens/script.js) is included here β it shows computed CSS values alongside token documentation.
SwatchKit includes sensible defaults in css/variables.css and css/global-styles.css.
- These are static files copied to your project during
init. - They are disabled by default (commented out in
main.css). - Action Required: Open these files, verify that the variable names match your
tokens/*.jsonnames, and then uncomment the imports inmain.cssto enable them.
| File / Folder | Safe to Edit? | Notes |
|---|---|---|
tokens/*.json |
β YES | Your source of truth. Safe. |
css/main.css |
β YES | Your entry point. Safe. |
css/global-styles.css |
β YES | You own this. Manually update if you rename tokens. |
css/tokens.css |
π« NO | Overwritten by every swatchkit build and swatchkit init. |
swatchkit/_layout.html |
β YES | Safe during normal use. init --force overwrites all init-managed files, including this one. |
swatchkit/_preview.html |
β YES | Same as _layout.html β safe unless you run init --force. |
swatchkit/tokens/*.html |
π« NO | Overwritten by swatchkit build (visual previews). |
swatchkit [command] [options]swatchkit(Default): Builds the library.swatchkit init: Scaffolds the layout and token files. If the project is already initialized, prints a status report showing which files differ from their blueprints (auto dry-run).swatchkit init --force: Overwrites all init-managed files with the latest blueprints. Custom swatch files and CSS files without blueprint counterparts are never touched.swatchkit init --dry-run: Shows what would be created or changed without writing anything.
| Flag | Short | Description |
|---|---|---|
--watch |
-w |
Watch files and rebuild on change. |
--config |
-c |
Path to config file. |
--input |
-i |
Pattern directory (Default: swatchkit/). |
--outDir |
-o |
Output directory (Default: dist/swatchkit). |
--force |
-f |
Overwrite all init-managed files with latest blueprints. |
--dry-run |
Show what init would create or change, without writing anything. | |
--help |
-h |
Show help message. |
--version |
-v |
Show version number. |
Optional. Create swatchkit.config.js in your root for persistent settings.
module.exports = {
// Override default pattern directory
input: "./patterns",
// Override default output directory
outDir: "./dist/patterns",
// Override CSS directory
cssDir: "./assets/css",
// Override tokens directory (JSON token definitions)
tokensDir: "./src/tokens",
// Skip copying CSS into SwatchKit's output directory.
// When false, SwatchKit references CSS at cssPath instead of copying it.
// See "Common Workflows" below for when to use this.
cssCopy: false,
// Relative path from SwatchKit's HTML to your CSS files.
// Only relevant when cssCopy is false.
// Defaults to "../<basename of cssDir>/" (e.g., "../css/" if cssDir is "./src/css").
// Set explicitly if your deployed CSS lives at a different path.
cssPath: "../css/",
// Exclude files (supports glob patterns)
exclude: ["*.test.js", "temp*"],
};If your build tool (Vite, Astro, etc.) already outputs CSS to dist/, you don't need SwatchKit to copy it again. Set cssCopy: false and SwatchKit's HTML will reference your existing CSS.
// swatchkit.config.js
module.exports = {
cssDir: "./src/css",
cssCopy: false,
};dist/
βββ css/ # Your build tool puts CSS here
β βββ main.css
β βββ tokens.css
βββ index.html # Your project
βββ swatchkit/
βββ index.html # References ../css/main.css
SwatchKit derives the default cssPath from your cssDir name. If cssDir is "./src/css", it defaults to "../css/". If cssDir is "./styles", it defaults to "../styles/".
If your deployed CSS ends up somewhere else (e.g., Vite hashes it into dist/assets/), set cssPath explicitly:
module.exports = {
cssDir: "./src/css",
cssCopy: false,
cssPath: "../assets/",
};If SwatchKit is just a development tool and you don't want it in dist/, set outDir to a separate directory. Keep cssCopy enabled (the default) so the output is fully self-contained.
// swatchkit.config.js
module.exports = {
cssDir: "./src/css",
outDir: "swatchkit-dist",
};my-project/
βββ dist/ # Your production build (no SwatchKit)
β βββ ...
βββ swatchkit-dist/ # Self-contained, serve locally during dev
βββ css/
β βββ main.css
β βββ tokens.css
βββ index.html
You can serve swatchkit-dist/ locally during development without affecting your production build.
SwatchKit outputs to dist/swatchkit/ by default. If your framework (Vite, Astro, etc.) cleans the dist/ directory during its build, run SwatchKit after your framework build:
{
"scripts": {
"build": "vite build && swatchkit",
"dev": "vite dev & swatchkit -w"
}
}In watch mode, SwatchKit detects when its output directory is deleted by an external tool and automatically rebuilds.
SwatchKit only ever writes inside its own output subdirectory β it will never modify or delete other files in dist/.
If you are rolling your own build system (e.g. using onchange to copy files), use a tool like npm-run-all to run your watchers in parallel.
swatchkit.config.js
module.exports = {
cssDir: "./src/css",
cssCopy: false, // Don't copy CSS (your build tool handles it)
};package.json
{
"scripts": {
"build": "rm -rf dist && mkdir -p dist && cp -r src/ dist/",
"swatchkit": "swatchkit",
"swatchkit:watch": "swatchkit --watch",
"dev:app": "onchange 'src/**/*' -- npm run build",
"dev": "npm-run-all --parallel dev:app swatchkit:watch"
},
"devDependencies": {
"npm-run-all": "^4.1.5",
"onchange": "^7.1.0"
}
}SwatchKit generates files into your source tree during each build β CSS token files (css/global/tokens.css, css/utilities/tokens.css) and token documentation HTML (swatchkit/tokens/*.html). To avoid triggering external file watchers unnecessarily, SwatchKit compares generated content against the existing file and skips the write when nothing has changed. This means most rebuilds (e.g., editing an HTML swatch) won't touch your CSS directory at all, preventing infinite rebuild loops when running alongside tools like onchange, chokidar, or framework dev servers that watch src/.
The CSS compositions included by default in SwatchKit are adapted from Every Layout by Heydon Pickering and Andy Bell. Highly recommend their documentation for a deep dive into their brilliant CSS techniques.