Everything about how the overlay looks is one theme name plus an optional [style] table. This
page is the whole catalogue: with it alone you can assemble a complete custom appearance without
reading any source.
The three-step chain
Every visual value is resolved once, at start-up, along one chain:
[style] override → named theme (colours only) → built-in default
So an override wins over the theme, the theme wins over the default, and a value you do not mention
falls through to whatever the next step supplies. [style] may be given with no theme at all, in
which case the overrides land on top of the default theme.
Two consequences worth stating outright:
- A theme is a palette and nothing else. It can change colours; it cannot change a font or a
dimension. Switching theme therefore recolours the overlay and never moves it —darkand
lightproduce a surface of exactly the same size and position. - A dimension outside its range is clamped, not rejected. The clamp is reported, the nearer
bound is used, and everything else in your file still applies.
theme = "light"
[style]
highlight = "#c04a2f"
font_family = "JetBrains Mono"
text_size = 0.85
width_fraction = 0.95
Colours
Eleven keys. Each accepts #rgb, #rrggbb or #rrggbbaa, and the two columns are what each
built-in theme sets it to.
Form: #rgb, #rrggbb, or #rrggbbaa. Alpha defaults to opaque when omitted. No other
notation is accepted (research R25).
Set by a theme: yes — these eleven values are what a built-in theme sets (FR-045, FR-049).
| Key | Draws | Default (dark) |
light |
|---|---|---|---|
backdrop |
Overlay background | #17171ced |
#f7f7faed |
highlight |
Highlighted entry background | #336bb8 |
#2e70cc |
active_mark |
Active-workspace mark | #6bb873 |
#298c47 |
text |
Primary entry text (workspace name) | #ebebf0 |
#1c1c24 |
text_highlighted |
Primary text on the highlighted entry | #ffffff |
#ffffff |
text_dim |
Secondary text (window names) | #a8a8b3 |
#595966 |
text_dim_highlighted |
Secondary text on the highlighted entry | #dbe6f5 |
#e0ebfa |
miniature |
Miniature background | #292930 |
#e6e6ed |
window |
Tiled window rectangle fill | #4d5261 |
#c2c7d6 |
window_floating |
Floating window rectangle fill | #61667a |
#adb5c9 |
window_edge |
Window rectangle edge | #858c9e |
#737a8f |
Two of the eleven are marks rather than fills, and what they draw is worth stating:
highlightfills the background of the entry the keyboard is currently on — the one that
commits when you release the modifier.active_markmarks the workspace each monitor is already displaying. In the grid
presentation it is the frame around the miniature; in the list it is the short bar down the left
edge of the row, whose thickness ismark_width. It is drawn as a mark rather than as a
different fill precisely so it survives being highlighted at the same time: one entry can carry
thehighlightbackground and theactive_markframe together, and the two answer different
questions — where you are about to go, and where you are now.
With several monitors you will see several active marks at once, one per monitor, since each
monitor has its own current workspace. Every other workspace in the overlay carries none.
The built-in themes
There are two, and their values are the two columns of the table above: theme = "dark" (the
default) and theme = "light". Naming one sets all eleven colours at once; any of them can still
be overridden individually on top of it. An unknown name is reported, the default applies, and
every other setting in your file still applies.
text_highlighted is white in both, because the highlight stays a saturated blue in both.
Contrast is not validated. A low-contrast or fully transparent combination renders exactly as
asked — if the overlay comes up unreadable, that is the palette you wrote.
Fonts
Set by a theme: no. Fonts are shared defaults, not per-theme values (FR-049, research R24). They are
reachable only as overrides.
| Key | Form | Default | Range | Notes |
|---|---|---|---|---|
font_family |
string | Sans |
— | Any family name. An absent family is substituted by the platform; text stays readable and nothing is reported (US4-AS5). |
text_size |
float | 0.78 |
0.3..=1.0 |
Fraction of the row’s text height. The row height follows it, so text is never clipped by its own row. |
Dimensions
Ten values, each a number with a documented range. These are the ones that are clamped rather than
rejected, so an out-of-range value is safe to experiment with — you will be told what was used.
Set by a theme: no — same reason as fonts. Values are logical units, scaled per monitor by the
existing rule (FR-055), so no per-monitor variants exist.
An out-of-range value is clamped to the nearer bound and reported; it is not rejected and does
not fall back to the default (FR-054, research R26).
| Key | Form | Default | Range | Governs |
|---|---|---|---|---|
text_line_height |
integer | 20 |
8..=200 |
Entry text height; drives row height |
row_padding |
integer | 8 |
0..=100 |
Vertical padding within a row |
overlay_padding |
integer | 12 |
0..=200 |
Overlay’s outer padding |
width_fraction |
float | 0.8 |
0.1..=1.0 |
Overlay width cap, as a fraction of the monitor |
height_fraction |
float | 0.8 |
0.1..=1.0 |
Overlay height cap, as a fraction of the monitor |
grid_cell_width |
integer | 240 |
40..=2000 |
Miniature cell width |
grid_cell_height |
integer | 135 |
40..=2000 |
Miniature cell height |
grid_gap |
integer | 12 |
0..=200 |
Gap between grid cells |
corner_radius |
float | 0.28 |
0.0..=1.0 |
Corner rounding, as a fraction of row height |
mark_width |
float | 0.12 |
0.0..=1.0 |
Active-mark width, as a fraction of row height |
Why these ranges
The bounds are not cosmetic; they are what makes SC-023 provable — that no valid combination can
produce an unusable overlay:
width_fractionandheight_fractioncap at1.0, so the overlay can never exceed its monitor.text_line_heightand the grid cell sizes have non-zero minimums, so the viewport arithmetic in
ui/layout.rscannot divide by zero and a row can always hold its text.- Nothing here can make entry size depend on the number of workspaces — entries stay fixed-size and
the overlay scrolls, exactly as FR-019 requires (FR-053). - The cap wins over the entry size. These ranges are absolute while the cap is a fraction of
the monitor, so a large enough entry on a small enough monitor asks for more than the cap allows
— agrid_cell_widthof2000on an 800-pixel panel, say.ui/layout.rsholds the overlay to
the cap and lets that one entry be clipped by the surface edge, rather than scaling it down to
fit, which FR-019 forbids. Below that extreme the row and column counts are chosen so the result
fits, and the clamp never engages. - Zero means zero.
row_padding,overlay_paddingandgrid_gapadmit0, and a zero there
is carried through to the buffer rather than floored to one device pixel — a floor would be the
one measurement that did not scale per monitor, which FR-055 forbids.
What is deliberately not a setting
Listed so their absence reads as a decision rather than an oversight (Principle II):
| Not a setting | Why |
|---|---|
| Icon size | Follows the resolved text height (FR-052), so it cannot disagree with the row it sits in. |
| Icon tint or recolouring | Program artwork is drawn as supplied (FR-051). Only the placeholder follows text. |
| Per-monitor or per-presentation values | One appearance applies everywhere (FR-048). |
| Background blur, shadows, gradients, animation | Out of scope — this feature makes existing drawing configurable, it adds no new visual elements (spec Assumptions). |
| Resolution timing | Not user-facing; settled in research R27 and the spec’s Assumptions. |
| A second colour notation | One form, by decision (research R25). |