Skip to content

Appearance & Theming

The Hive desktop and Android apps support full theming of the UI chrome, embedded terminals, and both CodeMirror-based editors via CSS custom properties.

Where to configure

Open Settings → Appearance. The card contains:

  • Preset picker - pick a built-in theme. Each theme is a complete light or dark look; selecting one also sets the app's light/dark mode, so there is no separate light/dark toggle. Built-ins: Default Dark/Light, Solarized Dark/Light, Dracula Dark/Light, Catppuccin Mocha (dark), Catppuccin Latte (light), Nord (dark), Tokyo Night (dark), and Custom.
  • Mode selector - shown only for the Custom theme; chooses whether your custom CSS renders as a light or dark theme.
  • CSS Variables editor - a CodeMirror editor showing the active theme. Read-only while a built-in preset is selected; pick Edit as Custom to fork the active CSS into the Custom slot and start editing.
  • Reset - drops back to the Default theme (matching your OS light/dark preference) and clears any custom CSS.

Edits in Custom mode apply live to the running app and are persisted in the device's localStorage. Each theme is a single :root { ... } block; the Custom theme's Mode selector decides whether it renders light or dark.

Available variables

UI chrome:

VariablePurpose
--background / --foregroundApp body
--card / --card-foregroundCard surfaces
--popover / --popover-foregroundPopovers, dropdowns
--primary / --primary-foregroundPrimary action buttons
--secondary / --secondary-foregroundSecondary surfaces
--muted / --muted-foregroundMuted text and surfaces
--accent / --accent-foregroundHover / selected states
--destructiveDestructive actions
--border / --input / --ringBorders, inputs, focus rings
--sidebar*Sidebar surface, foreground, primary, accent, border, ring

Terminal:

VariablePurpose
--terminal-bg / --terminal-fgTerminal background / default text
--terminal-cursorCursor color
--terminal-selection-bgMouse selection background
--terminal-black / --terminal-bright-blackANSI 0 / 8
--terminal-white / --terminal-bright-whiteANSI 7 / 15
--terminal-font-familyTerminal font stack

ANSI colors 1-6 (red, green, yellow, blue, magenta, cyan) are not driven by CSS variables; they come from a fixed light/dark palette in useTerminalManager.ts.

Editor:

VariablePurpose
--editor-bg / --editor-foregroundEditor canvas and default text
--editor-gutter-bg / --editor-gutter-fgLine-number gutter colors
--editor-gutter-active-fgActive line number
--editor-borderGutter / panel borders
--editor-selection-bgText selection
--editor-active-line-bg / --editor-active-line-gutter-bgActive line highlight
--editor-cursorCaret color
--editor-search-match-*Search result highlight fill / border / selected match
--editor-bracket-match-*Matching bracket highlight fill / border

Example: minimal custom theme

A custom theme is a single :root block. Set the Mode selector to light or dark to tell the app which ANSI palette and dark: utilities to use.

css
:root {
  --background: #1c1b18;
  --foreground: #e6e1d6;
  --primary: #d98a52;
  --editor-bg: #24221f;
  --terminal-bg: #1c1b18;
  --terminal-fg: #e6e1d6;
}

Notes

  • Themes are device-local by default. Use Settings → Sync preferences with cluster → Save to cluster to push the current theme (and every other hive-* localStorage preference) to the connected daemon; any client that later connects to that cluster can pull the same blob with Pull from cluster. Sync is opt-in per action: editing locally never touches the cluster value.
  • The base styles in crates/hive-app/src/style.css define the defaults; your overrides cascade on top of those, so you only need to declare the variables you want to change.
  • Already-open terminals and editors refresh their colors live when the theme changes; no reopen / reattach needed.

Resizable panels

Every side panel can be resized by dragging the thin handle on its border, the same gesture used between terminal panes. On desktop (md+) only - on phone widths these panels are full-width swappable panes or sheets, so the handle is hidden. Each panel remembers its width across restarts in localStorage:

PanelPageRangeKey
Main navigation (left)all pages180-420 pxhive-nav-width
Right sidebar (Files/Git/Run/Commands)Workspace220-560 pxhive-right-sidebar-width
Notes file tree (left)Notes180-480 pxhive-notes-tree-width
Notes agent chat (right)Notes280-720 pxhive-notes-agent-width
Notes editor split dividerNotes (split mode)20-80%hive-notes-editor-split

The shared drag logic lives in src/composables/useResizablePanel.ts; the handle visual is src/components/common/ResizeHandle.vue. The collapsed main nav keeps its fixed w-12 rail and is not resizable until re-expanded.

Cluster-shared preferences

The daemon persists the synced blob in cluster_preferences.json next to its config.toml and broadcasts updates to its cluster peers via PeerMessage::SyncClusterPreferences. Conflict resolution is last-writer-wins on the whole blob, ordered by a server-side updated_at timestamp - so a slow peer broadcast can never clobber a fresher local write. Wire protocol: ClientMessage::GetClusterPreferences / SetClusterPreferencesServerMessage::ClusterPreferences { preferences }.

Hive - remote AI coding agents over WebSocket.