diff --git a/LESSONS.md b/LESSONS.md index 8845ecb..bc115bf 100644 --- a/LESSONS.md +++ b/LESSONS.md @@ -1269,3 +1269,27 @@ every mode, and `outline` is already WCAG-lifted, so the high-contrast toggle reaches them too. Rule of thumb: if a control's fill is a near-surface tint, its legibility must come from a border or the saturated tone, never from the fill alone (this is the same "communicate via shape, not colour alone" rule the a11y notes state). + +--- + +## 56. Never give an elevated Card a translucent container colour: the shadow ring shows through + +Done tasks and done/archived memos were tinted with +`surfaceVariant.copy(alpha = 0.6f)` on a `Card` that still carried its 1dp light-mode +elevation. Every active card looked fine; every muted card grew a thick grey border +around a paler, smaller-radius inner rectangle, which nobody had drawn. + +The border is the platform shadow. HWUI renders an elevated node's shadow as a ring +(penumbra out to the umbra edge) and leaves the umbra unfilled, on the assumption +that the caster covers it. With an opaque fill that holds. With a 60% fill the ring +shows through around the edge, while the unfilled umbra in the middle shows only the +pale blend, so the card looks like a grey frame around a lighter inset panel. The +inset roughly matched the content padding, which made it look like a deliberate +(bad) inner border rather than a rendering artefact. + +Fix (`ui/theme/CardColors.kt`, `mutedCardContainer()`): keep the design intent but +flatten the blend into an opaque colour with `compositeOver(colorScheme.background)`. +Rule: any `Surface`/`Card` with `shadowElevation > 0` gets an opaque container. If a +translucent look is genuinely wanted, drop the elevation to 0dp instead of relying on +the fill to hide the shadow. Dark schemes here run at 0dp, which is why the bug only +showed in light mode. diff --git a/app/src/main/java/com/mapgie/dash/ui/components/ReminderCard.kt b/app/src/main/java/com/mapgie/dash/ui/components/ReminderCard.kt index 6fb8e11..4154f1f 100644 --- a/app/src/main/java/com/mapgie/dash/ui/components/ReminderCard.kt +++ b/app/src/main/java/com/mapgie/dash/ui/components/ReminderCard.kt @@ -40,6 +40,7 @@ import com.mapgie.dash.ui.theme.StatusTone import com.mapgie.dash.ui.theme.badgeContainerColor import com.mapgie.dash.ui.theme.barColor import com.mapgie.dash.ui.theme.isDarkScheme +import com.mapgie.dash.ui.theme.mutedCardContainer import com.mapgie.dash.ui.theme.spineColor import com.mapgie.dash.ui.theme.statusTone import com.mapgie.dash.ui.theme.textColor @@ -95,10 +96,8 @@ fun ReminderCard( .fillMaxWidth() .padding(horizontal = inset), colors = CardDefaults.cardColors( - containerColor = if (muted) - MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.6f) - else - MaterialTheme.colorScheme.surfaceVariant + containerColor = if (muted) mutedCardContainer() + else MaterialTheme.colorScheme.surfaceVariant ), elevation = CardDefaults.cardElevation(defaultElevation = if (dark) 0.dp else 1.dp) ) { diff --git a/app/src/main/java/com/mapgie/dash/ui/components/TaskCard.kt b/app/src/main/java/com/mapgie/dash/ui/components/TaskCard.kt index eaf3630..4eebe0e 100644 --- a/app/src/main/java/com/mapgie/dash/ui/components/TaskCard.kt +++ b/app/src/main/java/com/mapgie/dash/ui/components/TaskCard.kt @@ -44,6 +44,7 @@ import com.mapgie.dash.ui.theme.StatusTone import com.mapgie.dash.ui.theme.badgeContainerColor import com.mapgie.dash.ui.theme.barColor import com.mapgie.dash.ui.theme.isDarkScheme +import com.mapgie.dash.ui.theme.mutedCardContainer import com.mapgie.dash.ui.theme.statusTone import com.mapgie.dash.ui.theme.textColor import java.time.Instant @@ -97,7 +98,7 @@ fun TaskCard( colors = CardDefaults.cardColors( containerColor = when { zenMode -> MaterialTheme.colorScheme.surfaceContainerLow - isDone -> MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.6f) + isDone -> mutedCardContainer() else -> MaterialTheme.colorScheme.surfaceVariant } ), diff --git a/app/src/main/java/com/mapgie/dash/ui/theme/CardColors.kt b/app/src/main/java/com/mapgie/dash/ui/theme/CardColors.kt new file mode 100644 index 0000000..2fbe50d --- /dev/null +++ b/app/src/main/java/com/mapgie/dash/ui/theme/CardColors.kt @@ -0,0 +1,23 @@ +package com.mapgie.dash.ui.theme + +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.compositeOver + +/** + * Fill for a muted list card (a done task, a done or archived memo): 60% of + * `surfaceVariant` flattened onto the page background so the card reads as faded + * next to its active siblings. + * + * The blend is composited into an opaque colour on purpose. A translucent + * `containerColor` on an elevated `Card` lets the platform shadow show through + * the fill, and because the shadow is drawn as a ring with an unfilled umbra + * the card picks up a thick grey border around a paler inner rectangle. See + * `LESSONS.md` #56. + */ +@Composable +fun mutedCardContainer(): Color = + MaterialTheme.colorScheme.surfaceVariant + .copy(alpha = 0.6f) + .compositeOver(MaterialTheme.colorScheme.background) diff --git a/changelog/unreleased/muted-card-border.json b/changelog/unreleased/muted-card-border.json new file mode 100644 index 0000000..8c2eff6 --- /dev/null +++ b/changelog/unreleased/muted-card-border.json @@ -0,0 +1,4 @@ +{ + "bump": "patch", + "fixed": ["Done tasks and done or archived memos no longer show a thick grey border around a paler inner rectangle; the faded card fill is now a solid colour."] +}