Skip to content

Themes

Theme packages are plugins. Ship the same .hcp layout, require the ui permission, declare slots in contributes.themes, and set "categories": ["themes"] when the package should appear under File → Themes. Users pick an active theme from View → Theme or Settings → General → Appearance.

See Theme plugins for manifest fields and Marketplace → Theme listings for catalog publishing.

Custom appearance themes extend the built-in Light, Dark, System, and High contrast options in Settings → General. Plugin themes appear in the same dropdown once registered.

HarborClient styles the app with --mac-* CSS custom properties defined in src/renderer/src/styles.css. When a plugin theme is active, the host sets data-theme="plugin-<pluginId>-<themeId>" on <html> and applies your token overrides or injected stylesheet. Built-in light/dark/system behavior is unchanged when a builtin theme is selected.

Themes can be registered two ways:

  1. JavaScript — call registerTheme(hc, theme) or hc.themes.register(theme) from activate().
  2. JSON import — point the manifest contribution at a Theme Designer export file (see JSON theme import). No activate() call is required for those entries.

Requires the ui permission. For JavaScript registration, call registerTheme(hc, theme) or hc.themes.register(theme) from activate() — registration disposables are tracked automatically.

JSON theme import

Declare an import path on the contribution to ship a palette without JavaScript:

json
{
  "contributes": {
    "themes": [
      {
        "id": "solarized",
        "title": "Solarized Dark",
        "type": "dark",
        "import": "exported.json"
      }
    ]
  }
}

The file must be a harborclientExport: "theme" envelope — the same shape as File → Themes → Designer export:

json
{
  "harborclientVersion": 1,
  "harborclientExport": "theme",
  "title": "Solarized Dark",
  "type": "dark",
  "theme": {
    "surface": "#002b36",
    "accent": "#268bd2"
  },
  "stylesheet": "styles.css"
}
FieldDescription
harborclientVersionAlways 1
harborclientExportAlways "theme"
themeToken overrides without the --mac- prefix
title / typePresent in the export; manifest id / title / type remain authoritative
stylesheetOptional plugin-relative CSS filename, or inlined CSS after first read

On first read, if stylesheet points at an existing CSS file inside the plugin directory, HarborClient inlines the CSS text into the JSON on disk. Later reads treat the value as already-inlined CSS (idempotent). Theme-only packages can omit renderer and main entirely.

See the Solarized theme example and Theme plugins.

registerTheme(hc, theme)

Signature: (hc: PluginContext, theme: ThemeContribution) => Disposable

Convenience wrapper around hc.themes.register. Prefer this for single-theme plugins.

typescript
import { registerTheme } from '@harborclient/sdk';

registerTheme(hc, {
  id: 'solarized',
  title: 'Solarized Dark',
  type: 'dark',
  colors: { surface: '#002b36' }
});

Use defineTheme(theme) when you want to define the theme object in a separate module with full ThemeContribution typing.

Theme color tokens

Override any of these keys in colors. Each maps to --mac-<token> on the document root.

TokenUsed for
surfaceMain content background
headerTop header strip (sidebar search + request tab bar)
page-headerPage title header background (PageHeader)
page-header-textPage title header primary text
page-header-mutedPage title header description and decorative icons
sidebarLeft sidebar background
sidebar-toolbarSidebar/footer toolbar strip background
sidebar-railActivity rail background
sidebar-rail-activeActive/hover activity rail section fill
sidebar-rail-textActivity rail icons and labels
sidebar-rail-separatorActivity rail hairline between item groups
sidebar-sectionSidebar section headers
sidebar-section-textSidebar section header labels and chevrons
footerFooter status bar background
footer-textFooter primary text
footer-mutedFooter de-emphasized text
footer-icon-activeActive footer icon toggle color
toolbar-action-activePressed sidebar toolbar action icon color
breadcrumb-backgroundRequest editor breadcrumb bar track
breadcrumb-segmentBreadcrumb chevron segment fill
git-stagedGit-backed request names staged for commit
git-uncommittedGit-backed request names with tracked unstaged changes
git-unstagedGit-backed request names not yet added to the repository
controlPanels, inputs, footer bar
fieldInput field fill
separatorBorders and dividers
textPrimary text
text-secondarySecondary labels
mutedDe-emphasized text
accentLinks, focus rings, primary actions
selectionSelected row / highlight fill
tab-barRequest editor tab bar strip background
tab-activeActive request/editor tab fill
tab-inactiveInactive request/editor tab fill
tab-hoverInactive request/editor tab hover and focus-visible fill
tab-textActive (and hover/focus) request/editor tab label color
tab-text-inactiveInactive request/editor tab label color
tab-unsavedRequest/markdown tab title when the tab has unsaved changes
tab-underlineActive request tab underline
resize-separatorResizable panel separator track and edge border
resize-handleResizable panel grip (and high-contrast chrome accents)
variable-token{{variable}} syntax highlight in editors
danger, danger-light, warning, success, infoStatus colors
method-get, method-post, …HTTP method badge colors

See the Solarized theme example for a complete theme plugin.

hc.themes.getActive()

Available since v2.0.0

Signature:() => Promise<ActiveTheme>

Returns the currently active theme — either a built-in id or a plugin theme reference.

typescript
const active = await hc.themes.getActive();
if (active.source === 'plugin') {
  console.log(active.pluginId, active.themeId);
}

hc.themes.onDidChange(listener)

Available since v2.0.0

Signature:(listener: (theme: ActiveTheme) => void) => Disposable

Fires when the user changes the appearance theme in Settings or when the host resets theme after plugin deactivation.

typescript
hc.themes.onDidChange((theme) => {
  if (theme.source === 'plugin' && theme.themeId === 'solarized') {
    hc.ui.showToast('Solarized theme active');
  }
});

hc.themes.register(theme)

Available since v2.0.0

Signature:(theme: ThemeContribution) => Disposable

Manifest:contributes.themes

ParameterTypeDescription
idstringTheme id unique within your plugin
titlestringLabel in the appearance dropdown
type'light' | 'dark'Sets color-scheme and Electron native chrome base
colorsPartial<Record<ThemeColorToken, string>>Optional color token overrides
metricsPartial<Record<ThemeMetricToken, string>>Optional typography/geometry overrides (CSS strings such as 14px)
stylesheetstringOptional plugin-relative CSS file for complex themes

Provide colors, metrics, a stylesheet, or a combination. Use colors / metrics for token swaps; use stylesheet when you need selectors beyond :root (for example plugin-specific tweaks under [data-theme='plugin-…']).

When the user selects your theme, the persisted value is plugin:<pluginId>:<themeId>. If the plugin is disabled or uninstalled while its theme is active, HarborClient falls back to System.

typescript
hc.themes.register({
  id: 'solarized',
  title: 'Solarized Dark',
  type: 'dark',
  colors: {
    surface: '#002b36',
    sidebar: '#073642',
    control: '#073642',
    text: '#839496',
    'text-secondary': '#93a1a1',
    accent: '#268bd2',
    selection: 'rgba(38, 139, 210, 0.25)'
  },
  metrics: {
    'layout-font-size': '14px',
    'scrollbar-width': '10px'
  }
});