diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts
index a72f675f..99794847 100644
--- a/composeApp/build.gradle.kts
+++ b/composeApp/build.gradle.kts
@@ -366,6 +366,7 @@ kotlin {
implementation(libs.nucleus.decorated.window.tao)
implementation(libs.compose.native.tray)
implementation(libs.nucleus.media.control)
+ implementation(libs.nucleus.darkmode.detector)
}
}
}
diff --git a/composeApp/src/commonMain/kotlin/org/spotube/spotube/core/ui/theming/AppTheme.kt b/composeApp/src/commonMain/kotlin/org/spotube/spotube/core/ui/theming/AppTheme.kt
index 6720efb9..b283451e 100644
--- a/composeApp/src/commonMain/kotlin/org/spotube/spotube/core/ui/theming/AppTheme.kt
+++ b/composeApp/src/commonMain/kotlin/org/spotube/spotube/core/ui/theming/AppTheme.kt
@@ -17,7 +17,6 @@
package org.spotube.spotube.core.ui.theming
-import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
@@ -205,7 +204,7 @@ fun SpotubeTheme(
val isDarkTheme = when (settings.theme) {
Theme.LIGHT -> false
Theme.DARK -> true
- Theme.SYSTEM -> isSystemInDarkTheme()
+ Theme.SYSTEM -> isSystemDarkTheme()
}
val palette: MillenniumPalette = settings.palette
diff --git a/composeApp/src/commonMain/kotlin/org/spotube/spotube/core/ui/theming/SystemDarkTheme.kt b/composeApp/src/commonMain/kotlin/org/spotube/spotube/core/ui/theming/SystemDarkTheme.kt
new file mode 100644
index 00000000..3efa4474
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/org/spotube/spotube/core/ui/theming/SystemDarkTheme.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.spotube.spotube.core.ui.theming
+
+import androidx.compose.runtime.Composable
+
+/** Whether the OS is in dark mode, observed live; desktop can't rely on Compose's own check under Tao. */
+@Composable
+expect fun isSystemDarkTheme(): Boolean
diff --git a/composeApp/src/jvmMain/kotlin/org/spotube/spotube/core/ui/theming/SystemDarkTheme.jvm.kt b/composeApp/src/jvmMain/kotlin/org/spotube/spotube/core/ui/theming/SystemDarkTheme.jvm.kt
new file mode 100644
index 00000000..eae0a660
--- /dev/null
+++ b/composeApp/src/jvmMain/kotlin/org/spotube/spotube/core/ui/theming/SystemDarkTheme.jvm.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.spotube.spotube.core.ui.theming
+
+import androidx.compose.runtime.Composable
+import dev.nucleusframework.darkmodedetector.isSystemInDarkMode
+
+// The Tao window never feeds Compose's LocalSystemTheme, so ask the OS directly
+@Composable
+actual fun isSystemDarkTheme(): Boolean = isSystemInDarkMode()
diff --git a/composeApp/src/mobileMain/kotlin/org/spotube/spotube/core/ui/theming/SystemDarkTheme.mobile.kt b/composeApp/src/mobileMain/kotlin/org/spotube/spotube/core/ui/theming/SystemDarkTheme.mobile.kt
new file mode 100644
index 00000000..047248e3
--- /dev/null
+++ b/composeApp/src/mobileMain/kotlin/org/spotube/spotube/core/ui/theming/SystemDarkTheme.mobile.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.spotube.spotube.core.ui.theming
+
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.runtime.Composable
+
+@Composable
+actual fun isSystemDarkTheme(): Boolean = isSystemInDarkTheme()
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index c167d59c..ccc34f1f 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -134,6 +134,7 @@ newpipeextractor = { module = "com.github.teamnewpipe:NewPipeExtractor", version
nucleus-core-runtime = { module = "dev.nucleusframework:nucleus.core-runtime", version.ref = "nucleusNucleusApplication" }
nucleus-decorated-window-tao = { module = "dev.nucleusframework:nucleus.decorated-window-tao", version.ref = "nucleusNucleusApplication" }
nucleus-media-control = { module = "dev.nucleusframework:nucleus.media-control", version.ref = "nucleusNucleusApplication" }
+nucleus-darkmode-detector = { module = "dev.nucleusframework:nucleus.darkmode-detector", version.ref = "nucleusNucleusApplication" }
nucleus-graalvm-runtime = { module = "dev.nucleusframework:nucleus.graalvm-runtime", version.ref = "nucleusNucleusApplication" }
nucleus-nucleus-application = { module = "dev.nucleusframework:nucleus.nucleus-application", version.ref = "nucleusNucleusApplication" }
semver = { module = "net.swiftzer.semver:semver", version.ref = "semver" }