Skip to content

Theming jjui

Themes allow for detailed control over the application’s appearance.

The following configuration loads a base theme from ~/.config/jjui/themes/my-theme.toml.

~/.config/jjui/config.toml
[ui]
theme = "my-theme"
~/.config/jjui/themes/my-theme.toml
[colors]
":selected" = { bg = "red", bold = true }

A custom theme is self-contained: unspecified styles do not inherit from jjui’s built-in theme. Colors from jj and inline [ui.colors] overrides are still applied on top.

Override parts of the active theme in the ui.colors section. This example loads my-theme and sets the global selected background to red:

[ui]
theme = "my-theme"
[ui.colors]
":selected" = { bg = "red" }

[!NOTE] Theme support is actively being developed. The information on this page is subject to change as the application evolves.

Choose different theme files for light and dark terminal appearances with:

[ui.theme]
light = "my-light-theme"
dark = "my-dark-theme"

A single name selects the same theme file for both appearances:

[ui]
theme = "my-theme"

jjui detects changes to the terminal appearance and reapplies the matching theme.

On Linux, macOS, and other Unix systems, send SIGUSR1 to a running jjui to reload the active theme file without restarting:

Terminal window
kill -USR1 <jjui-pid>
# or reload every running instance
pkill -USR1 -x jjui

This is useful when another tool, such as a wallpaper-based color generator, rewrites the theme file. Only theme files are reloaded; changes to config.toml (including [ui.colors] overrides) and Lua scripts still need a restart. jjui does not watch the file for changes, so send the signal after the file is written. If the theme file can’t be loaded, jjui keeps the current colors.

Theme reloading by signal is not available on Windows.

A structured theme can contain shared colors plus appearance-specific overrides:

~/.config/jjui/themes/my-theme.toml
[colors]
title = "magenta"
":selected" = { bold = true }
[light]
background_blend = 0.2
[light.colors]
":selected" = { bg = "white" }
[dark]
background_blend = 0.4
[dark.colors]
":selected" = { bg = "bright black" }

Shared [colors] are loaded first, then [light.colors] or [dark.colors] overrides are applied for the current terminal appearance. Existing flat theme files remain supported.

Selected styles can use a blended background to maintain contrast across different terminal palettes and component surfaces. background_blend accepts a value from 0.0 to 1.0; 0.0 disables blending, while higher values move the selected background closer to the surrounding surface.

Set the value independently in a theme’s [light] and [dark] sections, as shown above. To override the active theme from config.toml, set one value for both appearances:

[ui]
background_blend = 0.4

Or set an override per appearance:

[ui]
background_blend = { light = 0.2, dark = 0.4 }

When blending is enabled, jjui queries the terminal background and ANSI palette. It reapplies the theme when the terminal background changes.

Selected rows and menu entries also receive an automatic foreground adjustment when their configured colors would be difficult to read against the selected background. Annotation highlights use the active theme colors and receive the same contrast treatment.

Theme colors are keyed by a selector that targets a UI element. A color can be a foreground color by itself or a style table.

[colors]
":selected" = { fg = "#FF8C00", bg = "#2B2B2B", bold = true }
border = "bright black"

The style table can contain any of the following properties:

PropertyTypeDescription
fgColorSets the foreground (text) color.
bgColorSets the background color.
boldboolIf true, makes the text bold.
underlineboolIf true, adds an underline to the text.
strikethroughboolIf true, adds a strikethrough line.
italicboolIf true, makes the text italic.
reverseboolIf true, swaps the foreground and background.

Colors can be specified in one of three formats:

  • TrueColor (Hex): A string representing a hex color code (e.g., "#FF4500").
  • Base16 Names: A string for standard terminal colors (e.g., red, bright green, white).
  • ANSI256 Codes: An integer from 0 to 255.

A selector combines a scope, component, role, and optional variant. For example, revset completion text:selected targets the text role in a selected completion row within the revset scope.

Selected variants use a :selected suffix:

"revset completion text" = { fg = "green" }
"revset completion text:selected" = { fg = "bright green" }
":selected" = { bg = "bright black" }

More-specific styles take precedence, while broader selectors fill properties that were not set. This lets :selected provide a global selected background while a role-specific selected style overrides only its foreground.

The legacy form with selected as a selector word, such as "revset completion selected text", remains supported. Prefer the suffix form; if both spellings occur in the same theme layer, the suffix form wins.

