Monkeytype's themes,
on any website.

monkeystyle bundles all 187 palettes from monkeytype and translates them onto your existing CSS variables. One small rule, one function call. No framework, no build plugin, no LLM.

01How it works

  1. Every theme is ten colors.

    Monkeytype describes each theme with the same ten semantic colors. monkeystyle exposes them as --mt-* custom properties and derives eight on-palette accents for charts, tags and syntax.

  2. A bridge maps your tokens once.

    The CLI reads your stylesheet, works out what each variable is for (page background, muted text, border, accent…) and writes one rule that re-points them at the active theme. It's deterministic and explainable, and you can override anything. Try it below.

  3. Switch at runtime.

    applyTheme("dracula") sets the variables, color-scheme, and remembers the choice. A ~300-byte head script restores it before first paint, so there's no flash.

02Quick start

1. Install

npm i monkeystyle

2. Generate your bridge

Point the CLI at the stylesheet(s) that define your color variables.

npx monkeystyle bridge src/styles/global.css --out src/styles/monkeystyle.css

Import the output after your own tokens. It only applies while a theme is active ([data-mt]), so your site looks unchanged until someone picks one.

3. Prevent the flash

Inline this in <head>. It's the output of headScript() and has no dependencies.

4. Let people pick

import { applyTheme, clearTheme, listThemes } from "monkeystyle";

// wire into your command menu, a select, an easter egg…
for (const t of listThemes({ scheme: "dark" })) {
  menu.add(t.name, () => applyTheme(t.name));
}
menu.add("default", () => clearTheme());

03The bridge

Paste the CSS that defines your variables. Each variable is scored on its name, its lightness relative to your page background and text, its saturation, and how your rules use it. Everything runs in your browser.

generated bridge
variable yours → themed role confidence why

04API

monkeystyle runtime

applyTheme(name | theme, opts?)
Sets --mt-* on <html>, data-mt, color-scheme, saves to localStorage and fires monkeystyle:change. Options: root, attribute, storageKey (or false).
clearTheme(opts?)
Back to your site's native styles.
listThemes({ scheme?, query? }) · getTheme(name) · randomTheme()
Theme objects: the ten colors plus name, scheme and harvested extras.
getActiveTheme() · getSavedTheme()
Current / persisted theme name, or null.
themeVars(theme, opts?) · themeCSS(theme, selector?)
Resolve a theme (or your own palette) to variables or a CSS rule, for SSR or static builds. { monkeytypeNames: true } also emits --bg-color, --main-color, …
headScript(opts?)
The inline no-flash snippet as a string.

monkeystyle/bridge translation layer

generateBridge(css, { selector?, overrides?, ignore? })
Returns { css, mappings, skipped, hardcoded }. Each mapping has a role, confidence and a human-readable reason.
scanCSS(css) · inferRoles(props) · renderBridge(mappings)
The same pipeline, one step at a time.

Variables

Roles

05CLI & agents

Non-interactive and deterministic, so it gives the same output for humans, CI and coding agents. Every command that reports data accepts --json.

npx monkeystyle bridge <css files…> [--out file] [--json] [--selector S]
npx monkeystyle list [--scheme dark|light] [--json]
npx monkeystyle css serika_dark [--selector S] [--monkeytype-names]
npx monkeystyle head-script
npx monkeystyle roles

Overrides live in monkeystyle.config.json, which the CLI picks up automatically:

{
  "selector": "html:root[data-mt]",
  "overrides": { "--glyph-ink": "fgMuted", "--brand": { "expression": "var(--mt-caret)" } },
  "ignore": ["--shadow-color"]
}

06Themes

↑↓ preview↵ applyesc revert