Documentation
Install the panel in a minute, make your CSS respond to it, and learn every control, API and workflow — including the inspector and custom-CSS escape hatches that work on any markup.
Quick start
One line before </body>. The panel, its styles and its logic inject themselves.
<script src="https://vetrisuriya.github.io/design-customizer/design-customizer.js"></script>
The panel auto-initializes with 12 presets, 6 font pairings and cookie key design-customizer. Open demo.html for a page that exercises every control.
Manual init with custom config
<script src="design-customizer.js" data-manual-init></script>
<script>
const panel = DesignCustomizer.init({
storageKey: 'my-project-theme', // unique per project
presets: [ /* replaces the built-in 12 */ ],
fonts: [ /* replaces the built-in 6 pairings */ ],
monos: [ /* replaces the built-in mono stacks */ ],
iconStyles: [ /* optional */ ]
});
</script>
data-manual-init disables auto-init so you can pass config. Calling init() twice is safe — the second call returns the existing instance.
Making your site themeable
The panel writes CSS variables onto your page's <html> element. Any CSS that reads those variables updates live — everything else can still be reached via the inspector or custom CSS.
Minimum: reference the tokens
:root {
--primary: #18181B; --accent: #E4E4E7;
--bg: #FFFFFF; --card: #FFFFFF;
--text: #18181B; --text-soft: #52525B;
--border: rgba(24,24,27,.12);
--font-heading: 'Space Grotesk', sans-serif;
--font-body: 'Inter', sans-serif;
--radius: 12px; --space-unit: 1;
}
body { background: var(--bg); color: var(--text); font-family: var(--font-body); }
.card { background: var(--card); border-radius: var(--radius); }
calc(24px * var(--space-unit))), shadows (box-shadow: var(--shadow)) and type rhythm (line-height: var(--line-height)) as you go. The demo page source is a complete worked example.Icon styles
The panel toggles one class on <body>: icon-gradient, icon-glass, icon-neu or icon-flat. Point them at your own tile class:
body.icon-gradient .icon-tile { background: linear-gradient(155deg, var(--accent-2), var(--accent-deep)); }
body.icon-glass .icon-tile { background: rgba(140,140,160,.18); backdrop-filter: blur(var(--blur)); }
body.icon-neu .icon-tile { background: var(--bg-soft); box-shadow: 6px 6px 14px rgba(0,0,0,.08); }
body.icon-flat .icon-tile { background: var(--primary); }
Configuration options
Passed once to DesignCustomizer.init(config).
| Option | Type | Default | What it does |
|---|---|---|---|
storageKey | string | "design-customizer" | Cookie + localStorage key. Use a unique key per project so themes don't collide when sites share a domain. |
presets | array | 12 built-ins | Replaces the palette. Each entry: { id, name, vars } where vars maps variable names to values. |
fonts | array | 12 pairings | Replaces the font list. Each entry: { id, label, heading, body }. |
monos | array | 5 stacks | Replaces the monospace list. Each entry: { id, label, stack }. |
iconStyles | array | 4 styles | Replaces the icon list. Each entry: { id, label } — you supply the matching CSS. |
Controls reference
Every control in the Design tab, what it writes, and its range. Search inside the panel with ⌕ to jump to any of these — and click any slider's value badge to type an exact number.
| Control | Writes | Range |
|---|---|---|
Color presets | --primary*, --accent* families | 12 swatches + “Surprise me” + “Add brand” |
Color pickers | --primary --accent --bg --card --text --text-soft --border --bg-soft --success --warning --destructive --info | Any hex color |
Font pairing | --font-heading --font-body | 12 pairings |
Monospace | --font-mono | 5 stacks |
Base font size | --font-size + root font-size | 13 – 20 px |
Heading scale | --heading-scale | ×1.00 – ×1.60 |
Heading weight | --font-weight | 400 – 800 |
Line height | --line-height | 1.20 – 2.00 |
Letter spacing | --letter-spacing | −1 – 3 px |
Global radius | --radius | 0 – 32 px |
Button / Card / Input radius | --btn-radius --card-radius --input-radius | 0 – 32 px |
Border width | --border-width | 0 – 4 px |
Density | --space-unit | ×0.70 – ×1.40 |
Content width | --container | 720 – 1280 px |
Shadow | --shadow | Flat / Subtle / Soft / Deep |
Backdrop blur | --blur | 0 – 20 px |
Animation speed | --anim-speed | Off – ×2 |
Icon style | body.icon-* class | Gradient / Glass / Neu / Flat |
Dark mode | surface vars (--bg --card --text …), data-theme | On / off — brand colors untouched |
Element inspector
For everything the tokens don't reach: third-party markup, one-off sections, legacy pages. Found under the panel's Inspect tab.
- Pick an element — the page enters crosshair mode; hover highlights, click selects. Esc cancels.
- Pick multiple — keep clicking to build a group selection (click again to remove one); every edit below applies to the whole group at once. Done exits picking, Clear empties the group.
- Edit background, text color, font size, weight, alignment, opacity, radius, padding, margin and shadow — applied as inline styles, instantly.
- Extra CSS — a per-element mini-editor for anything else (
text-transform,letter-spacing, …). Removed declarations are cleaned up automatically. - Hide / Show any element (useful for “what if this banner wasn't here?” reviews).
- Overrides persist — they're matched by a generated CSS selector and re-applied on every visit alongside the theme.
- Overrides export — Export theme.css and Copy CSS include every inspector edit as plain portable CSS rules, after the
:root {}block.
Custom CSS
The CSS tab is a freeform stylesheet with live apply. Anything valid works — gradients, keyframes, media queries — and it's bundled into Export.
/* example: gradient hero + smooth everything */
.hero {
background: linear-gradient(135deg, var(--primary), var(--accent-deep));
}
* { transition: background-color .25s, color .25s; }
Use Apply CSS to commit the textarea, Clear to remove it. The custom stylesheet lives in a dedicated <style id="dc-custom-css"> tag so it never fights your own files.
Export & import
Turn a live session into shippable artifacts — or pick up where a teammate left off.
- Export theme.css — downloads a
:root {}block with all current values, followed by inspector overrides as plain CSS rules, plus custom CSS. Paste it into your real stylesheet and the look holds without the script. - Copy CSS — same content to the clipboard for quick pastes into a PR or chat.
- Import — loads a previously exported theme JSON snapshot (state file) to restore an exact session, including inspector overrides and custom CSS.
- Reset — clears storage, removes overrides, restores defaults.
Storage
Choices save to a cookie (365-day expiry, SameSite=Lax, path=/) so a theme picked on one page follows visitors across every page that embeds the same script with the same storageKey — no account, no server.
Because cookies cap around 4 KB and inspector overrides + custom CSS can grow, the panel mirrors state to localStorage under the same key as a fallback. Only the visitor's selection is stored — your presets config itself is never written.
API reference
DesignCustomizer.init(config)
Creates and returns the panel instance. Idempotent — a second call returns the existing instance.
DesignCustomizer.getInstance()
Returns the active instance, or null before init().
instance.addPreset(preset)
Registers a preset at runtime (e.g. a client's brand) and redraws the swatch row immediately.
DesignCustomizer.getInstance().addPreset({
id: 'brand', name: 'Client Brand',
vars: {
'--primary': '#0B3D2E', '--primary-2': '#12563F', '--primary-deep': '#062318',
'--accent': '#D8B44A', '--accent-2': '#E8C468', '--accent-deep': '#B98A25'
}
});
instance.setVar(name, value) / instance.getVar(name)
Write or read any host CSS variable programmatically — handy for wiring your own UI (e.g. a brand switcher) into the same theming pipeline. Writes persist with the theme.
instance.buildCSS() / instance.exportCSS() / instance.exportJSON()
buildCSS() returns the :root {} string without downloading. exportCSS() downloads theme.css and also returns the string. exportJSON() returns a full state snapshot (including overrides + custom CSS) for re-import.
Variable list
Everything the panel manages and Export includes. Adopt what you need; the inspector and custom CSS cover the rest.
--primary --primary-2 --primary-deep --primary-fg
--accent --accent-2 --accent-deep
--bg --bg-soft --card --text --text-soft --border
--success --warning --destructive --info
--font-heading --font-body --font-mono
--radius --radius-sm --radius-md --radius-lg
--btn-radius --card-radius --input-radius
--space-unit --border-width --container
--line-height --letter-spacing --font-size
--heading-scale --font-weight
--shadow --blur --anim-speed
Troubleshooting
Moving a slider does nothing on my page
That control writes a variable your CSS never reads. Check the variable list: e.g. the Density slider only matters if your CSS uses var(--space-unit). Either adopt the token, or use the inspector / custom CSS for that element.
The panel looks broken / inherits my theme
It shouldn't — the panel UI uses isolated dc- styles, not your tokens. If you've globally styled button/input with !important, scope those rules to your own container instead.
Two sites on one domain share a theme
Give each project a unique storageKey — the cookie name is the namespace.
Dark mode looks off
Dark mode only swaps neutral surfaces and leaves brand colors alone by design. If a section sets its own hardcoded background, reach it with the inspector or add a [data-theme="dark"] override in custom CSS.