Skip to content

Platform UI Guidelines

A breadth-first survey of how eight platforms let an application follow the user's OS-level appearance preferences — not merely light and dark, but the accent color, the contrast level, forced colors and reduced motion — and of what an application must do with those preferences once it has them.

The motivating problem is concrete. hue bases every color it draws on a single sparkles.ui.theme.Theme value, chosen by name and then fixed for the life of the process. A user in dark mode gets whatever the default theme is; a user who switches to light mode at dawn gets no change at all until they restart. The survey asks what the platforms offer, what the field's consensus is, and what it would take to derive that Theme from the OS instead of authoring it.

This survey answers seven questions:

  1. What is the shared vocabulary — the appearance triple, semantic vs literal color, tone, quantized accents, forced colors, push vs poll, and why capability flags beat a support bit? See concepts.
  2. What each platform exposes, and how — the preference surface, the query API, the change-notification mechanism, and whether a palette comes with it. See the master catalog.
  3. What a self-rendering application can actually reach — with no native toolkit linked, which is the position sparkles:ui is in on every target. See comparison § Dimension 6.
  4. How to turn three scalars into a palette — tone placement, the tone-delta rule (measured, not assumed), and per-role accent resolution. See color-derivation.
  5. How somebody already solved this — libadwaita follows the native appearance on six platforms behind one interface. See libadwaita.
  6. Where the field agrees and where it genuinely forks, and where Sparkles stands against it today. See comparison and its delta table.
  7. What Sparkles should build — a milestoned plan for sparkles:appearance and the Theme derivation. See the proposal.

NOTE

Scope. This tree is about following OS preferences, not about window-system integration generally — for decorations, scaling, input translation and the event loop, see window-system-integration, whose os-apis subtree covers the same platforms from the windowing side. Where this survey needs a platform's windowing behaviour it links there rather than restating it.

Last reviewed: August 9, 2026


Master Catalog

One row per surveyed subject; the name links to its deep-dive. "Ships a palette" means the platform hands the application resolved colors rather than preferences it must derive from. "Reachable" is the survey's key column for Sparkles: whether a self-rendering application can read the preference without linking the platform's UI toolkit.

SubjectCategorySchemeAccentContrastPushShips a paletteReachable without the toolkit
iOS / iPadOSmobile OS3-valuednone2-valued✓ semanticno — UIKit traits only
Androidmobile OS3-valuedwallpaper-seeded3-valued✓ generated (5 × 13)scheme only; rest needs JNI
GNOMELinux desktop3-valued9 named2-valuedyes — D-Bus portal
KDE PlasmaLinux desktop3-valuedfree RGBnative only✓ 8 role setsyes — INI + portal
Windowsdesktop OS2-valuedfree RGB + ladderforced colorspartly — accent needs WinRT
macOSdesktop OS2-valued8 indexed2-valued✓ racy✓ semanticyes — global defaults
Terminal emulatorspty protocol2-valuednonenonethe 16-color paletteyes — two escape sequences
The websandboxed runtime2-valuednone4-valuedn/a — it is the sandbox
libadwaita AdwSettingsprior art (toolkit)abstracts 6 platformsquantized to 9boolean(the reference implementation)

Two facts frame the whole survey and are visible in the table. Light/dark is universal and solved; nothing else is — accent is missing on three platforms, contrast on two, and no two platforms agree on what "contrast" means. And push notification is universal, including over a pty — which makes re-theming at runtime, not reading at startup, the real requirement.

Library & platform deep-dives

Every deep-dive applies the same seven-dimension spine — preference surface, reading it, change notification, what the platform derives for you, what the vendor's guidelines require, reachability from a non-native application, and traps — so the pages can be read against each other. Where a dimension does not apply, the absence is recorded as a finding (the terminal has no accent; iOS has no accent; KDE exposes no contrast neutrally).

