# SSR-Safe Browser APIs

## Objective

Stop server-rendered pages breaking or flickering on browser-only APIs.

Wrappers around storage, clipboard, media queries, and viewport measurement that behave predictably where no browser exists.

## Before You Begin

This feature is being added to an application that already exists and already
works. Do not scaffold a new project, and do not assume a blank slate.

Inspect the codebase first and establish:

- The existing application structure and where code of this kind already lives.
- The framework and version in use.
- The existing design system — colours, spacing, typography, and component conventions.
- Existing UI components you can reuse instead of writing new ones.
- The existing database structure, if this feature needs to persist anything.
- The existing authentication and authorization system, if this feature is user-scoped.
- Dependencies already installed, so you don't add a library that duplicates one.
- The existing test setup and conventions.

Only start writing code once you understand the above. If the application
already implements part of this feature, extend it rather than replacing it.

## Implementation Instructions

1. Find every direct reach for a browser global in code that also runs on the server — stored preferences, clipboard writes, viewport width, dark-mode and reduced-motion queries, network status — and route them through a small set of shared accessors.
2. Give each accessor a deterministic value to return when there is no browser, and choose that value to match what the majority of users will resolve to, so the correction after mount is invisible for most and small for the rest.
3. Report availability explicitly alongside the value, so a caller can distinguish not yet known from genuinely unavailable and render a neutral state rather than guessing.
4. Return a refusal a caller can act on when an API exists but is blocked — storage disabled in a private window, clipboard denied, a permission never granted — instead of throwing from inside a render.
5. Do not solve the visible flash by rendering nothing until mount. A blank sidebar or a missing header is a worse first paint than a briefly imperfect one, and it costs the page its server-rendered content.

## UI and UX Requirements

Match the application's existing design system exactly. Reuse its components,
spacing, and typography. This feature should look like it was always there.

## Responsive Requirements

Works on mobile, tablet, and desktop. Touch targets are large enough to hit on a
phone, and nothing overflows horizontally at 320px.

## Accessibility Requirements

- Fully keyboard navigable.
- Correct semantic elements and ARIA roles.
- Visible focus states.
- Meets WCAG AA contrast.
- Dynamic changes are announced to screen readers.
- Respects prefers-reduced-motion.

## Edge Cases

- Every accessor needs one documented server-side default, and it must be the same on every request. A value that differs between the server render and the first client render produces a mismatch, and a value that differs between two server renders makes the page uncacheable.
- Reading a stored theme or a media query only after mount swaps the layout in front of the user. Reserve the space, keep the first paint stable, and apply the corrected value without moving anything around it.
- Storage throws rather than returning empty when it is disabled or the quota is full, and clipboard and notification permissions can be denied outright. Every wrapper must treat refusal as an ordinary outcome and let the caller fall back.
- The wrappers must be trivial to replace in tests, because assertions about a component's behaviour at a narrow viewport or with storage unavailable should not require driving a real browser.
- Media query and resize listeners registered by these wrappers must be removed when their caller goes away, or a long session accumulates listeners that fire against unmounted views.
- Viewport height on mobile changes as browser chrome and the on-screen keyboard appear, so a measurement taken once at mount is wrong within seconds.
- The same accessor may be called from many components at once, so it should share one underlying listener and one cached value rather than opening a subscription per caller.

## Testing

Exercise the feature end to end in the running application. Cover every edge case
above, then run the existing test suite and confirm nothing regressed.

## Acceptance Criteria

- [ ] No component reaches for a browser global directly on a path that also renders on the server.
- [ ] Each accessor returns one documented, deterministic value when no browser is present.
- [ ] Nothing on the page moves or is replaced when the real client value arrives after mount.
- [ ] Blocked storage, clipboard, and permission APIs return a handled refusal rather than throwing.
- [ ] Each accessor can be stubbed in a test without a browser environment.
- [ ] Listeners registered by the wrappers are removed with their callers and shared between concurrent ones.
- [ ] The feature matches the existing design system.
- [ ] No existing functionality is broken.

## Adaptation Rules

- Match the existing design system. Do not introduce a new colour palette,
  spacing scale, or component library.
- Reuse existing components and utilities wherever they fit.
- Follow the naming, file layout, and code style already present.
- Do not upgrade, replace, or remove existing dependencies to make this
  feature fit. Adapt the feature to the app, not the app to the feature.
- Do not break existing functionality. If a change is genuinely required in
  existing code, make the smallest one that works and say so.
- If something in these instructions conflicts with how the application is
  built, follow the application and explain the deviation.

## Final Verification

Before you report the work as done:

1. Re-read the acceptance criteria above and check each one against what you
   actually built.
2. Run the application and exercise the feature end to end.
3. Run the existing test suite and confirm you have broken nothing.
4. Check the feature on mobile, tablet, and desktop widths.
5. Check keyboard navigation and focus handling.
6. Summarize what changed: files added, files modified, and anything you
   deliberately did differently because of how this application is built.

If any acceptance criterion is unmet, fix it before reporting completion.