These are base elements that apply throughout the application unless overridden by a more specific selector.

  • text: The default style for all text.
  • dimmed: Less important text, such as hints, descriptions, and inactive elements.
  • :selected: The variant for a currently highlighted or active item in a list or menu.
  • border: The style for borders around windows, panes, and pop-ups.
  • title: The style for titles in windows, panes, and menus.
  • shortcut: The style for keyboard shortcuts (e.g., [Enter], [q]).
  • matched: The style for the part of the text that matches user input, typically in a completion or filter.

These styles appear during interactive operations like rebase, squash or duplicate.

  • source_marker: The marker for the revision being moved or acted upon.
  • target_marker: The marker for the destination of the operation.

The input bar at the top of the screen.

  • revset title: The “Revset:” label.
  • revset text: The user input area. It’s recommended to make this bold.
  • Completions Dropdown:
    • revset completion:selected: The highlighted item in the completions list.
    • revset completion matched: The part of a completion that matches the input.
    • revset completion dimmed: The auto-suggested part of a completion.
    • Append :selected to a role, such as revset completion matched:selected, to style that role in the highlighted row.

The central list of commits or operations.

  • revisions: The base style for the entire list area.
  • revisions:selected: The currently highlighted line.
  • revisions dimmed: Hint text shown during interactive operations.

The bar at the bottom showing the current mode and available actions.

  • status: The base style for the entire bar. A distinct bg is recommended.
  • status title: The current mode indicator (e.g., NORMAL). A contrasting bg helps it stand out.
  • The actions also uses shortcut and dimmed styles.

The pop-up list showing the evolution history for a revision.

  • evolog: Base style for the view.
  • evolog:selected: The highlighted item. Can be styled differently from revisions:selected to show which pane is active.

The Git and Bookmarks menus use their own scopes:

  • git / bookmarks: Base styles.
  • git:selected / bookmarks:selected: Selected items.
  • git matched / bookmarks matched: Filter matches.
  • Items also use scoped title, shortcut, dimmed, and border roles.

The pop-up window displaying key-bindings and help text.

  • help: The base style for the window. To avoid a “patchy” look, define a bg color here. This color will serve as the background for the entire content area.
  • The window uses border and title styles.

The pane on the right that shows diffs or other details.

  • preview: The base style for the pane.
  • Uses preview border style for its frame.

The small inline dialog for confirmations (e.g., “Abandon all?”).

  • confirmation: Base style for the dialog. Should have a border.
  • The message uses the global text style.
  • Options use confirmation:selected for the highlighted choice and confirmation dimmed for other choices.
fire theme screenshot
[colors]
"text" = { fg = "#F0E6D2", bg = "#1C1C1C" }
"dimmed" = { fg = "#888888" }
":selected" = { bg = "#4B2401", fg = "#FFD700" }
"border" = { fg = "#3A3A3A" }
"title" = { fg = "#FF8C00", bold = true }
"shortcut" = { fg = "#FFA500" }
"matched" = { fg = "#FFD700", underline = true }
"source_marker" = { bg = "#6B2A00", fg = "#FFFFFF" }
"target_marker" = { bg = "#800000", fg = "#FFFFFF" }
"revisions rebase source_marker" = { bold = true }
"revisions rebase target_marker" = { bold = true }
"status" = { bg = "#1A1A1A" }
"status title" = { fg = "#000000", bg = "#FF4500", bold = true }
"status shortcut" = { fg = "#FFA500" }
"status dimmed" = { fg = "#888888" }
"revset text" = { bold = true }
"revset completion:selected" = { bg = "#4B2401", fg = "#FFD700" }
"revset completion matched" = { bold = true }
"revset completion dimmed" = { fg = "#505050" }
"revisions:selected" = { bold = true }
"oplog:selected" = { bold = true }
"evolog:selected" = { bg = "#403010", fg = "#FFD700", bold = true }
"help" = { bg = "#2B2B2B" }
"help title" = { fg = "#FF8C00", bold = true, underline = true }
"help border" = { fg = "#3A3A3A" }
"git" = { bg = "#2B2B2B" }
"bookmarks" = { bg = "#2B2B2B" }
"confirmation" = { bg = "#2B2B2B" }
"confirmation text" = { fg = "#F0E6D2" }
"confirmation:selected" = { bg = "#4B2401", fg = "#FFD700" }
"confirmation dimmed" = { fg = "#888888" }
"confirmation border" = { fg = "#FF4500" }
"undo" = { bg = "#2B2B2B" }
"undo confirmation dimmed" = { fg = "#888888" }
"undo confirmation:selected" = { bg = "#4B2401", fg = "#FFD700" }
"preview" = { fg = "#F0E6D2" }
Contribute Community