Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions LESSONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
) {
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/java/com/mapgie/dash/ui/components/TaskCard.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
),
Expand Down
23 changes: 23 additions & 0 deletions app/src/main/java/com/mapgie/dash/ui/theme/CardColors.kt
Original file line number Diff line number Diff line change
@@ -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)
4 changes: 4 additions & 0 deletions changelog/unreleased/muted-card-border.json
Original file line number Diff line number Diff line change
@@ -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."]
}
Loading