Labels
Labels
Every form input needs a visible or accessible label.
Explicit Labels
// ✅ Best: Explicit label
<label htmlFor="email">Email</label>
<input id="email" type="email" />
// React
function EmailField() {
return (
<div>
<label htmlFor="email">Email</label>
<input id="email" type="email" name="email" />
</div>
);
}
Implicit Labels
// ✅ Good: Implicit label
<label>
Email
<input type="email" />
</label>
aria-label
// When visible label isn't possible
<input type="search" aria-label="Search products" />
<button aria-label="Close dialog">×</button>
aria-labelledby
// Reference visible text
<h2 id="billing-title">Billing Address</h2>
<form aria-labelledby="billing-title">
{/* Form fields */}
</form>
// Multiple references
<span id="label">Name</span>
<span id="hint">First and last name</span>
<input
aria-labelledby="label hint"
aria-describedby="hint"
/>
Placeholder is NOT a Label
// ❌ Bad: Placeholder as label
<input placeholder="Email" />
// ✅ Good: Label + placeholder
<label htmlFor="email">Email</label>
<input id="email" placeholder="you@example.com" />
Input Types
// Use correct input types
<input type="email" /> {/* Shows email keyboard */}
<input type="tel" /> {/* Shows phone keyboard */}
<input type="url" /> {/* Shows URL keyboard */}
<input type="number" /> {/* Shows number keyboard */}
Error Messages
Error Messages
Accessible error announcement and display.
Error Pattern
function FormField({ label, error, id }) {
const errorId = `${id}-error`;
const hintId = `${id}-hint`;
return (
<div>
<label htmlFor={id}>{label}</label>
<input
id={id}
aria-invalid={!!error}
aria-describedby={error ? errorId : hintId}
/>
<span id={hintId} className="hint">Help text</span>
{error && (
<span id={errorId} className="error" role="alert">
{error}
</span>
)}
</div>
);
}
Error Summary
function Form({ errors, onSubmit }) {
return (
<form onSubmit={onSubmit}>
{/* Error summary */}
{Object.keys(errors).length > 0 && (
<div role="alert" className="error-summary">
<h2>Please fix the following errors:</h2>
<ul>
{Object.entries(errors).map(([field, error]) => (
<li key={field}>
<a href={`#${field}`}>{error}</a>
</li>
))}
</ul>
</div>
)}
{/* Form fields */}
</form>
);
}
Live Error Announcements
function Form() {
const [error, setError] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
try {
await submitForm();
} catch (err) {
setError('Form submission failed. Please try again.');
}
};
return (
<form onSubmit={handleSubmit}>
{/* Visible errors */}
{error && <div className="error" role="alert">{error}</div>}
{/* Screen reader only errors */}
<div aria-live="assertive" className="sr-only">
{error}
</div>
</form>
);
}
},
{
"id": "ch3",
"title": "ARIA Describedby",
"content": "## ARIA Describedby
Provide additional context for form inputs.
Help Text
function PasswordField() {
return (
<div>
<label htmlFor="password">Password</label>
<input
id="password"
type="password"
aria-describedby="password-hint"
/>
<span id="password-hint" className="hint">
Must be at least 8 characters with one uppercase letter
</span>
</div>
);
}
Character Count
function TextareaField({ maxLength }) {
const [count, setCount] = useState(0);
return (
<div>
<label htmlFor="bio">Bio</label>
<textarea
id="bio"
maxLength={maxLength}
aria-describedby="bio-count"
onChange={(e) => setCount(e.target.value.length)}
/>
<span id="bio-count">
{count} of {maxLength} characters
</span>
</div>
);
}
Multiple Descriptions
function CreditCardField() {
return (
<div>
<label htmlFor="cc">Credit Card</label>
<input
id="cc"
type="text"
aria-describedby="cc-hint cc-format"
/>
<span id="cc-hint" className="hint">
Enter your 16-digit card number
</span>
<span id="cc-format" className="format">
Format: XXXX-XXXX-XXXX-XXXX
</span>
</div>
);
}
Conditional Descriptions
function FormField({ label, error, hint, id }) {
return (
<div>
<label htmlFor={id}>{label}</label>
<input
id={id}
aria-invalid={!!error}
aria-describedby={
[error && `${id}-error`, hint && `${id}-hint`]
.filter(Boolean)
.join(' ')
}
/>
{hint && <span id={`${id}-hint`} className="hint">{hint}</span>}
{error && (
<span id={`${id}-error`} className="error">
{error}
</span>
)}
</div>
);
}
Form Validation Checklist
- All inputs have labels
- Required fields marked
- Errors announced to screen readers
- Help text provided
- Error summary at top of form
- Focus moves to error on submit
Practice Problems
Create a reusable React component implementing Accessible Forms. Include proper state management and accessibility.
Solution
// Production-ready component with:
// - Proper TypeScript types
// - Accessibility (ARIA)
// - Error boundaries
// - Loading states
// - Memoization where neededWrite unit and integration tests for Accessible Forms using React Testing Library.
Solution
// Test coverage:
// 1. Rendering tests
// 2. Interaction tests
// 3. Edge case tests
// 4. Accessibility testsOptimize Accessible Forms for performance. Consider memoization, code splitting, and bundle size.
Solution
// Optimization techniques:
// 1. React.memo / useMemo / useCallback
// 2. Code splitting with lazy()
// 3. Virtual scrolling for lists
// 4. Image lazy loading
// 5. Bundle analysisQuiz
1. Why not use placeholder as a label?
2. What does aria-describedby do?
3. What is the primary purpose of Accessible Forms?
4. What is a common mistake when implementing Accessible Forms?
Flashcards
Question
Why are form labels important?
Click to reveal answer
Answer
They identify the purpose of inputs for screen readers and all users.
Question
How do you associate labels with inputs?
Click to reveal answer
Answer
Using htmlFor/id attributes, wrapping input in label, or aria-label.
Question
What is aria-describedby?
Click to reveal answer
Answer
An attribute that references elements providing additional help text for an input.
Question
How should form errors be displayed?
Click to reveal answer
Answer
Visible error messages near inputs, announced to screen readers, with error summary at top.
Question
What is Accessible Forms?
Click to reveal answer
Answer
Accessible Forms is a key concept in frontend development.
Revision Notes
Key Takeaways
- 1.Every input needs a label (explicit, implicit, or aria-label)
- 2.Errors should be announced to screen readers
- 3.Use aria-describedby for help text and hints
- 4.Provide error summary at top of form
- 5.Never use placeholder as the only label
Interview Tips
- •Explain the importance of form labels
- •Discuss how to announce form errors
- •Know how to use aria-describedby
Cheat Sheet
Accessible Forms Cheat Sheet
Labels
- Explicit:
- Implicit:
- aria-label when visible not possible
Errors
- role="alert" for announcements
- aria-invalid="true" on inputs
- aria-describedby for error text
Help Text
- aria-describedby for hints
- Multiple descriptions with space-separated IDs
Checklist
- All inputs have labels
- Errors announced
- Help text provided
- Error summary at top