Theme and accessibility
Appearance, interface style, status colors, VoiceOver, keyboard paths, text size and the keyboard shortcuts
Appearance and interface style
Two settings decide the look (D44). Both are in View ▸ Appearance, View ▸ Interface Style, Settings ▸ Appearance and the palette.
- Appearance: Auto (follows macOS), Light or Dark. Dark is the default.
- Interface style: Glass or Classic. Glass is the default.
- Glass: the window background is a behind-window blur under a tint, so the desktop shows through softly. The selected tab and space row are Liquid Glass capsules.
- A bright window behind a dark Glass window shows only as a soft, even light, as in Rex: no text or edges of it come through. The blur and a dark tint hide them. Light Glass's white tint hides a dark window behind it. Text stays at 4.5:1 or more in both cases.
- An inactive window looks the same as the active one: the blur stays active, and the tints are plain colors.
- Liquid Glass goes only on small or short-lived parts: the selected tab and space row, buttons, keycaps, the chat's reply box, and the panels below. It costs the GPU more than the classic blur, more the larger it is, and macOS turns it grey in an inactive window. Over the whole window it costs WindowServer about a quarter more GPU time while a terminal scrolls.
- Classic: an opaque window with no blur. The selected tab and row are plain raised pills.
- In both styles the sidebar sits flat on the window background, and pane cards are translucent fills with thin edges, never glass. Popovers, the palette, the hover card, the find bar and notices use the system glass over the system's regular material, so a terminal behind them is only a soft blur (on a small panel Liquid Glass alone lets its text read through). Tooltips are the system's.
- Pane cards have a 10 pt radius. The 8 pt gap between cards is the one fixed layout size.
One token table in Style.swift holds every color the views draw, per style
and appearance, with values measured from Rex and tuned by eye. In short:
| Token | Glass | Classic |
|---|---|---|
| Window background | behind-window blur (hudWindow) + a tint. Dark: black 0.6 blended overlay darkens the blur, then #1d1d1d at 0.845 on top. Light: white at 0.7 | opaque #1e1e1e dark, #f6f6f6 light |
| Card fill | white 0.03 dark, 0.6 light, a step brighter than the window; the keyboard card only a little brighter (white 0.02 more in dark, 0.25 in light, so it ends at 0.7) | white 0.018 dark (#222 on #1e1e1e), 0.6 light; a step apart on the keyboard card (black 0.1 more in dark; white 0.5 more in light, so it ends at 0.8) |
| Card edges | Rex's rim, plusLighter: a 0.7 pt inner rim (white 0.06 dark, 0.04 light) and over it along the top a 1 pt line (white 0.09 dark, 0.3 light) that fades out round the corners over 14 pt, with a glow under it (8 pt, about half); then a 1 pt outer stroke (black 0.5 dark, 0.1 light, the same on every card); in light a shadow (black 0.1, blur 5, 2 pt down) | a hairline outer stroke, so each card stands apart from the window: light black 0.14 at 0.7 pt (black 0.18 at 0.8 pt on the keyboard card); dark white 0.1 at 0.7 pt (white 0.16 at 0.8 pt on the keyboard card); no shadow and no rim |
| Selected tab and row | Liquid Glass capsule | a flat pill in dark (white 0.07, with a 1 pt edge at white 0.12); white with a fine stroke and a shadow in light |
| Tab track | the primary color at 0.05 (0.1 with Increase Contrast) | the same |
| Title row and sidebar text and glyphs | white 0.65 dark, black 0.65 light (0.85 with Increase Contrast); the selected tab's and row's title white 0.9 / black 0.9 (1 with Increase Contrast) | the same |
| Tab dividers | white 0.17 dark, black 0.14 light, as in Rex (0.3 and 0.28 with Increase Contrast) | the same |
| Card header buttons and the divider grip | white 0.42 dark, black 0.45 light (0.6 and 0.65 with Increase Contrast): at least 3:1 on the card and the window | the same |
The hover and pressed fills (rows, tabs, buttons) are the same in both styles: the primary color at 0.05 and 0.08, which follows the appearance by itself, so they are not tokens. The hover fill fades in and out over 0.14 s; a menu's highlight (the palette, the space menu, Open Session) moves at once.
Terminals keep a transparent background, so the card shows through. Their
text uses libghostty's defaults: Alabaster in light, Afterglow in dark. They
follow the appearance. A change updates every terminal without opening its
stream again. Selected text takes the theme's text color over a blue, as
Ghostty's own Alabaster and Afterglow themes draw it: #000000 on #BFDBFE
in light (Ghostty's Alabaster), #D0D0D0 on #1B3A58 in dark (Ghostty's
Afterglow selects on #303030, which hardly shows on a card). So a program's
colors never hide selected text. There are no other themes.
The text draws as Ghostty draws it by default: no font-thicken,
alpha-blending = native, 13 pt, in the JetBrains Mono that libghostty
carries, Ghostty's own default font (Settings).
Status
The status words and colors are the same everywhere (tabs, space rows, the agent rows, the hover card, notifications):
| Shown as | Word | Dark | Light | herdr status |
|---|---|---|---|---|
a dashed circle (lucide's circle-dashed) and "Working" | working | #60a5fa | #3b82f6 | working |
a circle with an exclamation mark (circle-alert) and "Needs you" | needs you | #fb923c | #ea580c | blocked |
a circle with a check (circle-check) and "Done" | finished | #34d399 | #10b981 | finished and not yet seen by this device |
| ↓behind on a space row, the error tone of a notice; an alert circle and "Error" on a chat card | error | #f85149 | #cf222e | — |
- Icons. The icons and colors are t3code's: lucide's round shapes, drawn by the app with strokes of 2 units in 24 and round ends, in the status color. No state icon moves: no spinner and no dots for a state. They are 12 pt everywhere: a space row, a tab, an agent row's box, the hover card, a state label, a menu row and the chat's loader.
- Priority. A tab or a space with several agents shows one icon, its strongest: Needs you, then Done (not seen), then Working. It shows nothing when all are idle.
- State label. The agent card's header and the overview's tiles show a state as its icon and word, 11 pt medium in the status color, with no fill. A chat's Error is the alert circle in the error color.
- State box. An agent row (Agent rows) shows its icon in a box in the status color, and for Working its time.
- An idle agent shows no icon, no label, no box and no time.
- A row that calls for the user (needs you, or done and not seen) has its name in medium weight; other rows are regular.
Accessibility
- Status is never color alone. Every icon carries its word, and VoiceOver reads, for example, "codex, needs you" on a tab. A space row adds the word to its value.
- The tab row makes only the capsules in and near its view, so VoiceOver reads those. ⌘1–9, ⇧⌘[ and ⇧⌘], and the palette reach the others; a space row's value has the strongest state of all its tabs.
- A terminal card gives VoiceOver only its grid size, not its text.
- ⌥⌘ and an arrow moves the keyboard between cards. Keyboard focus in the chrome follows Full Keyboard Access. A terminal keeps Tab for itself.
- VoiceOver's actions on a card are its menu's items, with Move Pane to Tab's New Tab (when the pane shares its tab) and New Space only. With any accessibility client running (BetterTouchTool, Raycast, a test), SwiftUI builds a card's actions with the card: with one per tab, a switch to a session of 300 tabs takes three times as long. VO-Shift-M on a card opens its menu, with every tab; the palette lists them too.
- Every card action can be reached from the keyboard, through the menu bar and the palette (D48). Every swipe has a keyboard path, a menu path and a button. Hover controls are also in context menus and the menu bar.
- Each divider is an adjustable element: "Divider between ‹a› and ‹b›", with the ratio as a percent (Dividers).
- The terminal font size is the app's text size (Settings).
- Reduce Motion makes the sidebar's session pages jump, and turns off the icon bounce, the layout motion, the reorder slides, the tab bar's slide to a new tab, the spring-load pulse, the sidebar's slide, the plugins' sidebar slide, a plugin card's toast slide, the panels' scale-in, the chat's first-message motion and its fades (the rows as they show, a message the agent takes).
- Reduce Transparency is handled by the system: its blurs and glass turn opaque. Classic is an opaque window for users who want no blur at all.
- Increase Contrast makes the app's own dim colors stronger (the table above: the title row's text, tab dividers, the tab track, card header buttons and the grip). The system's colors follow it by themselves.
- The card header buttons and the divider grip have a contrast of at least 3:1 against what is behind them, in both styles and both appearances (measured 3.3:1 to 4.1:1 on CI's desktop; in Glass it depends on the desktop).
Keyboard shortcuts
Every key lives in one registry: each action has an id (tab.new), a group,
its default keys and the key the user set in
Settings ▸ Keyboard Shortcuts. The
menu bar, the palette, the Keyboard Shortcuts panel, the tooltips, the text
that names a key, the terminal's line keys and the plugin cards' Back and
Forward all read it, and a plugin's command keys join it from its manifest
(Commands). The tables below are the
defaults.
The palette and the menu bar come from one command list. A command with two default keys shows the first in the menu; a key monitor runs the first's command for the second (⇧⌘P, ⌃⇥, ⌃⇧⇥, ⌘=), always on the key window's own command list. It takes ⌃⇥ and ⌃⇧⇥ in every card and text field (a chat's composer, a browser's address), so they never move the keyboard to the next terminal instead. A key the user records is the action's only key.
One pattern for every level: the key says the action, the modifier says the
level. T makes, W closes, [ and ] step, 1 to 9 jump. ⌘ is the pane
(and the tab for New Tab and the jumps), ⇧⌘ the tab, ⌃⌘ the space, ⌥⌘ the
session. Arrows mean panes, except ⇧⌘← and ⇧⌘→ (Move Tab) and ⌃⌘← and ⌃⌘→
(Go Back and Go Forward through the tabs shown, Xcode's keys).
| Pane | Tab | Space | Session | |
|---|---|---|---|---|
| New | ⌘D right, ⇧⌘D down | ⌘T | ⌃⌘T | ⌥⌘T (Open Session) |
| Close | ⌘W (the tab too when it is the last pane) | ⇧⌘W | ⌃⌘W | ⌥⌘W (Detach Session) |
| Previous, next | ⌘[ ⌘] | ⇧⌘[ ⇧⌘], ⌃⇧⇥ ⌃⇥ | ⌃⌘[ ⌃⌘] | ⌥⌘[ ⌥⌘] |
| Jump to 1–9 | ⌘1–9 | ⌃⌘1–9 | ⌥⌘1–9 | |
| Swipe | ⌥ + swipe up or down | ⌥ + swipe sideways, or sideways on the sidebar |
Pane steps follow the split tree, as in Ghostty: left before right, top before bottom, round the ends. Tab and space steps go round the ends too, spaces in herdr's order, pinned ones first, less the hidden ones (the sidebar's in Manual); session steps stop at the first and last page. A split gives the keyboard to the new pane. A tab, space or session shown, however it comes into view, gives the keyboard to its keyboard card.
Keyboard Shortcuts panel. ⌘/ (Help ▸ Keyboard Shortcuts, also in the palette) opens a glass panel over the window, like the palette. It lists the important keys, as set, in two columns grouped as Panes, Tabs, Spaces, Sessions, App and Edit, and Customize… under them opens Settings ▸ Keyboard Shortcuts, which has every key. Each modifier and key is its own keycap. An action's second key follows after "or". A set of actions that differ only by the key shows as one row with one keycap for the keys while each keeps its default: "Select Pane ⌥ ⌘ ←→↑↓", "Show Tab 1–9 ⌘ 1–9"; one changed, each shows on its own row. Esc, ⌘/ or a click outside closes it.
| Keys | Action | Menu |
|---|---|---|
| ⌘N | New Window, which lists every machine's sessions to pick from | File |
| ⌘T | new tab, in the keyboard card's folder | File |
| ⌃⌘T | new space | File |
| ⌥⌘T | Open Session (the panel) | File |
| ⇧⌘G | Go to Directory (in a card on This Mac that runs only its shell); over a File, Markdown or tree page or a Git diff, Find Previous | File, Edit |
| ⌘S | Save, on a File or Markdown page | File |
| ⌘R | Reload Page, on a browser card | View |
| ⌥⌘R | Redraw Terminal: the program in a terminal pane paints its screen again (a one-column size change and back); over a plugin card, Reload Plugin. Terminal's Soft Reset key | File; Reload Plugin: View ▸ Pane |
| ⇧⌘R | Replace Pane: a fresh shell in the pane's place, size and folder, with the keyboard (asks first unless the pane runs only its shell); on an agent, browser or plugin pane the new shell shows the New pane chooser | File |
| ⌘W | close the pane (asks first unless the pane runs only its shell) | File |
| ⇧⌘W | close the tab (asks first when a program runs in it) | File |
| ⌃⌘W | close the space (asks first when a program runs in it) | File |
| ⌥⌘W | Detach Session: the page leaves the window, the session keeps running | File |
| ⇧⌘I | Insert Path | Edit |
| ⌘L | Open Location: the address field of a browser card | Edit |
| ⌥⌘I | Show Web Inspector, on a browser card | Edit |
| ⌘F | find in the keyboard card; on a File or Markdown page, the editor's find bar; on File Explorer's tree and a Git diff, its find bar (a filter of the files) | Edit |
| ⌘G, or Enter in the find bar | next match (⇧Enter: previous match); in a Git diff, Next Change | Edit |
| ⌘E | Lock Editing: locks and unlocks a Markdown page | View |
| ⇧⌘E | Switch Between Tree and File: the keyboard between a file and the tree beside it, in a File Explorer card | View |
| ⌘K | the command palette; ⇧⌘P also opens it (Rex's key) | View |
| ⌃⌘S | show or hide the sidebar | View |
| ⌃⌘O | Show Overview: the overview on or off | View |
| ⌘D, ⇧⌘D | split right, split down | View ▸ Pane |
| ⇧⌘↩ | zoom or restore the card | View ▸ Pane |
| ⌥⌘= | Balance Splits: even out the splits of the tab | View ▸ Pane |
| ⌥⌘ + arrow | Select Pane Left, Right, Up, Down: the keyboard goes to the next card that way (as in Ghostty and iTerm2) | View ▸ Pane |
| ⌘[ and ⌘] | Select Previous Pane, Select Next Pane, over every card | View ▸ Pane |
| ⌃- and ⌃⇧-, ⌘← and ⌘→ | over a plugin card, Back and Forward (VS Code's Go Back and Go Forward), ahead of the page's own keys; an arrow key in a text field still moves the caret | |
| ⌘← ⌘→, ⌘⌫, ⌥← ⌥→ | in a terminal: to the line's start and end, delete to the line's start, back and forward a word (⌃A, ⌃E, ⌃U, ⌥B, ⌥F to the program) | |
| ⌥⇧⌘ + arrow | Move Pane: swap the keyboard card with the card that way | View ▸ Pane |
| ⌘+ (and ⌘=), ⌘-, ⌘0 | Increase, Decrease, Reset Font Size: the text size; on a browser card that has the keyboard, the page's zoom (⌘0 also undoes a pinch) | View |
| ⌃⌘F | full screen (the macOS key) | View |
| ⌘/ | Keyboard Shortcuts (the panel) | Help |
| ⌘1 … ⌘9 | switch tabs | Window |
| ⇧⌘[ and ⌃⇧⇥, ⇧⌘] and ⌃⇥ | previous and next tab | Window |
| ⌃⌘← and ⌃⌘→ | Go Back and Go Forward through the tabs shown before, across spaces (History). Xcode's keys: VS Code's ⌃- and ⌃⇧- are a plugin card's Back and Forward, a browser's ⌘[ ⌘] the pane steps | Window |
| ⇧⌘←, ⇧⌘→ | move the tab left, right (as iTerm2; in a text field they still select to the line's start or end) | Window |
| ⌃⌘[ and ⌃⌘] | previous and next space | Window |
| ⌃⌘1 … ⌃⌘9 | switch spaces, in herdr's order, pinned ones first, less the hidden ones (the sidebar's in Manual) | Window |
| ⌥⌘[ and ⌥⌘] | previous and next session | Window |
| ⌥⌘1 … ⌥⌘9 | switch sessions, in the window's order | Window |
Close Window (File), Float Pane and Resize Pane (View ▸ Pane), all in the palette too, have no shortcut (⌘W is Close Window when no main window has the keyboard): the window's close button closes it, the card header's button floats a pane, and the dividers and Balance Splits cover resizing. Open Terminal Here, Start Agent Here, Open Browser Here, Open File Explorer Here and Open Git Here (File, the card's menu, the palette) have none either (New pane). In the palette: ↩ runs, ⎋ goes back or closes, ⌫ on an empty field goes back.
The menu bar redraws whenever a command's title, key, or enabled state changes: SwiftUI rebuilds the menu bar only when the window's command list changes, and a stale disabled item would swallow its key (⇧⌘↩ after a split).
Rex shortcuts not taken, and why:
- ⌘K stays the palette (D48), so Change Session (⌘K in Rex) is not taken; sessions open with Open Session (⌥⌘T).
- ⌘/ (sidebar) is Keyboard Shortcuts instead: ⌃⌘S is the macOS sidebar key.
- ⌘R reloads only a browser card's page: the snapshot is live, so nothing else needs a refresh. Replace Pane is ⇧⌘R, Redraw Terminal ⌥⌘R: no card has both Redraw Terminal and Reload Plugin, and ⌃L stays the program's own (a shell clears its screen with it).
- Find Previous has a shortcut only over a File or Markdown page: elsewhere ⇧⌘G is Go to Directory. ⌘S and ⌘E have their key only over the cards that use them, because a disabled menu item swallows its key (File Explorer and Git).
- ⇧⌘← and ⇧⌘↑ (Split Left, Split Up) would steal select-to-line-start from text fields.
How it works: Platform and code, Agents and notifications.