# Headless Component Wrapper

## Objective

Put the app's own styling and API in front of behaviour-only primitives.

A wrapping layer that gives unstyled third-party primitives the app's visual language and its own stable interface.

## 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. Identify the behaviour-heavy primitives already in use — menus, dialogs, comboboxes, date pickers, tooltips — and give each one wrapper that the rest of the app imports instead of the primitive directly.
2. Define the wrapper's interface from the app's own vocabulary of sizes, tones, and states, and translate to whatever the primitive expects inside. The call site should not need to know which primitive is underneath.
3. Preserve every accessibility behaviour the primitive provides — focus trapping, roving focus, labelling, keyboard handling, dismissal — and verify it survives the wrapping rather than assuming it does.
4. Keep the wrapper thin enough to be replaced. Confine the primitive's concepts to that one file so swapping it later is a rewrite of the wrapper, not an audit of every screen.
5. Do not let some screens import the wrapper and others reach past it to the primitive. A bypass route means the styling, defaults, and accessibility fixes live in two places, and the second one drifts.

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

- Vendor props must not leak through the wrapper's interface. Spreading unknown props straight onto the primitive means every call site can quietly depend on vendor internals, and the wrapper stops being replaceable the moment one does.
- The primitive's accessibility work is the reason it was chosen, and it is easy to undo by overriding a role, replacing an internal element, stripping a generated identifier, or intercepting a keyboard handler. Every wrapper needs its keyboard and screen reader behaviour checked after wrapping.
- There must be a defined exit path from the vendor. If the primitive is abandoned or diverges, the work should be confined to the wrapper, which means no vendor type, prop name, or concept may appear in application code.
- A parallel component that bypasses the wrapper defeats the whole arrangement. Remove the direct import route, or make it obvious in review, so a second unstyled dialog does not appear next to the styled one.
- The primitive's own defaults may not match the app's conventions — dismissal on outside click, initial focus, portal target, animation timing — so the wrapper must set them explicitly rather than inheriting whatever the vendor decided.
- Version upgrades of the primitive can change behaviour without changing the wrapper's interface, so the wrapper's behaviour needs verifying on upgrade rather than trusting the type check to pass.
- Where a wrapped primitive exposes several coordinated parts, its composition should follow the same conventions as Compound Component Pattern rather than inventing a second way to compose the same shapes.

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

- [ ] Every behaviour-only primitive in use is reached through an application wrapper.
- [ ] Application code contains no vendor prop names, types, or concepts.
- [ ] Keyboard navigation, focus management, labelling, and dismissal behave identically before and after wrapping.
- [ ] The wrapper sets the app's own defaults rather than inheriting the primitive's.
- [ ] There is no route by which a screen can use the primitive directly.
- [ ] Replacing a primitive requires changing only its wrapper.
- [ ] 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.
