Disabled State Explanation
Status: ready
Intent
When the user can't continue, the app says why and what to do.
Problem
A disabled button hides the rule. A sighted user guesses from gray color and empty fields nearby. A VoiceOver user hears "dimmed" and has nothing to fix.
Solution
Verdict: for form validation, keep the button enabled and validate on tap. Disable it only when tapping would be unsafe or impossible, and then show the reason next to it and put it in the button's value.
Why: an enabled button turns "guess the rule" into a specific error with a fix. A disabled button is right when there's nothing the user can fix yet, for example while a card is being verified.
| Situation | Strategy |
|---|---|
| Form fields missing or invalid | Enabled, validate on tap, focus the first error |
| Card being verified, file uploading | Disabled, reason visible and in the value |
| User lacks permission | Disabled, reason visible, link to request access |
| Terms not accepted | Disabled, the checkbox is right above it |
Implementation
Enabled with validation:
Button("Continue") {
errors = form.validate()
if errors.isEmpty { submit() } else { errorFocused = true }
}
Error focus and field values: Focus Recovery.
Disabled with a reason:
Button("Pay $2.50") { pay() }
.disabled(isVerifyingCard)
.accessibilityValue(isVerifyingCard ? "Verifying card" : "")
if isVerifyingCard {
Text("Verifying card. This takes up to 10 seconds.")
}
The .notEnabled trait already says "dimmed". The value adds why.
UIKit:
payButton.isEnabled = !isVerifyingCard
payButton.accessibilityValue = isVerifyingCard ? "Verifying card" : nil
When a wait the user is aware of ends, one announcement is fine: "Card verified. Pay is available." Don't announce "enabled" and "disabled" as the user types.
Check
Profile: VoiceOver
Task: leave the email empty and press Continue
Expected: Continue can be pressed
focus moves to the email error
entered values stay
Profile: VoiceOver
Task: reach Pay while the card is verifying
Expected: the button reads its name, "Verifying card" and "dimmed"
the same reason is visible under it
Profile: Larger Text AX5, German
Task: the same screens
Expected: the reason wraps and stays next to the button
Pitfalls
- Gray color as the only explanation.
- The reason only in a tooltip.
- Clearing input after validation.
- Explaining the button and not marking the field.
- Hardcoded rules that are wrong for some users, like "16 digits" for a card. American Express has 15.
WCAG mapping
3.3.2 Labels or Instructions: requirements are stated.3.3.1 Error Identification: the blocked state is identified.3.3.3 Error Suggestion: the user gets a fix.1.4.1 Use of Color: gray isn't the only signal.