i3 Desktop Part 8: Theming the Whole Stack
Theming is easy to dismiss as cosmetic.
Sometimes it is. But on a custom desktop, inconsistent theming is also a sign that the system is made of pieces that do not know about each other. The point of theming this setup is not to make screenshots look nice. It is to make the desktop feel like one environment.
The theme is Catppuccin Frappe with a mauve accent. The main font is JetBrains Mono. Icons are Papirus-Dark. The cursor theme is Bibata-Modern-Ice.
Those choices are recorded in ~/.config/i3/theme.env.
That file holds both app theme names and the colour palette:
- GTK theme
- icon theme
- font names
- cursor theme and size
- Qt style settings
- Catppuccin colour variables
Scripts that need colours can source that file. Apps with their own config format keep explicit colours.
Applying GTK and Qt settings
The theme is applied by .local/bin/i3-apply-theme.
That script exports the GTK, Qt, cursor, and desktop session environment. It updates gsettings for GTK theme, icons, cursor, font, monospace font, and dark colour scheme. It also updates D-Bus activation environment so apps launched later see the same values.
For older X applications, it merges cursor and font rendering settings through xrdb. For GTK apps outside GNOME, it starts xsettingsd with the repo’s config.
Qt is handled through QT_STYLE_OVERRIDE=kvantum and QT_QPA_PLATFORMTHEME=qt5ct.
This is not perfect. Linux theming never is. But it covers the common paths without relying on GNOME to be the active desktop.
i3, rofi, and dunst
i3 itself uses the Catppuccin palette for window borders, focused and inactive states, urgent windows, placeholders, and the bar.
Rofi has its own .rasi theme with the same base, panel, text, muted, accent, urgent, and border colours. It uses fuzzy matching, JetBrains Mono, a compact centred window, and a simple selected-row style.
Dunst uses the same colour language:
- low urgency is muted
- normal urgency uses the base background and mauve frame
- critical urgency uses red with crust text
Notifications appear in the top-right, follow keyboard focus, use Papirus icons, and keep a short history. The result feels integrated without being flashy.
Kitty as the main terminal
Kitty is the main terminal in the setup.
The config uses JetBrains Mono, Catppuccin Frappe colours, subtle opacity, hidden window decorations, a top tab bar, and split-window keybindings. Clipboard behaviour is tuned so Ctrl+c copies selected text or interrupts when nothing is selected, and Ctrl+v pastes.
That last part is small but important. I want terminal muscle memory to be comfortable without breaking normal shell behaviour.
Kitty also matters because other scripts use it as the default place to open terminal editors. Notes, radio station editing, and browser editor actions all benefit from a terminal that is predictable.
qutebrowser as the managed browser
qutebrowser is the browser this desktop manages directly.
The config deliberately themes browser chrome, not website fonts. The UI uses JetBrains Mono and Catppuccin colours, while websites keep their own fonts. Dark mode is enabled with a smart page policy, but images are not forced.
Keybindings make it fit the rest of the desktop:
Ctrl+lopens the URL/search promptCtrl+topens in a new tabCtrl+wcloses a tabCtrl+Shift+trestores a tabCtrl+fsearchesCtrl+TabandCtrl+Shift+Tabswitch tabsAlt+LeftandAlt+Rightnavigate history
It is still qutebrowser, but with enough familiar browser shortcuts that it works well as a daily browser.
Picom without making it the point
Picom is used for compositing, shadows, fading, and small corner radii.
The config avoids blur. It disables shadows and fades for docks, desktop windows, dunst, i3lock, rofi, maim, and selection windows. That keeps functional UI crisp and avoids visual weirdness around screenshots and menus.
This is the theme rule I keep coming back to: subtle is enough.
Consistency over decoration
The theme is not one central magic file because these tools do not all read one format.
Instead, the setup has a shared palette and then applies it in the right place for each tool. i3 gets i3 colours. rofi gets .rasi. kitty gets kitty colours. qutebrowser gets Python config. dunst gets dunstrc. GTK and Qt get their own environment and settings.
That is slightly repetitive, but it is reliable.
The payoff is that the desktop feels coherent. Menus, notifications, terminal, browser furniture, bar, lock screen, and window borders all speak the same visual language.
For a custom i3 desktop, that matters. It turns a collection of tools into a place I can sit and work.