From e8e2c38aaae6406a7a7737bb26e5abb4bffb2895 Mon Sep 17 00:00:00 2001 From: austinried <4966622+austinried@users.noreply.github.com> Date: Wed, 23 Sep 2026 16:21:21 +0000 Subject: [PATCH] keep the accent blobs dimmer than the primary button --- .../com/subtracks/ui/theme/ArtworkTheme.kt | 18 ++++++++++++++-- .../com/subtracks/ui/AppScreenshotTest.kt | 21 +++++++++++++++++++ .../subtracks/ui/theme/ArtworkColorsTest.kt | 13 ++++++++++++ 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/com/subtracks/ui/theme/ArtworkTheme.kt b/app/src/main/kotlin/com/subtracks/ui/theme/ArtworkTheme.kt index 19b123cc..184f7672 100644 --- a/app/src/main/kotlin/com/subtracks/ui/theme/ArtworkTheme.kt +++ b/app/src/main/kotlin/com/subtracks/ui/theme/ArtworkTheme.kt @@ -35,7 +35,8 @@ private const val SEED_ART_SIZE_PX = 128 private const val PERIOD_SCREENS = 2f private const val BLOB_ZONE = 0.62f private const val BLOB_COUNT = 4 -private const val ACCENT_MAX_LUMINANCE = 0.34f +private const val ACCENT_MAX_LUMINANCE = 0.18f +private const val PRIMARY_MIN_LUMINANCE = 0.30f private const val MIN_GRADIENT_SATURATION = 0.30f private const val HERO_DARKEN_MAX = 1.0f private const val DARKEN_START_SCREENS = 0.5f @@ -66,7 +67,7 @@ fun artworkColorsFromSeeds( l: Float, ) = Color.hsl(h % 360f, sat.coerceIn(0f, 1f), l.coerceIn(0f, 1f)) - val primary = tone(hue, s, 0.62f) + val primary = tone(hue, s, 0.62f).withMinLuminance(PRIMARY_MIN_LUMINANCE) val background = tone(hue, (s * 0.35f).coerceAtMost(0.20f), 0.06f) val onBackground = tone(hue, (s * 0.10f).coerceAtMost(0.08f), 0.95f) @@ -138,6 +139,19 @@ private fun Color.withMaxLuminance(max: Float): Color { return color } +private fun Color.withMinLuminance(min: Float): Color { + if (luminance() >= min) return this + val (h, s, initial) = toHsl() + var lightness = initial + var color = this + var guard = 0 + while (color.luminance() < min && guard++ < 40) { + lightness = (lightness + 0.02f).coerceAtMost(0.95f) + color = Color.hsl(h, s, lightness) + } + return color +} + fun ArtworkColors.gradientColorAt(fraction: Float): Color { if (fraction.isNaN()) return gradientHigh val f = fraction.mod(1f) diff --git a/app/src/test/kotlin/com/subtracks/ui/AppScreenshotTest.kt b/app/src/test/kotlin/com/subtracks/ui/AppScreenshotTest.kt index c24ea035..9643ad8c 100644 --- a/app/src/test/kotlin/com/subtracks/ui/AppScreenshotTest.kt +++ b/app/src/test/kotlin/com/subtracks/ui/AppScreenshotTest.kt @@ -51,6 +51,7 @@ import com.subtracks.ui.settings.AddSourceState import com.subtracks.ui.settings.SettingsScreen import com.subtracks.ui.theme.SubtracksTheme import com.subtracks.ui.theme.artworkColorsFromSeed +import com.subtracks.ui.theme.artworkColorsFromSeeds import kotlinx.coroutines.flow.flowOf import org.junit.After import org.junit.Before @@ -133,6 +134,26 @@ class AppScreenshotTest { composeRule.onRoot().captureRoboImage("src/test/screenshots/album_detail.png") } + @Test + fun albumDetailCool() { + composeRule.setContent { + SubtracksTheme { + AlbumDetailScreen( + album = Fixtures.albums.first(), + songs = Fixtures.songs, + coverArt = { id, _ -> id?.let { CoverArtRef(it, "test:$it") } }, + artwork = artworkColorsFromSeeds(Color.rgb(45, 115, 240), Color.rgb(40, 180, 140)), + onBack = {}, + onSongClick = {}, + playingSongId = Fixtures.songs.first().id, + modifier = Modifier.fillMaxSize(), + ) + } + } + awaitText("Everything In Its Right Place") + composeRule.onRoot().captureRoboImage("src/test/screenshots/album_detail_cool.png") + } + @Test fun artistDetail() { composeRule.setContent { diff --git a/app/src/test/kotlin/com/subtracks/ui/theme/ArtworkColorsTest.kt b/app/src/test/kotlin/com/subtracks/ui/theme/ArtworkColorsTest.kt index 677bef1a..b5caec22 100644 --- a/app/src/test/kotlin/com/subtracks/ui/theme/ArtworkColorsTest.kt +++ b/app/src/test/kotlin/com/subtracks/ui/theme/ArtworkColorsTest.kt @@ -35,6 +35,19 @@ class ArtworkColorsTest { ) } + @Test + fun buttonStandsOutFromGradient() { + for (seed in seeds) { + val colors = artworkColorsFromSeed(seed) + val primary = colors.scheme.primary + assertTrue("primary vs gradientHigh for $seed", contrast(primary, colors.gradientHigh) >= 2f) + assertTrue("primary vs gradientLow for $seed", contrast(primary, colors.gradientLow) >= 2f) + for (accent in colors.accents) { + assertTrue("accent dimmer than primary for $seed", primary.luminance() > accent.luminance()) + } + } + } + private fun contrast( a: Color, b: Color,