# 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.
