Right Sized Grouping
Status: ready
Intent
Group what belongs together. Don't bury controls the user needs to reach directly.
Problem
Too little grouping is Label Soup. Too much is Over Grouped Control. Both come from grouping by how the layout is nested in code instead of by task.
Solution
Verdict: static information about one object is one element. Frequent controls stay separate. Rare secondary actions can be accessibility actions. Editable content always stays separate.
Why: the user hears less noise and still reaches every control the task needs.
| UI piece | Group? | Why |
|---|---|---|
| Decorative image + title | Yes, hide the image | The image isn't part of the task |
| Title + status + amount | Yes | One object summary |
| Favorite button on every card | Maybe | Custom action, or a button with the object name |
| Add to cart | No | Primary action |
| Text fields | No | Each one is edited separately |
| Payment method options | No | Selection needs its own role and state |
Implementation
Summary + primary action:
VStack(alignment: .leading) {
ProductSummary(tea)
.accessibilityElement(children: .combine)
Button("Add to cart") { add(tea) }
.accessibilityLabel("Add \(tea.name) to cart")
.accessibilityInputLabels(["Add to cart", "Add \(tea.name) to cart"])
}
Summary + secondary action, one stop:
AppointmentRow(appointment)
.accessibilityElement(children: .ignore)
.accessibilityLabel(appointment.title)
.accessibilityValue(appointment.timeText)
.accessibilityAction(named: "Cancel appointment") { confirmCancel(appointment) }
Form fields are never grouped:
// Bad: one element, nothing can be edited
VStack {
TextField("Card number", text: $card)
TextField("Expiration date", text: $date)
TextField("Security code", text: $cvc)
}
.accessibilityElement(children: .combine)
// Good: three fields
TextField("Card number", text: $card)
TextField("Expiration date", text: $date)
TextField("Security code", text: $cvc)
Check
Profile: VoiceOver
Task: complete the row's task
Expected: the summary is heard once
the primary action is reachable
every editable or selectable control is its own stop
Profile: Switch Control
Task: scan the list and activate one item
Expected: fewer stops than before grouping
required controls are still separate targets
Count stops before and after, and complete the task after. A lower count alone doesn't prove the grouping is right.
Pitfalls
- Combining editable fields.
- A destructive action inside a vague custom action.
- Grouping to make up for a confusing visual layout.
- A custom action as the only way to complete checkout.
WCAG mapping
1.3.1 Info and Relationships: related content is exposed as related.2.4.3 Focus Order: focus order matches the task.4.1.2 Name, Role, Value: controls keep their role and state.