# Production hardening guide This document captures the final hardening requirements for Mechanica’s runtime shell and viewer. ## Error boundaries Mechanica should use layered error boundaries: 1. **Application boundary** around the entire React tree. 2. **Route boundary** around catalogue/viewer pages. 3. **Scene boundary** around WebGL canvas and lazy-loaded machine scenes. Scene errors should not blank the entire application. A failed machine asset should render a dark error panel with: - machine title or slug - failed asset URL if available - retry button - catalogue link - support contact from `VITE_SUPPORT_EMAIL` Error boundaries should log enough context for maintainers while avoiding secret data. ## Loading fallbacks Every lazy boundary must reserve layout space before content loads. Recommended fallbacks: | Area | Fallback | | --- | --- | | Catalogue cards | Fixed-height skeleton card grid. | | Viewer canvas | Dark viewport, progress bar, machine title skeleton. | | Left part list | Skeleton rows matching final row height. | | Right controls | Skeleton sections with stable headings. | | Detail drawer | Empty state until a part is selected. | Fallbacks must not introduce white flashes. The inline CSS in `index.html` provides a dark initial loading shell before React boots. ## Lazy loading and code splitting Required lazy boundaries: - catalogue route - viewer route - each machine module - heavy guided-tour components - optional diagnostics/performance overlay - optional bundle/debug tooling The Vite config separates large vendor families to keep route chunks cacheable. Runtime machine modules should be loaded by slug through a registry-safe dynamic import map instead of arbitrary user-provided import strings. ## URL sharing The app should encode only stable, serializable view state: - machine slug - camera position and target or preset - exploded amount - display mode - selected part - hidden parts - opacity overrides - cross-section axis/offset - labels enabled - RPM/time scale Do not encode transient values such as hover state, FPS, temporary drag state, or internal Three.js object UUIDs. Share/copy behavior should: 1. Generate an absolute URL using `VITE_APP_ORIGIN` or `window.location.origin`. 2. Preserve known route params. 3. Clamp numeric values before serialization. 4. Use `navigator.clipboard.writeText` when available. 5. Fall back to a temporary text area selection for older browsers. 6. Announce success/failure through an ARIA live region. ## Keyboard navigation Minimum keyboard support: | Key | Action | | --- | --- | | `Tab` / `Shift+Tab` | Move focus through visible controls. | | `Enter` / `Space` | Activate buttons, toggles, catalogue cards. | | `Escape` | Close drawer, popover, command palette, or focused overlay. | | `?` | Open keyboard shortcut help. | | `1`-`6` | Camera presets when viewer canvas region is active. | | `R` | Reset camera. | | `E` | Toggle exploded view. | | `W` | Toggle wireframe. | | `L` | Toggle labels. | | `P` or `Space` | Play/pause animation when not typing in a form field. | | `.` | Step animation forward in step-through mode. | Shortcuts must be disabled while focus is inside text inputs, textareas, selects, or content-editable regions. ## Reduced motion Respect `prefers-reduced-motion: reduce`. Behavior: - Disable auto-playing guided camera flights. - Prefer instant camera preset transitions or very short fades. - Pause animations by default on first load. - Disable looping decorative UI animations. - Keep user-triggered mechanical animation available because it is core educational content. - Provide a visible preference override if the preferences panel exists. ## Accessibility Core requirements: - Semantic buttons and links. - Visible focus styles. - ARIA labels for icon-only controls. - ARIA live region for copy-link status and loading errors. - `role="status"` for progress messages. - `aria-current` on active route/category when applicable. - Modal/drawer focus trapping where panels block interaction. - Escape closes overlays. - Text contrast meets WCAG AA. - Touch targets are at least 44px. Canvas accessibility: - The viewer canvas should have an accessible name describing the active machine. - Provide non-canvas part list controls for selection, visibility, and opacity. - Guided tour captions should mirror critical visual highlights in text. ## Mobile hardening At mobile widths: - Left and right sidebars collapse into bottom sheets or drawers. - Canvas remains the primary visible region. - Toolbar buttons wrap or become horizontally scrollable. - Part list rows stay touch-friendly. - Hover-only features have tap equivalents. - Tooltips do not require hover. - The browser address bar should not cause full-height layout jumps; use dynamic viewport units where supported. ## WebGL failure handling Detect renderer initialization failures and present a non-WebGL fallback message: - Explain that WebGL is required. - Link back to the catalogue. - Keep machine descriptions and facts readable. - Avoid repeated renderer creation loops. Common failure cases: - WebGL disabled by browser policy. - Unsupported GPU/driver. - Context lost during heavy asset use. - Memory pressure on mobile. Handle `webglcontextlost` and `webglcontextrestored` on the canvas when possible. ## Security and privacy - No secrets in `VITE_` variables. - No eval-based dynamic module loading. - No user HTML injection in machine descriptions. - Sanitize or encode any registry content that could become HTML. - Copy links should contain only viewer state, not personal data. - Favourites remain local unless an explicit account system is added.