Themes
Plugins can register full color palettes via buoy.addTheme(...). Once registered, a theme appears in Settings → Appearance alongside the built-ins, persists across restarts like any other theme choice, and participates in the .app.danger impersonation overlay used by /as.
A theme is a palette of CSS color values plus a little metadata. Buoy translates it into a [data-theme="..."] { --bg: ...; ... } block injected into a single <style> tag, and <html data-theme="..."> swaps onto your palette when the user picks it.
addTheme(spec)
buoy.addTheme({
id: "midnight", // local id; auto-namespaced to plugin:<plugin-id>:midnight
name: "Midnight", // shown in the Appearance picker
description: "Deep ink palette",// optional one-line description shown under the name
colorScheme: "dark", // "dark" | "light" — drives the browser's color-scheme
colors: { /* 24 required keys, see below */ },
danger: { /* optional 4 keys for /as overlay */ },
swatch: { /* optional 4-color preview; derived from colors when omitted */ },
});
IDs
The id you supply is your local id only. Lowercase letters / digits / dashes, 1–32 characters. The registry namespaces it as plugin:<plugin-id>:<id> before persisting and applying — so you can never collide with a built-in (nord, dark, etc.) or with another plugin. The full namespaced id is what shows up in <html data-theme="..."> and what prefs.theme stores.
The <plugin-id> segment is the basename Buoy assigns when it scans your file (my-themes.tsx → my-themes). Aim for short, kebab-case ids — the full attribute string ends up roughly plugin:my-themes:midnight.
Colors
All 24 keys are required. Each must be a non-empty CSS color string (#rrggbb, rgb(...), rgba(...), etc.). The SDK rejects partial palettes at registration time — a missing key would otherwise leave the app falling through to the dark default mid-render, which looks broken.
| Key | CSS variable | Used for |
|---|---|---|
bg | --bg | App background. |
bgElevated | --bg-elevated | Cards, panels, modals. |
bgInput | --bg-input | Text inputs, the command bar. |
bgHover | --bg-hover | Hover states on rows, buttons, menu items. |
border | --border | Default borders. |
borderStrong | --border-strong | Emphasized borders (selected rows, focus rings). |
text | --text | Primary text color. |
textMuted | --text-muted | Secondary text, labels. |
textSubtle | --text-subtle | De-emphasized text (timestamps, captions). |
accent | --accent | Primary action color (selected tab, link). |
accentBg | --accent-bg | Translucent accent fill (selected row tint, pill bg). |
error | --error | Error text and inline error pills. |
green | --green | Healthy / Ready status badges. |
greenBg | --green-bg | Translucent green fill. |
yellow | --yellow | Pending / Warning status. |
yellowBg | --yellow-bg | Translucent yellow fill. |
red | --red | Failed / Error status, danger buttons. |
redBg | --red-bg | Translucent red fill. |
orange | --orange | CrashLoop / Terminating, mid-severity status. |
orangeBg | --orange-bg | Translucent orange fill. |
gray | --gray | Neutral / Unknown status. |
grayBg | --gray-bg | Translucent gray fill. |
blue | --blue | Informational badges (Completed, Bound). |
blueBg | --blue-bg | Translucent blue fill. |
Look at any built-in theme block in src/App.css ([data-theme="nord"], [data-theme="gruvbox"], etc.) for concrete reference values.
Danger overrides (optional)
When /as impersonation is active Buoy adds a .app.danger class to the root, which re-tints four variables to a “this could destroy things” red. The base override built into the app is calibrated for dark backgrounds and reads poorly on light palettes — light-theme authors should supply their own.
Allowed keys (all optional, all CSS color strings):
| Key | CSS variable |
|---|---|
border | --border |
borderStrong | --border-strong |
bgElevated | --bg-elevated |
bgHover | --bg-hover |
If you omit danger, the built-in reddish-dark .app.danger rule applies. The top-border red glow Buoy renders during impersonation is always on — that lives outside the theme.
Swatch (optional)
The Settings picker shows a four-color preview swatch per theme. If you omit swatch, Buoy derives one from colors.bg / bgElevated / text / accent, which is usually fine. Supply an explicit swatch: { bg, surface, text, accent } if you want to show off colors that aren’t in the structural-bg slot — for example, a vivid accent that hides behind the muted --accent you actually use.
Full example
/// <reference path="./buoy-sdk.d.ts" />
export default (buoy: Buoy) => {
buoy.meta({ name: "Midnight Theme Pack", version: "0.1.0" });
buoy.addTheme({
id: "midnight",
name: "Midnight",
description: "Deep navy palette, neon accents.",
colorScheme: "dark",
colors: {
bg: "#050810",
bgElevated: "#0d1220",
bgInput: "#0a0f1c",
bgHover: "#161e30",
border: "#1a2238",
borderStrong: "#2a3550",
text: "#e5ecff",
textMuted: "#8a96b8",
textSubtle: "#525d7a",
accent: "#6ea8ff",
accentBg: "rgba(110, 168, 255, 0.18)",
error: "#ff7a90",
green: "#5bd6a0",
greenBg: "rgba(91, 214, 160, 0.14)",
yellow: "#ffd166",
yellowBg: "rgba(255, 209, 102, 0.14)",
red: "#ff7a90",
redBg: "rgba(255, 122, 144, 0.14)",
orange: "#ff9f6a",
orangeBg: "rgba(255, 159, 106, 0.14)",
gray: "#8a96b8",
grayBg: "rgba(138, 150, 184, 0.12)",
blue: "#6ea8ff",
blueBg: "rgba(110, 168, 255, 0.14)",
},
danger: {
border: "#5a2030",
borderStrong: "#7a2c40",
bgElevated: "#1f0d14",
bgHover: "#2d1320",
},
});
};
Test it locally
- Save the plugin under your plugins dir (see Installation paths).
- Open Buoy or use Settings → Plugins → Reload to pick up the change.
- Open Settings → Appearance. Your theme appears with a small
From <plugin name>tag under the description. - Click it; the whole app re-tints. Reload Buoy to confirm the choice persists.
- Run
/as some-userto flip on the impersonation overlay and verify yourdangercolors look right (or that the fallback reads acceptably if you skippeddanger). - Delete the plugin file; the picker shows a “Selected theme not loaded” hint card with a Revert to Dark button.
Compile errors
addTheme validates at registration time. The most common errors show up in Settings → Plugins under the offending plugin:
colors.<key> is required (non-empty string)— you missed a key. Compare against the table above.colorScheme must be "dark" or "light"— typo or missing.unknown danger.<key> key — allowed: border, borderStrong, bgElevated, bgHover— only those four keys are accepted.duplicate id — each theme id must be unique within a plugin— you calledaddThemetwice with the same localid.