# @duo/ui

Web components (Lit) for the DUO design system.

## `duo-button` and HTML forms

The real `<button>` is rendered **inside the component’s shadow root**. Browsers only reliably pair submit/reset actions with a `<form>` when the **native** control is in the light DOM (or uses the `form` attribute in browsers that implement it consistently). To avoid inconsistent behaviour, **`duo-button` resolves the target form and calls `HTMLFormElement.requestSubmit()` (submit) or `.reset()` (reset) on click.**

### Recommended pattern

1. Prefer putting `<duo-button>` inside the `<form>` (then the component finds the form via `closest("form")`).
2. Or give the form a stable **`id`** and set **`form`** on `<duo-button>` (stored as the `formTarget` property; HTML attribute name stays `form`) so the form can be resolved with `getElementById`.

```html
<form id="sign-in-form" method="post" action="/login">
  <label for="email">Email</label>
  <input id="email" name="email" type="email" />

  <duo-button type="submit" form="sign-in-form" variant="primary" block>
    Sign in
  </duo-button>
</form>
```

The `form` attribute value must match the form’s `id` when you use explicit association.

### Alternatives (not preferred here)

- **Native `<button type="submit" class="duo-button …">`**: works without `form`, but you lose `duo-button` features (loading state, slots, etc.) unless duplicated.
- **JavaScript** (`click` → `form.requestSubmit()`): extra logic and edge cases (multiple forms, keyboard submit); use only when necessary.

### Related

Apps that vendor this bundle should rebuild `@duo/ui` and copy `dist/index.js` after upgrading—see that repo’s sync script. CDN consumers can use `dist/index.min.js` for a smaller browser bundle.
