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); }
New to tokens? Start with color + radius + fonts (the highest-leverage five minutes), then adopt spacing (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).

OptionTypeDefaultWhat it does
storageKeystring"design-customizer"Cookie + localStorage key. Use a unique key per project so themes don't collide when sites share a domain.
presetsarray12 built-insReplaces the palette. Each entry: { id, name, vars } where vars maps variable names to values.
fontsarray12 pairingsReplaces the font list. Each entry: { id, label, heading, body }.
monosarray5 stacksReplaces the monospace list. Each entry: { id, label, stack }.
iconStylesarray4 stylesReplaces 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.

ControlWritesRange
Color presets--primary*, --accent* families12 swatches + “Surprise me” + “Add brand”
Color pickers--primary --accent --bg --card --text --text-soft --border --bg-soft --success --warning --destructive --infoAny hex color
Font pairing--font-heading --font-body12 pairings
Monospace--font-mono5 stacks
Base font size--font-size + root font-size13 – 20 px
Heading scale--heading-scale×1.00 – ×1.60
Heading weight--font-weight400 – 800
Line height--line-height1.20 – 2.00
Letter spacing--letter-spacing−1 – 3 px
Global radius--radius0 – 32 px
Button / Card / Input radius--btn-radius --card-radius --input-radius0 – 32 px
Border width--border-width0 – 4 px
Density--space-unit×0.70 – ×1.40
Content width--container720 – 1280 px
Shadow--shadowFlat / Subtle / Soft / Deep
Backdrop blur--blur0 – 20 px
Animation speed--anim-speedOff – ×2
Icon stylebody.icon-* classGradient / Glass / Neu / Flat
Dark modesurface vars (--bg --card --text …), data-themeOn / 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.
Inspector overrides are page-structure dependent: if you radically restructure the HTML, a saved selector may no longer match and that override is skipped silently. Tokens + custom CSS are the durable path for structural redesigns.

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.