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