# Focus Containment Primitive

## Objective

Keep keyboard focus inside an open overlay and return it where it started.

A shared containment primitive that moves focus into an overlay, cycles it within, and restores it on close.

## 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 overlay that takes over the screen — dialogs, drawers, command palettes, mobile navigation — and give all of them the same containment behaviour rather than each implementing its own.
2. Record the element that had focus before the overlay opened, move focus to a sensible starting point inside it, and return focus to the recorded element when it closes.
3. Recompute the set of focusable elements at the moment focus moves, not once when the overlay opens. Overlay content changes as fields appear, sections expand, and content loads.
4. Track containment as a stack so a confirmation opened from a dialog contains focus to itself and hands containment back to its parent when dismissed.
5. Do not hide the rest of the page from assistive technology by removing it from the accessibility tree and then forget to reverse it. A stale hidden state leaves the whole application silent to a screen reader after the overlay closes.

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

- The focusable elements inside an overlay change while it is open — asynchronously loaded content, conditionally rendered fields, a disabled submit that becomes enabled. Query the set live rather than caching a list captured at open time.
- Overlays open other overlays. Containment must nest, so the innermost overlay holds focus and closing it restores containment to the one beneath rather than to the page.
- Screen reader users navigate with virtual cursors and gestures that are not tab presses, and a containment that fights those tools traps them. Keep Escape working, keep the close control reachable, and do not attempt to intercept every mode of navigation.
- The element that had focus before the overlay opened may be gone by the time it closes — the row was deleted, the list re-rendered, the trigger was itself inside the overlay. Fall back to a stable nearby container so focus never lands on the document body.
- An overlay with no focusable content at all still needs a target, otherwise focus stays behind it and every keystroke goes to the page underneath.
- Containment must not swallow browser-level shortcuts or prevent the user reaching the address bar; contain focus within the document, not within the browser.
- Content inside the overlay rendered elsewhere in the DOM — a menu or popover portalled to the document — is visually inside but structurally outside. Treat it as part of the contained region or focus will escape whenever such a control opens.

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

- [ ] Tab and Shift-Tab cycle only within the open overlay and never reach the page behind it.
- [ ] Focus moves into the overlay on open and returns to the triggering element on close.
- [ ] Focusable content added or removed while the overlay is open is included in the cycle immediately.
- [ ] Nested overlays contain focus independently and hand containment back in order.
- [ ] Escape and the visible close control both work in every overlay using the primitive.
- [ ] A missing or removed trigger results in focus landing on a defined fallback, never on the document body.
- [ ] The rest of the page is hidden from assistive technology only while an overlay is open, and is exposed again afterwards.
- [ ] 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.
