Back to impeccable-harden
impeccable-harden 7.3 min read 389 lines

harden

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.

Strengthen interfaces against edge cases, errors, internationalization issues, and real-world usage scenarios that break idealized designs.

Assess Hardening Needs

Identify weaknesses and edge cases:

  • Test with extreme inputs:
- Very long text (names, descriptions, titles)
- Very short text (empty, single character)
- Special characters (emoji, RTL text, accents)
- Large numbers (millions, billions)
- Many items (1000+ list items, 50+ options)
- No data (empty states)

  • Test error scenarios:
- Network failures (offline, slow, timeout)
- API errors (400, 401, 403, 404, 500)
- Validation errors
- Permission errors
- Rate limiting
- Concurrent operations

  • Test internationalization:
- Long translations (German is often 30% longer than English)
- RTL languages (Arabic, Hebrew)
- Character sets (Chinese, Japanese, Korean, emoji)
- Date/time formats
- Number formats (1,000 vs 1.000)
- Currency symbols

CRITICAL: Designs that only work with perfect data aren't production-ready. Harden against reality.

Hardening Dimensions

Systematically improve resilience:

Text Overflow & Wrapping

Long text handling:

/ Single line with ellipsis /
.truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

/ Multi-line with clamp /
.line-clamp {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}

/ Allow wrapping /
.wrap {
word-wrap: break-word;
overflow-wrap: break-word;
hyphens: auto;
}

Flex/Grid overflow:

/ Prevent flex items from overflowing /
.flex-item {
min-width: 0; / Allow shrinking below content size /
overflow: hidden;
}

/ Prevent grid items from overflowing /
.grid-item {
min-width: 0;
min-height: 0;
}

Responsive text sizing:

  • Use clamp() for fluid typography
  • Set minimum readable sizes (14px on mobile)
  • Test text scaling (zoom to 200%)
  • Ensure containers expand with text

Internationalization (i18n)

Text expansion:

  • Add 30-40% space budget for translations
  • Use flexbox/grid that adapts to content
  • Test with longest language (usually German)
  • Avoid fixed widths on text containers

// ❌ Bad: Assumes short English text

// ✅ Good: Adapts to content

RTL (Right-to-Left) support:

/ Use logical properties /
margin-inline-start: 1rem; / Not margin-left /
padding-inline: 1rem; / Not padding-left/right /
border-inline-end: 1px solid; / Not border-right /

/ Or use dir attribute /
[dir="rtl"] .arrow { transform: scaleX(-1); }

Character set support:

  • Use UTF-8 encoding everywhere
  • Test with Chinese/Japanese/Korean (CJK) characters
  • Test with emoji (they can be 2-4 bytes)
  • Handle different scripts (Latin, Cyrillic, Arabic, etc.)

Date/Time formatting:

// ✅ Use Intl API for proper formatting
new Intl.DateTimeFormat('en-US').format(date); // 1/15/2024
new Intl.DateTimeFormat('de-DE').format(date); // 15.1.2024

new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
}).format(1234.56); // $1,234.56

Pluralization:

// ❌ Bad: Assumes English pluralization
${count} item${count !== 1 ? 's' : ''}

// ✅ Good: Use proper i18n library
t('items', { count }) // Handles complex plural rules

Error Handling

Network errors:

  • Show clear error messages
  • Provide retry button
  • Explain what happened
  • Offer offline mode (if applicable)
  • Handle timeout scenarios

// Error states with recovery
{error && (

Failed to load data. {error.message}




)}

Form validation errors:

  • Inline errors near fields
  • Clear, specific messages
  • Suggest corrections
  • Don't block submission unnecessarily
  • Preserve user input on error

API errors:

  • Handle each status code appropriately
- 400: Show validation errors
- 401: Redirect to login
- 403: Show permission error
- 404: Show not found state
- 429: Show rate limit message
- 500: Show generic error, offer support

Graceful degradation:

  • Core functionality works without JavaScript
  • Images have alt text
  • Progressive enhancement
  • Fallbacks for unsupported features

Edge Cases & Boundary Conditions

Empty states:

  • No items in list
  • No search results
  • No notifications
  • No data to display
  • Provide clear next action

Loading states:

  • Initial load
  • Pagination load
  • Refresh
  • Show what's loading ("Loading your projects...")
  • Time estimates for long operations

Large datasets:

  • Pagination or virtual scrolling
  • Search/filter capabilities
  • Performance optimization
  • Don't load all 10,000 items at once

