Skip to content

System Color

The system-color module provides reactive detection of the OS accent color and high contrast mode. It uses native JNI bridges on each platform to register OS-level listeners that trigger recomposition the instant a setting changes — no polling, no restart required.

Installation

dependencies {
    implementation("io.github.kdroidfilter:nucleus.system-color:<version>")
}

Usage

Accent Color

@Composable
fun App() {
    val accentColor = systemAccentColor()

    MaterialTheme(
        colorScheme = if (accentColor != null) {
            // Build a dynamic color scheme from the system accent
            darkColorScheme(primary = accentColor)
        } else {
            darkColorScheme()
        }
    ) {
        // UI automatically recomposes when the accent color changes
    }
}

Check Support

Use isSystemAccentColorSupported() to check platform support before entering a composable context — useful for feature gating or conditional UI:

fun main() = application {
    if (isSystemAccentColorSupported()) {
        // Platform supports accent color — safe to use systemAccentColor()
    }
}

High Contrast

@Composable
fun App() {
    val highContrast = isSystemInHighContrast()

    if (highContrast) {
        // Use high contrast colors / larger borders
    }
}

Material You on Desktop

Android's Material You dynamic theming derives an entire color scheme from a single seed color — the system accent color. On desktop, this is not supported out of the box, but you can reproduce the same effect by combining systemAccentColor() with the material-kolor library.

dependencies {
    implementation("io.github.kdroidfilter:nucleus.system-color:<version>")
    implementation("com.materialkolor:material-kolor:4.1.1")
}
@Composable
fun App() {
    val accentColor = systemAccentColor()
    val seedColor = accentColor ?: Color(0xFF6750A4) // Material default purple

    DynamicMaterialTheme(
        seedColor = seedColor,
        isDark = isSystemInDarkTheme(),
        animate = true,
        style = PaletteStyle.TonalSpot,
    ) {
        // Your app content — the entire color scheme adapts
        // to the OS accent color in real time
    }
}

When the user changes their accent color in system settings, systemAccentColor() triggers a recomposition and DynamicMaterialTheme smoothly animates the entire palette to the new color.

The Nucleus example app uses this exact approach — you can test it by running:

./gradlew :example:run

API Reference

Function Returns Description
systemAccentColor() Color? Composable. Returns the current system accent color, or null if unsupported. Recomposes on change.
isSystemInHighContrast() Boolean Composable. Returns true if the OS is in high contrast / increased contrast mode. Recomposes on change.
isSystemAccentColorSupported() Boolean Non-composable. Returns whether the current platform supports accent color detection.

Platform Detection Methods

Platform Accent Color High Contrast Reactive
macOS NSColor.controlAccentColor (macOS 10.14+) accessibilityDisplayShouldIncreaseContrast Yes — NSSystemColorsDidChangeNotification / NSWorkspaceAccessibilityDisplayOptionsDidChangeNotification
Windows HKCU\SOFTWARE\Microsoft\Windows\DWM\AccentColor registry key (AABBGGRR) SystemParametersInfoW(SPI_GETHIGHCONTRAST) Yes — RegNotifyChangeKeyValue on background thread
Linux XDG Desktop Portal org.freedesktop.appearance / accent-color — RGB tuple (0.0–1.0) XDG Desktop Portal org.freedesktop.appearance / contrast — uint32 (1 = high) Yes — D-Bus SettingChanged signal listener

All three platforms use JNI native libraries bundled inside the JAR. The library is extracted and loaded at runtime automatically.

Native Libraries

The module ships pre-built native binaries for:

  • macOS: libnucleus_systemcolor.dylib (arm64 + x64)
  • Windows: nucleus_systemcolor.dll (x64 + ARM64)
  • Linux: libnucleus_systemcolor.so (x64 + aarch64)

On Linux, libdbus-1 must be present at runtime (installed by default on all major desktop distributions).

Linux Desktop Environment Support

The Linux implementation uses the XDG Desktop Portal D-Bus interface, which provides a desktop-agnostic abstraction:

  • GNOME 47+ — Full accent color and contrast support
  • KDE Plasma 6+ — Accent color support
  • elementary OS — Accent color support
  • Other DEs — Works if the desktop portal implements org.freedesktop.appearance

If the portal is not available or the setting is not configured, systemAccentColor() returns null and isSystemInHighContrast() returns false.

ProGuard

When ProGuard is enabled, the native bridge classes must be preserved. The Nucleus Gradle plugin includes these rules automatically, but if you need to add them manually:

# macOS
-keep class io.github.kdroidfilter.nucleus.systemcolor.mac.NativeMacSystemColorBridge {
    native <methods>;
    static void onAccentColorChanged(float, float, float);
    static void onContrastChanged(boolean);
}

# Windows
-keep class io.github.kdroidfilter.nucleus.systemcolor.windows.NativeWindowsSystemColorBridge {
    native <methods>;
    static void onAccentColorChanged(int, int, int);
    static void onHighContrastChanged(boolean);
}

# Linux
-keep class io.github.kdroidfilter.nucleus.systemcolor.linux.NativeLinuxSystemColorBridge {
    native <methods>;
    static void onAccentColorChanged(float, float, float);
    static void onHighContrastChanged(boolean);
}

-keep class io.github.kdroidfilter.nucleus.systemcolor.** { *; }

Logging

Debug messages are logged under the tags MacSystemColorDetector, WindowsSystemColor, and LinuxSystemColorDetector. Logging is off by default. To enable it:

import io.github.kdroidfilter.nucleus.core.runtime.tools.allowNucleusRuntimeLogging

allowNucleusRuntimeLogging = true