🎨 Palette: Improve accessibility of MissingScopeCard expand/collapse action (#395)

Added dynamic onClickLabel to MissingScopeCard to indicate whether clicking will Expand or Collapse the card content.

Expandable cards using Jetpack Compose `Card(onClick=)` don't inform screen readers of their state change action, which creates a confusing experience for accessibility users.

Visuals remain unchanged. Screen readers now announce "Expand" or "Collapse" dynamically instead of a generic button action.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
This commit is contained in:
Yu-ga
2026-03-28 00:55:59 +09:00
committed by GitHub
co-authored by google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
parent e2b17c5c22
commit e6a111a85c
2 changed files with 16 additions and 1 deletions
+4
View File
@@ -13,3 +13,7 @@
## 2025-02-13 - Redundant Screen Reader Announcements on Icons
**Learning:** Adding a `contentDescription` to an `Icon` when the adjacent `Text` provides the exact same descriptive string causes screen readers (like TalkBack) to announce the action twice (e.g., "Scan QR Code, Scan QR Code").
**Action:** When an `Icon` is used alongside descriptive text within a clickable area, set the `Icon`'s `contentDescription` to `null` so the screen reader only reads the text once.
## 2025-03-27 - Expand/Collapse Accessibility Pattern for MissingScopeCard
**Learning:** Using `Card(onClick=)` without an `onClickLabel` leads to generic, unhelpful screen reader announcements. Expandable cards require explicit labels that change based on state (e.g. Expand / Collapse).
**Action:** When converting `Card(onClick=)` to use `Modifier.clickable()`, use `onClickLabel = stringResource(if (expanded) R.string.action_collapse else R.string.action_expand)` and assign `role = Role.Button` so the action changes dynamically for screen readers.
@@ -49,6 +49,10 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.onClick
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.role
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
@@ -1265,8 +1269,15 @@ fun MissingScopeCard(error: String, onClick: () -> Unit) {
var expanded by rememberSaveable { mutableStateOf(false) }
val context = LocalContext.current
val onClickLabel = stringResource(if (expanded) R.string.action_collapse else R.string.action_expand)
Card(
modifier = Modifier.fillMaxWidth(),
modifier = Modifier
.fillMaxWidth()
.semantics(mergeDescendants = true) {
onClick(label = onClickLabel, action = null)
role = Role.Button
},
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.errorContainer),
onClick = { expanded = !expanded }
) {