1---
2name: harden
3description: Make interfaces production-ready: error handling, empty states, onboarding flows, i18n, text overflow, and edge case management. Use when the user asks to harden, make production-ready, handle edge cases, add error states, design empty states, improve onboarding, or fix overflow and i18n issues.
4version: 2.1.1
5---
6
7Strengthen interfaces against edge cases, errors, internationalization issues, and real-world usage scenarios that break idealized designs.
8
9## Assess Hardening Needs
10
11Identify weaknesses and edge cases:
12
131. **Test with extreme inputs**:
14 - Very long text (names, descriptions, titles)
15 - Very short text (empty, single character)
16 - Special characters (emoji, RTL text, accents)
17 - Large numbers (millions, billions)
18 - Many items (1000+ list items, 50+ options)
19 - No data (empty states)
20
212. **Test error scenarios**:
22 - Network failures (offline, slow, timeout)
23 - API errors (400, 401, 403, 404, 500)
24 - Validation errors
25 - Permission errors
26 - Rate limiting
27 - Concurrent operations
28
293. **Test internationalization**:
30 - Long translations (German is often 30% longer than English)
31 - RTL languages (Arabic, Hebrew)
32 - Character sets (Chinese, Japanese, Korean, emoji)
33 - Date/time formats
34 - Number formats (1,000 vs 1.000)
35 - Currency symbols
36
37**CRITICAL**: Designs that only work with perfect data aren't production-ready. Harden against reality.
38
39## Hardening Dimensions
40
41Systematically improve resilience:
42
43### Text Overflow & Wrapping
44
45**Long text handling**:
46```css
47/* Single line with ellipsis */
48.truncate {
49 overflow: hidden;
50 text-overflow: ellipsis;
51 white-space: nowrap;
52}
53
54/* Multi-line with clamp */
55.line-clamp {
56 display: -webkit-box;
57 -webkit-line-clamp: 3;
58 -webkit-box-orient: vertical;
59 overflow: hidden;
60}
61
62/* Allow wrapping */
63.wrap {
64 word-wrap: break-word;
65 overflow-wrap: break-word;
66 hyphens: auto;
67}
68```
69
70**Flex/Grid overflow**:
71```css
72/* Prevent flex items from overflowing */
73.flex-item {
74 min-width: 0; /* Allow shrinking below content size */
75 overflow: hidden;
76}
77
78/* Prevent grid items from overflowing */
79.grid-item {
80 min-width: 0;
81 min-height: 0;
82}
83```
84
85**Responsive text sizing**:
86- Use `clamp()` for fluid typography
87- Set minimum readable sizes (14px on mobile)
88- Test text scaling (zoom to 200%)
89- Ensure containers expand with text
90
91### Internationalization (i18n)
92
93**Text expansion**:
94- Add 30-40% space budget for translations
95- Use flexbox/grid that adapts to content
96- Test with longest language (usually German)
97- Avoid fixed widths on text containers
98
99```jsx
100// ❌ Bad: Assumes short English text
101<button className="w-24">Submit</button>
102
103// ✅ Good: Adapts to content
104<button className="px-4 py-2">Submit</button>
105```
106
107**RTL (Right-to-Left) support**:
108```css
109/* Use logical properties */
110margin-inline-start: 1rem; /* Not margin-left */
111padding-inline: 1rem; /* Not padding-left/right */
112border-inline-end: 1px solid; /* Not border-right */
113
114/* Or use dir attribute */
115[dir="rtl"] .arrow { transform: scaleX(-1); }
116```
117
118**Character set support**:
119- Use UTF-8 encoding everywhere
120- Test with Chinese/Japanese/Korean (CJK) characters
121- Test with emoji (they can be 2-4 bytes)
122- Handle different scripts (Latin, Cyrillic, Arabic, etc.)
123
124**Date/Time formatting**:
125```javascript
126// ✅ Use Intl API for proper formatting
127new Intl.DateTimeFormat('en-US').format(date); // 1/15/2024
128new Intl.DateTimeFormat('de-DE').format(date); // 15.1.2024
129
130new Intl.NumberFormat('en-US', {
131 style: 'currency',
132 currency: 'USD'
133}).format(1234.56); // $1,234.56
134```
135
136**Pluralization**:
137```javascript
138// ❌ Bad: Assumes English pluralization
139`${count} item${count !== 1 ? 's' : ''}`
140
141// ✅ Good: Use proper i18n library
142t('items', { count }) // Handles complex plural rules
143```
144
145### Error Handling
146
147**Network errors**:
148- Show clear error messages
149- Provide retry button
150- Explain what happened
151- Offer offline mode (if applicable)
152- Handle timeout scenarios
153
154```jsx
155// Error states with recovery
156{error && (
157 <ErrorMessage>
158 <p>Failed to load data. {error.message}</p>
159 <button onClick={retry}>Try again</button>
160 </ErrorMessage>
161)}
162```
163
164**Form validation errors**:
165- Inline errors near fields
166- Clear, specific messages
167- Suggest corrections
168- Don't block submission unnecessarily
169- Preserve user input on error
170
171**API errors**:
172- Handle each status code appropriately
173 - 400: Show validation errors
174 - 401: Redirect to login
175 - 403: Show permission error
176 - 404: Show not found state
177 - 429: Show rate limit message
178 - 500: Show generic error, offer support
179
180**Graceful degradation**:
181- Core functionality works without JavaScript
182- Images have alt text
183- Progressive enhancement
184- Fallbacks for unsupported features
185
186### Edge Cases & Boundary Conditions
187
188**Empty states**:
189- No items in list
190- No search results
191- No notifications
192- No data to display
193- Provide clear next action
194
195**Loading states**:
196- Initial load
197- Pagination load
198- Refresh
199- Show what's loading ("Loading your projects...")
200- Time estimates for long operations
201
202**Large datasets**:
203- Pagination or virtual scrolling
204- Search/filter capabilities
205- Performance optimization
206- Don't load all 10,000 items at once
207
208**Concurrent operations**:
209- Prevent double-submission (disable button while loading)
210- Handle race conditions
211- Optimistic updates with rollback
212- Conflict resolution
213
214**Permission states**:
215- No permission to view
216- No permission to edit
217- Read-only mode
218- Clear explanation of why
219
220**Browser compatibility**:
221- Polyfills for modern features
222- Fallbacks for unsupported CSS
223- Feature detection (not browser detection)
224- Test in target browsers
225
226### Onboarding & First-Run Experience
227
228Production-ready features work for first-time users, not just power users. Design the paths that get new users to value:
229
230**Empty states**: Every zero-data screen needs:
231- What will appear here (description or illustration)
232- Why it matters to the user
233- Clear CTA to create the first item or start from a template
234- Visual interest (not just blank space with "No items yet")
235
236Empty state types to handle:
237- **First use**: emphasize value, provide templates
238- **User cleared**: light touch, easy to recreate
239- **No results**: suggest a different query, offer to clear filters
240- **No permissions**: explain why, how to get access
241
242**First-run experience**: Get users to their "aha moment" as quickly as possible.
243- Show, don't tell -- working examples over descriptions
244- Progressive disclosure -- teach one thing at a time, not everything upfront
245- Make onboarding optional -- let experienced users skip
246- Provide smart defaults so required setup is minimal
247
248**Feature discovery**: Teach features when users need them, not upfront.
249- Contextual tooltips at point of use (brief, dismissable, one-time)
250- Badges or indicators on new or unused features
251- Celebrate activation events quietly (a toast, not a modal)
252
253**NEVER**:
254- Force long onboarding before users can touch the product
255- Show the same tooltip repeatedly (track and respect dismissals)
256- Block the entire UI during a guided tour
257- Create separate tutorial modes disconnected from the real product
258- Design empty states that just say "No items" with no next action
259
260### Input Validation & Sanitization
261
262**Client-side validation**:
263- Required fields
264- Format validation (email, phone, URL)
265- Length limits
266- Pattern matching
267- Custom validation rules
268
269**Server-side validation** (always):
270- Never trust client-side only
271- Validate and sanitize all inputs
272- Protect against injection attacks
273- Rate limiting
274
275**Constraint handling**:
276```html
277<!-- Set clear constraints -->
278<input
279 type="text"
280 maxlength="100"
281 pattern="[A-Za-z0-9]+"
282 required
283 aria-describedby="username-hint"
284/>
285<small id="username-hint">
286 Letters and numbers only, up to 100 characters
287</small>
288```
289
290### Accessibility Resilience
291
292**Keyboard navigation**:
293- All functionality accessible via keyboard
294- Logical tab order
295- Focus management in modals
296- Skip links for long content
297
298**Screen reader support**:
299- Proper ARIA labels
300- Announce dynamic changes (live regions)
301- Descriptive alt text
302- Semantic HTML
303
304**Motion sensitivity**:
305```css
306@media (prefers-reduced-motion: reduce) {
307 * {
308 animation-duration: 0.01ms !important;
309 animation-iteration-count: 1 !important;
310 transition-duration: 0.01ms !important;
311 }
312}
313```
314
315**High contrast mode**:
316- Test in Windows high contrast mode
317- Don't rely only on color
318- Provide alternative visual cues
319
320### Performance Resilience
321
322**Slow connections**:
323- Progressive image loading
324- Skeleton screens
325- Optimistic UI updates
326- Offline support (service workers)
327
328**Memory leaks**:
329- Clean up event listeners
330- Cancel subscriptions
331- Clear timers/intervals
332- Abort pending requests on unmount
333
334**Throttling & Debouncing**:
335```javascript
336// Debounce search input
337const debouncedSearch = debounce(handleSearch, 300);
338
339// Throttle scroll handler
340const throttledScroll = throttle(handleScroll, 100);
341```
342
343## Testing Strategies
344
345**Manual testing**:
346- Test with extreme data (very long, very short, empty)
347- Test in different languages
348- Test offline
349- Test slow connection (throttle to 3G)
350- Test with screen reader
351- Test keyboard-only navigation
352- Test on old browsers
353
354**Automated testing**:
355- Unit tests for edge cases
356- Integration tests for error scenarios
357- E2E tests for critical paths
358- Visual regression tests
359- Accessibility tests (axe, WAVE)
360
361**IMPORTANT**: Hardening is about expecting the unexpected. Real users will do things you never imagined.
362
363**NEVER**:
364- Assume perfect input (validate everything)
365- Ignore internationalization (design for global)
366- Leave error messages generic ("Error occurred")
367- Forget offline scenarios
368- Trust client-side validation alone
369- Use fixed widths for text
370- Assume English-length text
371- Block entire interface when one component errors
372
373## Verify Hardening
374
375Test thoroughly with edge cases:
376
377- **Long text**: Try names with 100+ characters
378- **Emoji**: Use emoji in all text fields
379- **RTL**: Test with Arabic or Hebrew
380- **CJK**: Test with Chinese/Japanese/Korean
381- **Network issues**: Disable internet, throttle connection
382- **Large datasets**: Test with 1000+ items
383- **Concurrent actions**: Click submit 10 times rapidly
384- **Errors**: Force API errors, test all error states
385- **Empty**: Remove all data, test empty states
386
387Remember: You're hardening for production reality, not demo perfection. Expect users to input weird data, lose connection mid-flow, and use your product in unexpected ways. Build resilience into every component.