1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
|
# GTK3 and GTK4
Generated from the palette, installed as `~/.themes/udt`. One fixed name for
every scheme, like Kvantum: the name is referenced by gsettings and by the
GTK4 symlink, so a per-scheme name would mean rewriting both on every switch
and leaving stale themes behind.
Derived from `catppuccin/gtk` v1.0.3, which is **archived upstream**. Forking
it is deliberate: it is the theme this desktop already ran, and it will not
track future GTK4/libadwaita changes. Replacing it means replacing these
templates, not editing a consumer.
## What is substituted
Colours appear in two forms and both are replaced: the hex (`#b7bdf8`) and the
bare decimal triple GTK writes inside `rgba()` (`183, 189, 248`). Substitution
is whole-file, not confined to the `@define-color` block at the tail, because
the 8400 lines above it hardcode palette colours too. That was measured, not
assumed: 338 occurrences in GTK3 and 328 in GTK4 sit outside the block.
`#eff1f5` is upstream's foreground, which is Latte `text`, a near-white it
pairs with a Macchiato background. It maps to the `fg` role so GTK text matches
rofi, kitty and waybar rather than being brighter than all of them.
The generator replaces `@ROLE_RGB@` before `@ROLE@`, because `@FG@` is a prefix
of `@FG_RGB@` and the other order would eat the start of the triple placeholder
and leave a literal `_RGB@` in the CSS.
## What is left alone
Neutral greys and upstream's derived tints: `#3e4152`, `#2e3143`, the
`rgba(0, 0, 0, ...)` shadows, `#ffffff`, and the `*_500`/`*_700`/`*_900`
Material swatch constants, which ship with every build and are not theme
colour. Same rule as the Kvantum SVG: shading is not palette.
Two decimal-triple values are mapped anyway, because they are semantic rather
than shading: `17, 17, 27` (Latte crust, used as text on the accent) becomes
`bg_deep`, and `182, 106, 119` (a darkened red on the close button) becomes
`critical`.
## Derived tints
Twelve hexes are shades upstream computed from a palette colour rather than
taking one, and each paints exactly one widget surface. Leaving them literal,
which is the right rule for the neutral greys, left twelve Macchiato-tinted
spots on every other scheme, including the window close, minimise and maximise
hover fills. They are substituted as `@ROLE_PCT@`, where the percentage is that
shade's brightness relative to its base, measured off the Macchiato theme these
came from.
| Was | Paints | Now |
| --- | --- | --- |
| `#a0a8f6` | infobar hover | `accent` at 93% |
| `#bfc5f8` | selected progress bar (GTK3 only) | `accent` at 103% |
| `#d3d7fb` | selection-mode titlebar backdrop (GTK3 only) | `accent` at 109% |
| `#e5e8fd` | check and radio, checked and hovered | `accent` at 118% |
| `#eea2ae` / `#ea7183` / `#e55267` | infobar, window close active | `critical` at 110/92/79% |
| `#eedbb5` / `#eaca89` / `#e5bd6b` | infobar, window minimise active | `warning` at 105/94/86% |
| `#b8e0ad` / `#80ca68` | infobar, window maximise active | `success` at 109/81% |
`shade()` scales each channel and clamps at 255, so it keeps the relationship
rather than reproducing the upstream value exactly. It is closest on the accent
family and crudest on the reds, where upstream darkened toward saturation and a
flat channel scale darkens toward grey. That is a deliberate ceiling: these are
low-traffic surfaces, and a per-tint hand-tuned value for nine schemes would be
108 numbers to maintain.
`#e5e8fd` is read as the accent pushed near-white rather than as `fg`, which it
is numerically closer to, because it backs a checked-and-hovered check box
whose glow is already an accent tint.
## Assets
67 SVGs, carrying only three palette colours: `bg`, `bg_alt` and the accent,
the last in 36 of them. The upstream `gtk-3.0/assets` and `gtk-4.0/assets`
directories are byte-identical, so one templated set is rendered and copied
into both.
They are discovered from disk by the generator rather than listed, because
they arrive as a set from upstream and a hand-written list would silently miss
one added by a future refresh.
## Gotchas
- **GTK3 takes its theme name from gsettings on Wayland**, not `settings.ini`.
The file is written too, but gsettings wins; it was once pinned to `Breeze`,
silently overriding the file for who knows how long.
- **GTK4 ignores `gtk-theme-name` entirely.** Its stylesheet is imported by
`~/.config/gtk-4.0/gtk.css` through the `theme` symlink.
- **`gtk-dark.css` must exist** alongside `gtk.css`. Apps request one or the
other depending on the prefer-dark setting; upstream ships them identical.
- **GTK apps only reread a theme at startup**, like Qt. A running app keeps the
old colours, which is why the accent here is the fixed fallback rather than
the wallpaper-tracking one.
- **A placeholder with no matching role renders literally** and GTK drops the
declaration with no error. `udt-palette --selftest` asserts every placeholder
in these templates maps to a role, because 8500 lines is too much to eyeball.
|