Every colour, radius, spacing step and font in @gjsify/adwaita-web comes from a
CSS custom property. Set one above the widgets and everything below picks it up.
That is the whole theming API, and it is the part that keeps working across
upgrades.
Put the tokens on a container that wraps your widgets. This is the route that
behaves the same in light and dark mode: the only rules that give a token a
second, scheme-dependent value sit on :root and on the .theme-dark /
.theme-light elements, so a container below them keeps your values in both.
:root works too, with one thing to watch: most of the colour tokens get a
second value in dark mode, and that dark rule outranks a plain :root
declaration. Tokens that mean the same thing in both schemes are safe on :root:
the accent fill, the radii, the spacing scale, the fonts and the opacity tokens.
CSS
:root {
--accent-bg-color: #9141ac;
--font-family: 'Inter', sans-serif;
--card-radius: 16px;
}
If you want to move one of the scheme-dependent colours globally, give it a dark
counterpart as well, or move the override into a container as above.
Dark mode applies on its own under prefers-color-scheme: dark. To pin a scheme
for part of the page, put .theme-dark or .theme-light on any element, not
only on <html>:
Those two classes re-declare the full palette on the element they sit on. So if
one element carries both your theme and a .theme-* class, write the selector so
yours wins:
Adwaita has nine named accents: blue (the default), teal, green, yellow,
orange, red, pink, purple and slate. Applying one sets both accent
properties for you, picking the standalone variant that matches the target’s
current scheme:
Borders and disabled dimming, as libadwaita's own two-step (_colors.scss:263, 317-329): an OPACITY token the high-contrast query moves, and a colour DERIVED from it against `currentColor` — so a border follows the text colour of the surface it is drawn on instead of being a fixed grey
Three of them move on their own: under prefers-contrast: more the stylesheet
changes --border-opacity, --dim-opacity and --disabled-opacity. Every
colour derived from those follows, so --border-color sharpens without a query
of your own.
You can also write plain CSS. The components render in the light DOM, so a
higher-specificity rule of yours wins the way it normally would. Tokens are the
route that survives upgrades; a rule targeting internal markup is not. See
How It Works
for what that boundary does and does not guarantee.
If you would rather build a skin from the source, the SCSS partials ship with the
package at @gjsify/adwaita-web/scss/…, with @gjsify/adwaita-web/scss as the
entry point.
On the desktop, the accent is not yours to set through AdwStyleManager
A native GTK app takes its accent from the user’s GNOME setting, and
AdwStyleManager:accent-color is the wrong handle for an app’s own accent on every
runtime: it is an enum (AdwAccentColor — nine named accents), so a token colour
like #9141ac has no representation in it. It is read-only as well — libadwaita
installs the property readable-only, and every closure measured agrees — but the enum
is the reason that holds whatever a flag says, which is why it is the one given.
A native app can still override the palette, and by the same mechanism this page
uses on the web: redefining --accent-bg-color and its siblings on :root, in a
Gtk.CssProvider above libadwaita’s own. What is different on the desktop is the
provider priority — see
Styling on GTK
for the three measurements and the registry that wraps them.
Whether you should is a separate question, and the default answer is no: someone who
picked an accent in Settings expects your app to use it. If your app renders on both,
set the token for the web and leave the desktop alone unless the look is the point.