"Themes in GNOME" is a complicated topic in technical and social terms. Technically there are a lot of incomplete moving parts; socially there is a lot of missing documentation to be written, a lot of miscommunication and mismatched expectations.
The following is a brief and incomplete, but hopefully encouraging, summary of the status of themes in GNOME. I want to give you an overall picture of the status of things, and more importantly, an idea of how you can help. This is not a problem that can be solved by a small team of platform developers.
I wish to thank Alexander Mikhaylenko for providing most of the knowledge in this post.
Frame of reference
First, I urge you to read Cassidy James Blaede's comprehensive "The Need for a FreeDesktop Dark Style Preference". That gives an excellent, well-researched introduction to the "dark style" problem, the status quo on other platforms, and exploratory plans for GNOME and Elementary from 2019.
Go ahead, read it. It's very good.
There is also a GUADEC talk about Cassidy's research if you prefer to watch a video.
Two key take-aways from this: First, about this being a preference, not a system-enforced setting:
I’m explicitly using the language “Dark Style Preference” for a reason! As you’ll read further on, it’s important that this is treated as a user “preference,” not an explicit “mode” or strictly-enforced “setting.” It’s also not a “theme” in the sense that it just swaps out some assets, but is a way for the OS to support a user expressing a preference, and apps to respond to that preference.
Second, about the accessibility implications:
Clearly there’s an accessibility and usability angle here. And as with other accessibility efforts, it’s important to not relegate a dark style preference to a buried “Universal Access” or “Accessibility” feature, as that makes it less discoverable, less tested, and less likely to be used by folks who could greatly benefit, but don’t consider themselves “disabled.”
Libadwaita and the rest of the ecosystem
Read the libadwaita roadmap; it is very short, but links to very interesting issues on gitlab.
For example, this merge request is for an API to query the dark style and high-contrast preferences. It has links to pending work in other parts of the platform: libhandy, gsettings schemas, portals so that containerized applications can query those preferences.
As far as I understand it, applications that just use GTK3 or libhandy can opt in to supporting the dark style preference — it is opt-in because doing that unconditionally in GTK/libhandy right now would break existing applications.. If your app uses libadwaita, it is assumed that you have opted into supporting that preference, since libadwaita's widgets already make that assumption, and it is not API-stable yet — so it can make that assumption from the beginning.
There is discussion of the accessibility implications in the design mockups.
CSS parity across implementations
In GNOME we have three implementations of CSS:
-
librsvg uses servo's engine for CSS selector matching, and micro-parsers for CSS values based on servo's cssparser.
-
GTK has its own CSS parser and processor.
-
Gnome-shell uses an embedded version of libcroco for parsing, but it does most of the selector matching and cascading with gnome-shell's own Shell Toolkit code.
None of those implementations supports @media
queries nor custom
properties with var()
. That is, unlike in the web platform, GNOME
applications cannot have this in their CSS:
@media (prefers-color-scheme: dark) {
/* styles for dark style */
}
@media (prefers-color-scheme: light) {
/* styles for light style */
}
Or even declaring colors in a civilized fashion:
:root {
--main-bg-color: pink;
}
some_widget {
background-color: var(--main-bg-color);
}
Or combining the two:
@media (prefers-color-scheme: dark) {
:root {
--main-bg-color: /* some nice dark background color */;
--main-fg-color: /* a contrasty light foreground */;
}
}
@media (prefers-color-scheme: light) {
:root {
--main-bg-color: /* some nice light background color */;
--main-fg-color: /* a contrasty dark foreground */;
}
}
some_widget {
background-color: var(--main-bg-color);
}
Boom. I think this would remove some workarounds we have right now:
-
Just like GTK, libadwaita generates four variants of the system's stylesheet using scss (regular, dark, high-contrast, high-contrast-dark). This would be obviated with
@media
queries forprefers-color-scheme
,prefers-contrast
,inverted-colors
as in the web platform. -
GTK has a custom
@define-color
keyword, but neither gnome-shell nor librsvg support that. This would be obviated with CSS custom properties - thevar()
mechanism. (I don't know if some "environmental" stuff would be better done asenv()
, but none of the three implementations support that, either.)
Accent colors
They are currently implemented with GTK's @define-color
, which is
not ideal if the colors have to trickle down from GTK to SVG icons,
since librsvg doesn't do @define-color
- it would rather have
var()
instead.
Of course, gnome-shell's libcroco doesn't do @define-color
either.
Look for @accent_color
, @accent_bg_color
, @warning_color
, etc. in the default
stylesheet,
or better yet, write documentation!
The default style:
Accent color set to orange (e.g. tweak it in GTK's CSS inspector):
/* Standalone, e.g. the "Page 1" label */
@define-color accent_color @orange_5;
/* background+text pair */
@define-color accent_bg_color @orange_4;
@define-color accent_fg_color white;
Custom widgets
Again, your app's custom stylesheet for its custom widgets can use the
colors defined through @define-color
from the system's stylesheet.
Recoloring styles
You will be able to do this after it gets merged into the main branch, e.g. recolor everything to sepia:
@define-color headerbar_bg_color #eedcbf;
@define-color headerbar_fg_color #483a22;
@define-color bg_color #f9f3e9;
@define-color fg_color #483a22;
@define-color dark_fill_color shade(#f9f3e9, .95);
@define-color accent_bg_color @orange_4;
@define-color accent_color @orange_5;
Of course shade()
is not web-platform CSS, either. We could keep
it, or redo it by implementing calc()
function for
color values.
Recoloring icons
Currently GTK takes some defined colors and creates a chunk of CSS to inject into SVG for icons. This has some problems.
There is also some discussion about standardizing recolorable icons across desktop environments.
How you can help
Implement support for @media
queries
in our three CSS implementations (librsvg, gnome-shell, GTK). Decide
how CSS media features like prefers-color-scheme
,
prefers-contrast
, inverted-colors
should interact with the GNOME's
themes and accessibility, and decide if we should use them for
familiarity with the web platform, or if we need media features with
different names.
Implement support for CSS custom properties -
var()
in our three CSS implementations. Decide if we should replace the
current @define-color
with that (note that @define-color
is only
in GTK, but not in librsvg or gnome-shell).
See the libadwaita roadmap and help out!
Port applications to use the proposed APIs for querying the dark style preference. There are a bunch of hacky ways of doing it right now; they need to be migrated to the new system.
Personally I would love help with finishing to port gnome-shell's styles to Rust - this is part of unifying librsvg's and gnome-shell's CSS machinery.