CategorySubjects
MobileiOS / iPadOS · Android (Material You)
Linux desktopGNOME (portal) · KDE Plasma
Desktop OSWindows · macOS
Non-GUI surfacesTerminal emulators · The web
Prior artlibadwaita AdwSettings
Applying itColor derivation · Comparison · Sparkles proposal

Taxonomy

By what the platform hands you

The platform gives youSubjectsThe application must…
A complete semantic paletteiOS, macOSname roles; write no color code
A generated tonal paletteAndroidname tokens; the wallpaper drives the rest
A full role-scoped paletteKDEmap its roles onto its own
Scalars onlyGNOME, Windows, terminal, webderive everything — see color-derivation

By how the scheme is answered

MechanismSubjects
A semantic valueiOS, Android, GNOME, KDE, macOS, terminal (mode 2031), web
Inferred from a colorWindows (documented!), terminal (OSC 11 fallback)

By accent model

ModelSubjects
Free RGBKDE, Windows, Android
Quantized to a named setGNOME (9), macOS (8), libadwaita (9)
Not exposediOS, terminal, web

Milestones

When each capability became available to applications.

YearMilestone
2018macOS Mojave ships Dark Mode; NSAppearance gains darkAqua
2019iOS 13 adds UITraitCollection.userInterfaceStyle; Android 10 adds system-wide dark theme
2019prefers-color-scheme reaches all major browsers (widely available Jan 2020)
2019Windows 10 exposes dark mode to Win32 via UISettings; DWMWA_USE_IMMERSIVE_DARK_MODE 19→20
2021Android 12 ships Material You — wallpaper-seeded tonal palettes as android.R.color.system_*
2021org.freedesktop.appearance color-scheme lands in xdg-desktop-portal
2023xdg-desktop-portal 1.17.1 adds accent-color
2023Android 14 adds a three-step contrast level (UiModeManager.getContrast())
2023iOS 17 replaces traitCollectionDidChange with registerForTraitChanges
2024libadwaita 1.6 adds accent colors, with native macOS and Windows backends
2024light-dark() reaches baseline availability in CSS
2024–2025DEC mode 2031 spreads across terminals — Contour, Ghostty 1.0, foot 1.23, kitty, iTerm2, VTE, tmux 3.6

The bottom row is the newest and, for hue, the most consequential: terminal applications only recently gained the semantic answer plus change notification that GUI applications have had since 2018.

Runnable examples

Four programs the ci helper compiles and runs on every pass, so the behaviour these pages describe cannot rot silently. Each prints SKIP: and exits 0 where the host lacks the capability.

ExampleBacksWhat it proves
color-derivation/examples/derive-palette.dcolor-derivationthe tone-delta rule misses its claimed 4.5:1 by 0.022; derived palettes clear WCAG AA
gnome/examples/portal-appearance.dGNOMEGNOME's accent is one of libadwaita's nine; reduced-motion answers NotFound on a live v2 portal
kde/examples/kdeglobals-appearance.dKDEColors:View and Colors:Window genuinely differ (#141618 vs #202326 in Breeze Dark)
terminal/examples/color-scheme-probe.dterminalmode 2031 and OSC 11 round-trip, and a silent terminal is handled by timeout, not a hang

Suggested reading paths

"I want the conclusions."comparison → its delta tablethe proposal.

"I'm implementing the Sparkles feature this informs."conceptslibadwaita (the abstraction to copy) → color-derivation (the math) → terminal (the first backend) → the proposal.

"I'm porting hue to a specific platform."concepts → that platform's deep-dive → comparison § what follows the system → the relevant proposal milestone.

"I only care about the terminal."terminalcolor-scheme-probe.dproposal § P1.

"I want to understand the color math."concepts § tonecolor-derivationderive-palette.d.

Sources

Primary sources are cited per deep-dive; each page's Sources section is its provenance. The recurring ones:

Live captures on a GNOME session with portal Settings v2, and pty-harness captures of a mode-2031 terminal, were taken in August 2026 and are reproduced by the examples above.