Theming
Themes: Catppuccin Frappé, Vim Adwaita Dark, Tokyo Night, built-in --light.
Themes: Lotus Light, the built-in --light, Vim Light, Catppuccin Latte.
Without any flags, dog defaults to a built-in dark theme. You can use --light to use the built-in light theme, use existing Zed themes, or create your own by passing --theme.
INFO
dog has two free built-ins: they're hardcoded into the binary, so dog doesn't read, install, or place any JSON files for them — nothing to set up, no file I/O at startup.
For the light built-in you can use the flag:
dog Sources/dog/Dog.swift --light # UtilityBrightPassing --theme with a theme name overrides the defaults. Keep reading for more info.
dog Sources/dog/Dog.swift --theme "Catppuccin Mocha"You can find all theme names quickly anytime:
dog --list-themesBoth built-ins (UtilityDark, UtilityBright) plus any themes in your theme directory show in the list.
Set a default theme
Rather than passing --theme every run, save a theme as your default:
dog --set-default-theme 'Vim Dark'Every run from then on uses it — no flags needed. An explicit --theme still wins for that run, and --dark/--light get you a built-in back for one run.
--set-default-theme takes anything --theme takes: a theme name, a file in your theme directory (Catppuccin.json), or a path to one.
INFO
dog resolves the theme once and saves the finished result to ~/.config/dog/default-theme. Startup reads it in microseconds — no JSON parsing — so a custom default is just as fast as the built-ins. The saved copy is the finished theme: if you edit the theme's JSON later, run --set-default-theme again to pick up the changes.
Reset to the built-in default anytime:
dog --set-default-theme UtilityDarkFinding, editing, or creating themes
Zed has a large theme ecosystem. Three sources worth knowing:
- zed-themes.com — third-party community site with direct JSON downloads, previews, and in-browser editing of existing themes or starting fresh from a template.
- zed.dev/extensions?filter=themes — the official Zed extensions registry. Theme extensions here.
- zed.dev/theme-builder — Zed's official in-browser theme builder. Good for authoring from scratch.
Any theme downloaded from those sources comes as a JSON file — drop it in your theme directory and dog --list-themes picks it up.
By default dog looks for themes in ~/.config/dog/themes — see theme directory for the full resolution order.
mkdir -p ~/.config/dog/themes
mv ~/Downloads/my-theme.json ~/.config/dog/themes/Using existing Zed themes
If you already have Zed installed, your themes usually live in ~/.config/zed/themes. Point dog at that directory with --theme-dir:
dog --theme-dir ~/.config/zed/themes --theme "One Dark Pro" Sources/dog/Dog.swiftTo make it default without typing the flag every time, alias or shell-function it until the config file lands (planned):
alias dog='dog --theme-dir ~/.config/zed/themes'Why Zed themes
Zed's theme format is plain JSON with a large ecosystem behind it, and it describes the whole editor surface — not just token colors. dog reads it directly, so any Zed theme works unchanged. From each theme, dog uses:
- Colors for every syntax token it maps (see Supported tokens below).
- Editor surface colors:
editor.background,editor.foreground,editor.line_number,editor.gutter.background. This is what allowsdogto paint the background, line numbers, and gutter the way your editor would. - Font weight and style per token —
font_weight >= 700renders bold,font_style: "italic"renders italic.
dog does not read fonts from the theme. Terminal fonts come from your terminal emulator; themes only set colors and bold/italic attributes.
Writing a custom theme
You don't have to start from scratch: copy any Zed theme's JSON and edit it to your liking, or use zed-themes.com's in-browser editor on an existing or new theme. You also don't need a full Zed theme file — dog only uses the mappings defined in Supported tokens below, and anything you leave out falls back to its parent scope.
Minimal shape dog accepts:
{
"name": "My Theme Bundle",
"themes": [
{
"name": "My Theme Dark",
"appearance": "dark",
"style": {
"editor.background": "#1e1e1e",
"editor.foreground": "#d4d4d4",
"editor.line_number": "#606060",
"editor.gutter.background": "#1a1a1a",
"syntax": {
"keyword": { "color": "#c586c0", "font_weight": 700 },
"string": { "color": "#ce9178" },
"comment": { "color": "#6a9955", "font_style": "italic" },
"function": { "color": "#dcdcaa" },
"type": { "color": "#4ec9b0" },
"number": { "color": "#b5cea8" }
}
}
}
]
}Save as my-theme.json in your theme directory, then:
dog --theme "My Theme Dark" Sources/dog/Dog.swiftOne JSON file can hold multiple variants in themes[] — address any variant by its name, or directly as file:variant.
🐩 Dog Fact
The Catahoula Leopard Dog can display up to five colors layered over a solid background with bold multicolored spots — guaranteed no two are identical.
Colors: hex and alpha
dog accepts two hex color formats:
#rrggbb— solid 24-bit color (e.g.#ce9178)#rrggbbaa— with an alpha channel (e.g.#ce917880)
Terminals have no transparency. When dog loads a theme, every #rrggbbaa color is composited against editor.background once at load time and stored as a solid RGB. The render loop only ever emits solid colors.
If a theme uses alpha colors but doesn't define editor.background, dog drops the alpha channel and renders the raw RGB as solid. Prefer explicit editor.background in themes that use alpha.
Resolving theme names
dog resolves --theme "<name>" in two phases:
- Progressive prefix match. For a name like
macOS Classic Dark,dogstatsmacOS Classic Dark.json, thenmacOS Classic.json, thenmacOS.json— stopping on the first file that exists. Inside that file, it looks for a variant matching the full name. - Full directory scan. If phase 1 misses,
dogscans every.jsonin the theme directory and returns the first variant whosenamematches — files sharing the name's first word are read first.
If nothing matches, dog prints an error with a Levenshtein-distance suggestion.
Skip the search
Name a file instead of a theme and dog reads it directly — no directory search:
dog --theme Nord.json Sources/dog/Dog.swift # file in the theme directory
dog --theme ~/Downloads/Nord.json Sources/dog/Dog.swift # any path
dog --theme 'Nord:Nord Dark' Sources/dog/Dog.swift # file:variantA file on its own means its first variant. The file:variant form splits at the last colon — the left side is a file in the theme directory (.json optional) or a path, the right side picks a variant inside it. It's the only way to address a non-first variant without a search, which makes it a good fit for previewer commands.
To set a theme as the default and not have to pass --theme at all, see Set a default theme.
Supported tokens
dog reads any of these capture names under style.syntax. Unknown names fall back hierarchically: string.special.key → string.special → string.
Zed ↔ dog name mapping
Zed uses slightly different names for a few scopes. Both the Zed name and the dog/nvim-treesitter name work — dog normalizes them:
| Zed name | Resolves to |
|---|---|
variable.special | variable.builtin |
link_uri | text.uri |
link_text | markup.link.label |
title | text.title |
punctuation.list_marker | markup.list |
tag.doctype | tag.builtin |
comment.doc | comment.documentation |
enum | type |
variant | constant |
preproc | keyword.directive |
macro | function.macro |
regex | string.regex |
These Zed scopes are editor UI — dog silently ignores them: hint, predictive, primary.
🐾 Dog Fact
Dogs love dark mode. They have superior night vision and motion detection, which was more important evolutionarily than color vision for their survival as hunters.
Full token list
73 capture names covering the 17 bundled languages. Group by category.
Keywords and control flow
keyword, keyword.conditional, keyword.conditional.ternary, keyword.coroutine, keyword.directive, keyword.directive.define, keyword.exception, keyword.function, keyword.import, keyword.modifier, keyword.operator, keyword.repeat, keyword.return, keyword.type, conditional, repeat, include
Functions and methods
function, function.builtin, function.call, function.macro, function.method, function.method.builtin, function.method.call, function.special, method, constructor
Types
type, type.builtin, type.definition
Variables and parameters
variable, variable.builtin, variable.member, variable.parameter, parameter, field, property, label
Modules
module, module.builtin
Strings and characters
string, string.documentation, string.escape, string.regex, string.regexp, string.special, string.special.key, string.special.path, string.special.regex, string.special.symbol, string.special.url, character, character.special
Numbers and literals
number, number.float, float, boolean, constant, constant.builtin, constant.macro
Punctuation and operators
punctuation.bracket, punctuation.delimiter, punctuation.special, delimiter, operator
Markup
markup.heading, markup.heading.1, markup.heading.2, markup.heading.3, markup.heading.4, markup.heading.5, markup.link.label, markup.list, markup.quote, markup.raw.block
Tags and attributes
tag, tag.attribute, tag.builtin, tag.delimiter, tag.error, attribute, attribute.builtin
Text and other
text.literal, text.title, text.reference, text.uri, spell, nospell, none, conceal, comment, comment.documentation, escape, embedded, error
Hierarchical fallback
If a theme doesn't define a specific capture, dog walks up the dot hierarchy and uses the nearest ancestor that is defined. So defining just keyword covers every keyword.* that isn't defined explicitly.
Examples:
keyword.returnundefined → falls back tokeywordstring.special.urlundefined →string.special→stringmarkup.heading.3undefined →markup.heading→ base text
A theme that defines only a dozen top-level scopes (keyword, string, function, type, etc.) still colors everything — the rest inherits.