# Polymorphic Components

## Objective

Reuse one visual component while it still renders the element the browser needs.

A mechanism for letting a shared component keep its appearance while the caller chooses the underlying element it renders as.

## 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 the components that already fake their element — a button styled as a link, a card wrapped in an anchor, a heading rendered at the wrong level to get the right size — and make the element an explicit choice rather than a workaround.
2. Let the caller pass through the native attributes that belong to the chosen element, and merge them with the component's own rather than letting either side silently win. Class names, event handlers, and identifiers all need a defined merge order.
3. Constrain the choice. A component that can render as anything will eventually render as something meaningless; allow the small set of elements that make sense for that component and reject the rest.
4. Keep the reference to the real underlying node reachable by the caller, so focus management, measurement, and scroll-into-view still work once the element changes.
5. Do not style on the element name. Appearance belongs to the variant chosen through props — the rules named in Component Prop Guardrails — and the element belongs to semantics; if the two are coupled, changing one silently changes the other.

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

- Native attributes passed by the caller must merge rather than clobber. Two class lists concatenate, two click handlers both run in a defined order, and an identifier supplied by the caller wins over a generated one.
- Anything that navigates must be an anchor with a destination, and anything that acts must be a button with a type. A clickable division with a click handler is not keyboard-operable and must be refused, not worked around with a tab index.
- The reference to the underlying node must survive the element switch, and the shapes of the events the component emits must match the element actually rendered, so a form event is not typed or documented as a pointer event.
- Styling must not depend on which element was chosen. A rule that only matches when the component renders as an anchor breaks the moment a caller renders it as a button, and the breakage shows up in an unrelated screen.
- A disabled state has to be expressed differently on an anchor than on a button — anchors cannot be disabled — so the component must either refuse the combination or render an inert non-navigating state.
- Nesting must be checked: an interactive element inside another interactive element is invalid and produces unpredictable activation, so a card rendered as an anchor cannot also contain buttons.
- Any accessible name, role, or state the component supplies must be recomputed for the chosen element, not hardcoded for the default one.

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

- [ ] Each polymorphic component accepts only a documented set of elements and rejects the others.
- [ ] Caller-supplied attributes, class names, and handlers merge with the component's own under a stated precedence.
- [ ] The underlying element node remains reachable by callers for focus and measurement.
- [ ] No visual rule in the codebase keys off which element a polymorphic component rendered as.
- [ ] Navigating instances render anchors with destinations and acting instances render buttons with an explicit type.
- [ ] Disabled and inert states are correct for every permitted element, not only the default.
- [ ] 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.
