Skip to content

Add a GTK4 / libadwaita theme - #439

Open
george-petrakis wants to merge 1 commit into
grassmunk:masterfrom
george-petrakis:feat/gtk4-libadwaita-theme
Open

george-petrakis wants to merge 1 commit into
grassmunk:masterfrom
george-petrakis:feat/gtk4-libadwaita-theme

Conversation

@george-petrakis

Copy link
Copy Markdown

Adds a GTK4 / libadwaita stylesheet to Chicago95. This addresses #256 ("Theme not applying to GTK-4") and covers the GTK4 part of #363 ("use this on gnome?").

At the moment every GTK4 app is unthemed, and on a current GNOME that covers most of the desktop, since Files, Settings, Text Editor and Console are all libadwaita.

Why the earlier GTK4 attempts didn't stick

The prototype in #256 was abandoned in 2024. The reason matters, because it dictates how this has to be shipped.

libadwaita ignores gtk-theme. On adw_init() it sets gtk-theme-name to Adwaita-empty, which is a real stylesheet shipped inside libgtk that contains zero bytes, and then installs its own three providers at GTK_STYLE_PROVIDER_PRIORITY_THEME (200). The result is that ~/.themes/Chicago95/gtk-4.0/gtk.css does get found and parsed, GTK even warns about loading it, and then it is discarded. A theme that ships only a theme directory is a no-op in every libadwaita app while looking like it should work. I confirmed this at runtime: with the theme installed, gtk-theme-name reads back as Adwaita-empty.

The hook that does survive is $XDG_CONFIG_HOME/gtk-4.0/gtk.css. GTK loads that at PRIORITY_USER (800), which is above libadwaita's own sheet, so that is where the theme has to go. installer.py now installs it there.

GTK_THEME=Chicago95 is not the recommended route. It does change the look, but only because it makes libadwaita skip loading its stylesheet altogether, so its widgets come out unstyled rather than restyled.

What's here

Theme/Chicago95/gtk-4.0/, split the same way gtk-3.0 already is, with gtk.css holding the palette, the global resets and the @import list:

file covers
gtk.css palette, libadwaita colour overrides, resets, imports
gtk-widgets.css button, entry, check/radio, switch, spinbutton, scale, progressbar
gtk-containers.css window/CSD titlebar, windowcontrols, notebook, scrollbar, paned
gtk-lists.css listview, columnview, gridview, treeexpander, sidebars
gtk-menus.css popovers, menus, dropdowns, tooltips, dialogs, toasts
gtk-adwaita.css AdwToolbarView, preferences rows, .boxed-list, tabs, status pages
gtk-dark.css dark-variant entry point; Chicago95 has one look, so it re-imports gtk.css
settings.ini theme-level GTK settings

Palette and metrics come from gtk-3.0 rather than being re-picked, including the parts that look like typos but aren't. bg_bright is shade(white, 0.99), which computes to #fcfcfc rather than #ffffff, and the GTK4 sheet uses the same value. There is no border-radius, and no gradients, shadows or transitions.

gtk-dark.css is not filler. With color-scheme=prefer-dark GTK asks for the dark variant, and when it is missing GTK falls back to its own builtin Default-dark rather than to gtk.css, so Chicago95 would disappear entirely for anyone running dark mode.

Also in this branch:

  • installer.py installs the user CSS inside a marker-delimited block, keeps a backup, and gains disable_gtk4_user_css() to remove it again. It also gains a GNOME branch that configures the theme with gsettings instead of xfconf-query, chosen from XDG_CURRENT_DESKTOP rather than assuming Xfce.
  • INSTALL.md gets a GNOME/GTK4 section that spells out what this does not cover.

