diff --git a/.specs/field-select.md b/.specs/field-select.md new file mode 100644 index 000000000..ad6b0d7d7 --- /dev/null +++ b/.specs/field-select.md @@ -0,0 +1,150 @@ +--- +name: field-select +category: inputs +structure: monolithic +status: implemented +spec_version: 1 +figma: + url: https://www.figma.com/design/t97pXRs7xME3SJDs5iZ5RF/Webkit?node-id=536-22 + node_id: 536:22 +checksum: 4dfbb39bbec8d2ef0f8ecab7d8ca38cf4bb084a5aa6caf745d187fd5f0ad5a9f +created: 2026-07-22 +last_updated: 2026-07-22 +--- +# Field Select — Component Spec + +## Purpose + +Form field wrapper for `Select` that composes `Label`, `Select` (with its `Trigger` + `Content` + `Option` sub-components), and `HelperText` into a single vertical stack with consistent spacing. Use it whenever a select needs a visible label or helper/error message — mirroring `FieldText`'s role for text inputs. Options are supplied via a data prop (`options: { value, label }[]`) so the field owns the rendering; consumers who need slot-based composition can drop to the underlying `Select` compound directly. + +## Usage + +```vue + + + +``` + +## Props + +| Prop | Type | Default | Required | JSDoc | +|---|---|---|---|---| +| `options` | `Array<{ value: unknown; label: string; disabled?: boolean }>` | `[]` | no | Options rendered inside the dropdown. When empty, the dropdown renders no options. | +| `label` | `string` | `''` | no | Text rendered inside the `Label`. When empty, the label row is omitted. | +| `placeholder` | `string` | `''` | no | Placeholder shown on the trigger when nothing is selected. | +| `helperText` | `string` | `''` | no | Auxiliary text rendered inside `HelperText`. When empty, the helper row is omitted **except** when `disabled` is true — in that case the component falls back to the default disabled message so the lock icon always has matching copy. | +| `size` | `'small' \| 'medium' \| 'large'` | `'medium'` | no | Size forwarded to the `Select` trigger. Heights: small=28px, medium=32px, large=40px. | +| `multiple` | `boolean` | `false` | no | Switches the component to multi-select; `modelValue` becomes an array. | +| `disabled` | `boolean` | `false` | no | Disables the select and switches the helper to `kind="disabled"` (lock icon). | +| `readonly` | `boolean` | `false` | no | Marks the select read-only; value is visible but the dropdown is locked. | +| `required` | `boolean` | `false` | no | Adds the `Required` tag to the `Label` and sets `aria-required` on the trigger. | +| `invalid` | `boolean` | `false` | no | Switches the helper to `kind="invalid"` and applies invalid border/ring tokens on the trigger. | +| `inputId` | `string` | `''` | no | id for the trigger; consumed by `Label` via `for` and by `aria-describedby` wiring. When empty, the component auto-generates an id via `useId()`. | + +## v-model + +| Model | Type | Default | Emits | Notes | +|---|---|---|---|---| +| `v-model` | `string \| number \| unknown[]` | `undefined` | `update:modelValue` | Two-way bound selection via `defineModel`. Scalar in single mode; array in multi mode. | + +## Events + +_No plain events — activation flows through `v-model`._ + +## Slots + +| Slot | Scope | Notes | +|---|---|---| +| `default` | — | Optional override for the rendered options. When absent, the component renders one `Select.Option` per entry in `options`. | + +## States + +- Visual states: `default`, `required`, `invalid`, `disabled`, `readonly` +- `data-required` mirrors the `required` prop +- `data-invalid` mirrors the `invalid` prop +- `data-disabled` mirrors the `disabled` prop +- `data-readonly` mirrors the `readonly` prop +- `data-size` mirrors the `size` prop + +## Motion & Animations + +| Trigger | Animation / Transition | Token | Reduced-motion fallback | +|---|---|---|---| +| _none_ — motion is owned by the underlying `Select` | — | — | — | + +## Tokens + +| Region | Token (DESIGN.md) | +|---|---| +| gap (between label / select / helper rows) | `var(--spacing-xs)` | + +(Typography and color tokens are owned by the children — `Label`, `Select`, `HelperText` — and not redeclared here.) + +## Theme gaps + +| Figma variable | Temporary primitive | Follow-up | +|---|---|---| +| _none_ | — | — | + +## Accessibility (WCAG 2.1 AA) + +- The `Label`'s native `