Concurrent operations:

  • Prevent double-submission (disable button while loading)
  • Handle race conditions
  • Optimistic updates with rollback
  • Conflict resolution

Permission states:

  • No permission to view
  • No permission to edit
  • Read-only mode
  • Clear explanation of why

Browser compatibility:

  • Polyfills for modern features
  • Fallbacks for unsupported CSS
  • Feature detection (not browser detection)
  • Test in target browsers

Onboarding & First-Run Experience

Production-ready features work for first-time users, not just power users. Design the paths that get new users to value:

Empty states: Every zero-data screen needs:

  • What will appear here (description or illustration)
  • Why it matters to the user
  • Clear CTA to create the first item or start from a template
  • Visual interest (not just blank space with "No items yet")

Empty state types to handle:

  • First use: emphasize value, provide templates
  • User cleared: light touch, easy to recreate
  • No results: suggest a different query, offer to clear filters
  • No permissions: explain why, how to get access

First-run experience: Get users to their "aha moment" as quickly as possible.

  • Show, don't tell -- working examples over descriptions
  • Progressive disclosure -- teach one thing at a time, not everything upfront
  • Make onboarding optional -- let experienced users skip
  • Provide smart defaults so required setup is minimal

Feature discovery: Teach features when users need them, not upfront.

  • Contextual tooltips at point of use (brief, dismissable, one-time)
  • Badges or indicators on new or unused features
  • Celebrate activation events quietly (a toast, not a modal)

NEVER:

  • Force long onboarding before users can touch the product
  • Show the same tooltip repeatedly (track and respect dismissals)
  • Block the entire UI during a guided tour
  • Create separate tutorial modes disconnected from the real product
  • Design empty states that just say "No items" with no next action

Input Validation & Sanitization

Client-side validation:

  • Required fields
  • Format validation (email, phone, URL)
  • Length limits
  • Pattern matching
  • Custom validation rules

Server-side validation (always):

  • Never trust client-side only
  • Validate and sanitize all inputs
  • Protect against injection attacks
  • Rate limiting

Constraint handling:


type="text"
maxlength="100"
pattern="[A-Za-z0-9]+"
required
aria-describedby="username-hint"
/>

Letters and numbers only, up to 100 characters

Accessibility Resilience

Keyboard navigation:

  • All functionality accessible via keyboard
  • Logical tab order
  • Focus management in modals
  • Skip links for long content

Screen reader support:

  • Proper ARIA labels
  • Announce dynamic changes (live regions)
  • Descriptive alt text
  • Semantic HTML

Motion sensitivity:

@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}

High contrast mode:

  • Test in Windows high contrast mode
  • Don't rely only on color
  • Provide alternative visual cues

Performance Resilience

Slow connections:

  • Progressive image loading
  • Skeleton screens
  • Optimistic UI updates
  • Offline support (service workers)

Memory leaks:

  • Clean up event listeners
  • Cancel subscriptions
  • Clear timers/intervals
  • Abort pending requests on unmount

Throttling & Debouncing:

// Debounce search input
const debouncedSearch = debounce(handleSearch, 300);

// Throttle scroll handler
const throttledScroll = throttle(handleScroll, 100);

Testing Strategies

Manual testing:

  • Test with extreme data (very long, very short, empty)
  • Test in different languages
  • Test offline
  • Test slow connection (throttle to 3G)
  • Test with screen reader
  • Test keyboard-only navigation
  • Test on old browsers

Automated testing:

  • Unit tests for edge cases
  • Integration tests for error scenarios
  • E2E tests for critical paths
  • Visual regression tests
  • Accessibility tests (axe, WAVE)

IMPORTANT: Hardening is about expecting the unexpected. Real users will do things you never imagined.

NEVER:

  • Assume perfect input (validate everything)
  • Ignore internationalization (design for global)
  • Leave error messages generic ("Error occurred")
  • Forget offline scenarios
  • Trust client-side validation alone
  • Use fixed widths for text
  • Assume English-length text
  • Block entire interface when one component errors

Verify Hardening

Test thoroughly with edge cases:

  • Long text: Try names with 100+ characters
  • Emoji: Use emoji in all text fields
  • RTL: Test with Arabic or Hebrew
  • CJK: Test with Chinese/Japanese/Korean
  • Network issues: Disable internet, throttle connection
  • Large datasets: Test with 1000+ items
  • Concurrent actions: Click submit 10 times rapidly
  • Errors: Force API errors, test all error states
  • Empty: Remove all data, test empty states

Remember: 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.