Limits

  • GNOME Shell is not themed. The in-tree gnome-shell/ theme targets 3.32, and 46% of its selectors no longer exist on Shell 50. That is out of scope here.
  • Snap and Flatpak apps are not covered, since they don't see the host's user CSS.
  • Window decorations are CSD under Mutter, so the xfwm4 themes don't apply. The CSD titlebar is styled instead, including headerbar.default-decoration, which is what mutter-x11-frames uses for Xwayland windows.
  • No thumbnail.png. A maintainer screenshot would be better than anything I could make up.
  • Window buttons use symbolic icons recoloured to black, so they follow whatever icon theme is active. With the Chicago95 icon theme they look right. The sheet does not force -gtk-icon-style: regular, because that makes GTK substitute whatever full-colour legacy icon the active theme ships; under Yaru that turns the close button into a red circle.

Verification

Checked on GTK 4.22.4 / libadwaita 1.9.1 (Ubuntu 26.04, GNOME 50, Wayland):

  • the sheet parses through GtkCssProvider with 0 errors, with @imports followed;
  • the Chicago95 palette resolves in both a libadwaita process and a plain GTK4 one, checked by reading the resolved values back rather than by eye;
  • gtk4-widget-factory and adwaita-1-demo both run with no GTK warnings. An earlier revision of this branch produced 14 negative-min-size warnings; those are fixed, with the causes written up in comments next to the rules;
  • all 44 relative url() asset references resolve;
  • no undeclared @colour references.

Not checked: GNOME Shell, snaps and flatpaks, HiDPI, RTL, and Xfce. I don't have an Xfce session to confirm the GTK3 side still behaves, though no gtk-3.0 file is touched by this branch.

If you'd rather the installer changes landed separately, or want the files named or split differently, I'm glad to rework it.

Chicago95 has had no GTK4 stylesheet, so every GTK4 app - and in particular
every libadwaita app, which is most of GNOME now - has been unthemed. This
adds Theme/Chicago95/gtk-4.0/, written against GTK 4.22 and libadwaita 1.9,
plus the installer and docs changes needed for it to actually take effect.

The sheet is split the same way gtk-3.0 is, with gtk.css owning the palette,
the global resets and the @import list:

  gtk.css             palette, libadwaita colour overrides, resets, imports
  gtk-widgets.css     button, entry, check/radio, switch, spin, scale, progress
  gtk-containers.css  window/CSD titlebar, windowcontrols, notebook, scrollbar
  gtk-lists.css       listview, columnview, gridview, treeexpander, sidebars
  gtk-menus.css       popovers, menus, dropdowns, tooltips, dialogs, toasts
  gtk-adwaita.css     AdwToolbarView, preferences rows, .boxed-list, tabs, etc.
  gtk-dark.css        dark-variant entry point (Chicago95 has one look)
  settings.ini        theme-level GTK settings

Why the delivery path is what it is. libadwaita does not honour gtk-theme: on
init it sets gtk-theme-name to "Adwaita-empty" (a real, zero-byte stylesheet
inside libgtk) and installs its own providers at PRIORITY_THEME, so a theme
directory is read and then discarded. The one hook that survives is
$XDG_CONFIG_HOME/gtk-4.0/gtk.css, which GTK loads at PRIORITY_USER (800) and
which therefore outranks libadwaita's own sheet. So installer.py now also
installs the sheet there, behind a marker-delimited block with a backup, and
disable_gtk4_user_css() removes it again. GTK_THEME= is deliberately not the
recommended route: it makes libadwaita skip loading its stylesheet entirely,
which leaves its widgets unstyled rather than restyled.

installer.py additionally grows a GNOME branch that configures the theme with
gsettings instead of xfconf-query, and picks the branch from
XDG_CURRENT_DESKTOP rather than assuming Xfce.

INSTALL.md documents the limits honestly: GNOME Shell itself is not themed,
snap and flatpak apps are not covered, and window decorations are CSD under
Mutter so the xfwm4 themes do not apply.

Verified on GTK 4.22.4 / libadwaita 1.9.1: the whole sheet parses with zero
errors through GtkCssProvider; the Chicago95 palette resolves in both a
libadwaita and a plain-GTK4 process; gtk4-widget-factory and adwaita-1-demo
both run with no GTK warnings; and all 44 relative url() asset references
resolve. Not verified: GNOME Shell, snaps/flatpaks, HiDPI, and RTL.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant