# Form

## Overview

The form composition layer normalizes native form grouping patterns that combine multiple controls into one horizontal affordance.

Use it for semantic search forms and grouped control wrappers. Individual controls still come from the input, button, select, checkbox, radio, switch, and related component stylesheets.

Labelled fields and grouped controls use their intrinsic inline size by default, capped at the available container width. Add `class="fluid"` to the wrapping `label`, search form, or group when that complete field or group should fill its container.

## Usage

```html
<form role="search">
  <input type="search" name="q" placeholder="Search" />
  <button type="submit">Search</button>
</form>
```

Use `role="search"` on every form whose purpose is search unless an application has an explicit reason to expose different semantics. A compact search input and button do not need a fieldset. For an advanced search form where the query controls form a meaningfully labeled subset alongside filters, use a native fieldset and concise visible legend:

```html
<form role="search" class="flow">
  <fieldset role="group">
    <legend>Search governed knowledge</legend>
    <input type="search" name="q" />
    <button type="submit">Search</button>
  </fieldset>
  <!-- Search filters -->
</form>
```

```html
<fieldset role="group">
  <legend>Amount</legend>
  <input type="number" name="amount" />
  <button type="button">Apply</button>
</fieldset>
```

Use `form[role="search"]` for compact search bars. A grouped search row consists of one `input[type="search"]` followed by one `button[type="submit"]`; a preceding legend does not count as a grouped control or alter the input's outer corners. Use `fieldset[role="group"]` when grouped controls need a native legend. Use `div[role="group"]` only when a fieldset would be semantically incorrect.

Use `.cluster` around multiple groups when the groups should keep their intrinsic widths, sit beside one another when space permits, and wrap as complete units. Use `.switcher` instead when peer groups should grow to share the available width.

Button-styled links can also form a group when the actions navigate to related destinations:

```html
<div role="group" aria-label="Source actions">
  <a class="button outline" href="/source">View source</a>
  <a class="button outline icon" href="/edit" aria-label="Edit source">edit</a>
</div>
```

Unlike most component styles, labels and legends do not map these values through component-specific variables. They consume the shared `--form-label-*` variables directly. This is intentional: customize these variables on the form or a containing region so every label and legend in that form retains the same visual treatment.

## Migrating From Full-Width Controls

Labelled fields, search bars, and grouped controls previously filled their container by default. Add `.fluid` to the wrapping label to preserve that layout for a labelled field:

```html
<label class="fluid">
  <span data-role="label">Email</span>
  <input type="email" />
</label>
```

Add `.fluid` directly to a search form or grouped-control wrapper when that complete group should remain full width:

```html
<form class="fluid" role="search">...</form>
<fieldset class="fluid" role="group">...</fieldset>
```

For native radio and checkbox fieldsets, explicit `role="group"` also opts into joined button styling. Without it, the controls retain their default appearance. `fieldset.chips` uses chip styling; adding `role="group"` arranges chips in a wrapping row. The explicit role is a styling hook even though fieldsets already have implicit group semantics.

## Behavior

Covered by [Playwright behavior tests](../../tests/e2e/forms-inputs.spec.js).

- FRM-001: A compact `form[role="search"]`, `fieldset[role="group"]`, and `div[role="group"]` render native buttons, button-styled links, and supported inputs as single horizontal control groups. A search form containing a grouped control row remains a normal block container so it can include filters.
- FRM-002: Adjacent controls overlap one border width so joins read as one divider.
- FRM-003: Interior control corners are square; only the first and last grouped controls keep the outer radius. A fieldset legend does not count when determining the first grouped control.
- FRM-004: `fieldset[role="group"]` resets native border, margin, and padding so it behaves like the other group wrappers.
- FRM-005: Inputs and text buttons or button-styled links in grouped controls grow and shrink together. Search actions retain their intrinsic width so the search input absorbs extra space in a fluid group, while icon-only actions retain the square width defined by the shared control height.
- FRM-006: Native fieldsets that keep their border chrome derive their corner radius from `--form-control-radius`.
- FRM-007: Search and group wrappers use their intrinsic inline size by default, shrink within the available container without page-level overflow, and fill the container only when composed with `.fluid`.
- FRM-008: Visible field labels and native legends consume the shared `--form-label-*` variables directly so their color, font size, and line height remain consistent across form control types.
- FRM-009: A label wrapping an input, select, or textarea uses the field's intrinsic inline size by default; applying `.fluid` to that label makes the complete field, including feedback, fill its container.

## Accessibility

- Give every search form `role="search"` unless the application explicitly requires different semantics, and do not use it for non-search behavior.
- Use the search landmark itself for a compact bar. Use `<fieldset>` and `<legend>` when an advanced search form's query controls need a shared visible label that distinguishes them from filters; do not add the grouping when the individual controls are already sufficiently clear.
- Prefer `fieldset[role="group"]` with a visible `legend` when controls need a shared label.
- Keep grouped controls in logical source order.

## Variables

| Variable | Type | Description |
| --- | --- | --- |
| `--form-control-border-width` | `<length>` | Sets the border width used to join adjacent controls in semantic form groups; change it when grouped controls need to match a different control border rhythm. |
| `--form-control-radius` | `<length>` | Sets the fallback outer corner radius for grouped form controls; change it when a grouped control does not provide its own radius hook. |
| `--form-icon-button-inline-size` | `<length>` | Sets the square inline size of icon-only buttons inside grouped controls; change it when their typography or control padding requires a different footprint. |
| `--form-label-color` | `<color>` | Shared color for field labels and native legends. |
| `--form-label-font-size` | `<length>` | Shared font size for field labels and native legends. |
| `--form-label-line-height` | `<number>` | Shared line height for field labels and native legends. |
| `--grouped-control-radius` | `<length>` | Corner radius hook for controls inside native form groups. Buttons and inputs map this hook back to their component radius aliases so grouped controls keep compatible outer corners. |

## Depends On

- `--color-text-accent`
- `--font-line-height-body`
- `--font-size`
- `--layout-control-border-width`
- `--layout-control-min-height`
- `--layout-control-radius`
