UX Engineering Standards
Purpose
Define consistent behavior and quality standards for portfolio UX across accessibility, interaction, motion, performance, and responsive implementation. This enables:
- Consistency across all portfolio pages and future projects
- Reusability of tested interaction and accessibility patterns
- Accessibility compliance with WCAG AA standards
- Maintainability through clear UX engineering documentation
For visual identity, tokens, and component materiality, see the Design System reference:
Scope
In scope
- Layout and navigation behavior (header, footer, route structure)
- Navigation architecture (sticky behavior, keyboard access, responsive behavior)
- Accessibility standards and validation
- Animation strategy and performance
- Theme consistency discipline (token-driven, class-safe implementation)
- UX testing approach (manual + automated)
Out of scope
- Visual identity definitions (color palettes, typography specs, spacing scales)
- Design-token authority and component visual specs (see Design System reference)
- Framework-specific implementation details (see Theme System Reference)
Layout Components
Sticky Header
Rationale:
The portfolio uses a sticky (not fixed) header to keep navigation accessible while scrolling. This choice balances:
- Pros: Always-visible navigation, clear scroll position context, professional UX
- Cons: Slight reduction in viewport height (minimal with 4px padding)
- Alternative considered: Non-sticky header rejected for poor UX on long pages
Implementation:
<header className="control-strip sticky top-0 z-50">
<div className="mx-auto max-w-5xl px-4 py-4 flex items-center justify-between">
{/* Logo */}
{/* Navigation */}
</div>
</header>
Key Patterns:
sticky top-0(browser native, performant)z-50(ensures above content while preserving utility overlays)- Border below for visual separation
- Background color required for sticky to not see-through
Navigation Link Structure
Primary Navigation:
- Home (/) - Landing page with proposition
- Work (/projects) - Portfolio showcase
- CV (/cv) - Career timeline and experience
- Docs (external docs app)
- Contact (/contact) - Contact information
- GitHub (external repository link)
Navigation Behavior:
- Keyboard navigable (Tab/Shift+Tab)
- ARIA labels for screen readers:
aria-label="Main navigation" - Current page indication (visual + semantic)
- Responsive adaptation (horizontal on desktop, expandable on mobile)
Responsive Layout
Desktop (lg and above):
┌─────────────────────────────────────┐
│ Logo Nav1 Nav2 Nav3 Theme │ Header (sticky)
├─────────────────────────────────────┤
│ │
│ Main content (max-w-5xl) │
│ │
├─────────────────────────────────────┤
│ Footer links │
└─────────────────────────────────────┘
Mobile (< md):
┌──────────────────┐
│ Logo Menu │ Header with expandable mobile nav
├──────────────────┤
│ Main content │
│ (full width, │
│ px-4 padding) │
└──────────────────┘
Current approach: Full-bleed control-strip navigation with inline route controls and no hamburger/menu toggle.
Navigation Architecture
Keyboard Navigation
Accessibility Requirements:
- All interactive elements focusable via Tab key
- Logical tab order: left-to-right, top-to-bottom
- Clear focus indicators (ring utilities minimum 2px)
- No
tabindex="-1"abuse (keeps elements in tab order) - Links and buttons distinguishable to assistive tech
Implementation:
{
/* Links use <a> or Link component */
}
<Link
href="/cv"
className="focus-visible:outline focus-visible:outline-2 rounded"
>
CV
</Link>;
{
/* Buttons use <button> */
}
<button className="focus-visible:outline focus-visible:outline-2 rounded">
Action
</button>;
{
/* Skip link for screen readers (placed first in DOM) */
}
<a href="#main" className="sr-only focus:not-sr-only">
Skip to main content
</a>;
Mobile Responsiveness
Current implementation:
- Full-bleed navigation surface with inline controls
- All primary routes remain reachable via keyboard and touch
- Touch-friendly spacing and targets
- Theme toggle always visible
Animation Strategy
Performance-First Approach
Principle: Use CSS transforms and opacity (GPU-accelerated), never layout-affecting properties
Allowed:
opacitychangestransform: translate(),scale(),rotate()filtereffects (blur, brightness)
Forbidden:
width,heightchanges (triggers layout recalculation)margin,paddingchanges (triggers reflow)left,right,top,bottom(use transform instead)
Example:
/* ✅ Good: GPU accelerated */
.fade-in {
opacity: 0;
animation: fadeIn 300ms ease-out forwards;
}
@keyframes fadeIn {
to {
opacity: 1;
}
}
/* ❌ Bad: Triggers layout */
.slide-in {
left: -100px;
animation: slideIn 300ms ease-out forwards;
}
@keyframes slideIn {
to {
left: 0;
} /* Expensive! Use transform instead */
}
Animation Types
Fade-In (Scroll Trigger)
Used for: Content entering viewport on scroll
.scroll-fade-in {
opacity: 0;
transition: opacity 300ms ease-out;
}
.scroll-fade-in.in-view {
opacity: 1;
}
Implementation: Intersection Observer API (no scroll listeners)
Smooth Scroll
Used for: "Back to Top" button, anchor links
element.scrollTo({ top: 0, behavior: 'smooth' });
Transition Timings:
- Fast: 150ms (hover states, small interactions)
- Base: 200ms (theme changes, fade-ins)
- Slow: 300ms (page transitions, major layout changes)
Respecting prefers-reduced-motion
Requirement: Disable animations for users who prefer reduced motion
Implementation:
/* Default: with animation */
.interactive {
transition: all 200ms ease-out;
}
/* Respect user preference */
@media (prefers-reduced-motion: reduce) {
.interactive {
transition: none;
}
}
Testing:
- macOS: System Settings → Accessibility → Display → Reduce motion
- Windows: Settings → Ease of Access → Display → Show animations
- Linux: Varies by desktop environment
Expected: Animations disappear; functionality remains
Accessibility Standards
WCAG AA Compliance
Target Level: WCAG 2.1 Level AA (industry standard)
Key Requirements:
- Color Contrast: 4.5:1 minimum for normal text, 3:1 minimum for large text
Validate against active design tokens from the Design System reference (do not hardcode legacy palette assumptions).
-
Keyboard Navigation: All functionality accessible via keyboard
-
Focus Indicators: Visible outline on focused elements (minimum 2px)
-
Alt Text: All images have descriptive alt text
-
Semantic HTML: Proper heading hierarchy (h1-h6, no skipping levels)
-
ARIA Labels: Screen reader text for icon-only or mode-switch controls
<buttontype="button"aria-label="Toggle light/dark theme"onClick={toggleTheme}>{/* Cockpit-style rocker control */}</button> -
Form Labels: All inputs have associated labels
<label htmlFor="name">Name</label><input id="name" type="text" />
Testing Approach
Automated:
- Lighthouse audit (Chrome DevTools)
- axe DevTools extension
- WAVE accessibility checker
Manual:
- Keyboard-only navigation (no mouse)
- Screen reader testing (NVDA on Windows, VoiceOver on Mac)
- Color contrast verification (WebAIM Contrast Checker)
- Zoom testing (200% zoom should remain usable)
Regular cadence: Test on every significant layout/color change
Best Practices
Theme Consistency
DO:
- Use CSS variables for all theme-dependent colors
- Test all components in both light and dark modes
- Verify contrast ratios in both modes
- Use token-driven names and values defined in shared
globals.cssvariable sets
DON'T:
- Hardcode hex/RGB values in components
- Apply theme classes directly to components (use variables)
- Assume light mode is default (always test both)
- Use
dark:Tailwind class for everything (use variables + dark: sparingly)
Animation Guidelines
DO:
- Use animations to guide attention or indicate state changes
- Keep animations under 300ms for UI feedback
- Respect
prefers-reduced-motion - Test animations on actual mobile hardware
- Keep status sequencing deterministic when motion conveys state
DON'T:
- Animate for decoration alone
- Chain animations without user input (jarring)
- Use animations that block interaction
- Assume fast networks (animations should work on 3G)
- Use looping animations for state progress where one-time sequences are clearer
Hero Pipeline Timing Contract
Current implementation contract for the deploy pipeline indicator in the home hero:
- One-time sequence per page load
- Stage cadence: 1 second per stage
- Order: COMMIT -> CHECKS -> STAGING -> PRODUCTION
- Final state: PRODUCTION remains active, previous stages turn off as the sequence advances
This post-spec timing contract supersedes earlier short-sequence draft guidance and is the baseline for UX validation.
Navigation Extension Pattern
Adding New Pages:
- Add route:
src/app/new-page/page.tsx - Export metadata:
title,description - Add navigation link in layout
- Verify keyboard navigation works
- Test sticky header behavior
Adding New Navigation Section:
<nav className="flex items-center gap-4">
{/* Existing items */}
<Link href="/new-section" className="text-sm">
New Section
</Link>
</nav>
Mobile-First Design
DO:
- Start with mobile layout (single column)
- Use responsive images (NextJS
Imagecomponent) - Test on actual mobile devices
- Consider touch target sizes (44px minimum)
DON'T:
- Design desktop first, then shrink
- Use hover-only interactions (no hover on touch)
- Assume large screens have more bandwidth