fix(settings): the paired "Connected with" card rendered muddy and unpolished

Two defects, both only in the paired state — the unpaired state was already
styled correctly, which is why this hid.

1. The card was see-through. `partnerCardColor` used primaryContainer at 52%
   alpha, and the page behind it is a gradient brush, so the gradient bled up
   through the card: it read muddy against the crisp profile card directly above
   (which is 96%), and the card's own content bounds showed as a paler,
   sharp-edged band inside it — measured, not guessed: a 24-unit jump
   (218,209,226 -> 242,232,251 -> 217,208,225) across the Row's 18dp content
   inset. Now 96% like its sibling; the same scanline is flat (240,229,253 ->
   241,231,255). Content colours are the container's own on* pair rather than
   the page's ink, so contrast is correct in both themes.

2. The heart was a bare, outsized glyph. ProfileAvatar's no-photo fallback is a
   40dp Icon with no container, so the paired row showed a naked heart unlike
   anything else on the page — while the *unpaired* branch right below it, and
   every settings row, use a 48dp tile + 24dp glyph. The avatar is now used only
   when a real partner photo exists; otherwise it falls back to that same tile.

Verified live both fixtures (dark 5554 / light 5556) with pixel probes before
and after. theme-scan REVIEW 25 -> 24 (one hardcoded-colour hit removed);
CRITICAL/MAJOR unchanged. Unit tests green, 0 FATAL.

Filed alongside this round's Pass C results (R32) in ClaudeReport/ClaudeQACoverage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
null 2026-07-14 22:29:05 -05:00
parent eb254d9a9c
commit 76d8746a9b
1 changed files with 22 additions and 10 deletions

View File

@ -355,17 +355,24 @@ fun SettingsScreen(
}
}
// Partner card — fully tappable, visually obvious
// Partner card — fully tappable, and deliberately tinted so it reads as the one
// relationship row rather than another settings row.
// The tint must be a near-opaque container: the page behind is a gradient brush, so
// a see-through card (this was `primaryContainer` at 52%) lets that gradient bleed
// up and the card renders muddy — and its own inner content bounds show as a paler
// band against it. The sibling profile card directly above is 96% and never does
// this. Content colours are the container's `on*` pair so contrast is correct in
// both themes rather than borrowed from the page's ink.
val partnerCardColor = if (state.isPaired) {
SettingsSoft.copy(alpha = 0.52f)
MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.96f)
} else if (isCloserDarkTheme()) {
MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.74f)
MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.96f)
} else {
MaterialTheme.colorScheme.secondaryContainer.copy(alpha = 0.46f)
MaterialTheme.colorScheme.secondaryContainer.copy(alpha = 0.96f)
}
val partnerAccent = if (state.isPaired) SettingsPrimaryDeep else MaterialTheme.colorScheme.secondary
val partnerContentColor = if (state.isPaired) SettingsInk else MaterialTheme.colorScheme.onSecondaryContainer
val partnerMutedColor = if (state.isPaired) SettingsMuted else MaterialTheme.colorScheme.onSecondaryContainer.copy(alpha = 0.72f)
val partnerAccent = if (state.isPaired) MaterialTheme.colorScheme.onPrimaryContainer else MaterialTheme.colorScheme.secondary
val partnerContentColor = if (state.isPaired) MaterialTheme.colorScheme.onPrimaryContainer else MaterialTheme.colorScheme.onSecondaryContainer
val partnerMutedColor = if (state.isPaired) MaterialTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.72f) else MaterialTheme.colorScheme.onSecondaryContainer.copy(alpha = 0.72f)
Card(
onClick = {
onNavigate(
@ -386,11 +393,16 @@ fun SettingsScreen(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
if (state.isPaired) {
// A real partner photo shows as the avatar; otherwise fall back to the same
// 48dp tile + 24dp glyph the unpaired state and every settings row use.
// ProfileAvatar's own fallback is a bare 40dp icon with no container, which
// left the paired state showing an outsized naked heart unlike anything
// else on the page.
if (state.isPaired && !state.partnerPhotoUrl.isNullOrBlank()) {
ProfileAvatar(
imageUrl = state.partnerPhotoUrl,
fallbackIcon = CloserGlyphs.Heart,
fallbackTint = SettingsPrimaryDeep
fallbackTint = partnerAccent
)
} else {
Box(
@ -401,7 +413,7 @@ fun SettingsScreen(
contentAlignment = Alignment.Center
) {
Icon(
CloserGlyphs.HeartOutline,
if (state.isPaired) CloserGlyphs.Heart else CloserGlyphs.HeartOutline,
contentDescription = null,
tint = partnerAccent,
modifier = Modifier.size(24.dp)