# InputGroup

> A text field with addons joined to it: a fixed text segment, an icon, or a button such as Copy.

Category: Forms. Docs page: https://rime.mjsons.net/components/input-group

## Installation

```bash
npx shadcn@latest add KezLahd/rime/input-group
```

InputGroup wears the TextInput box and its --input-* tokens, with addons set into either end. A string addon ("https://", "$", "kg") is drawn as a sunken text segment divided by a hairline; an element addon (an icon or an IconButton) sits inline.

Focus, invalid, read-only and disabled all draw on the whole group, addons included. The <input> takes every native attribute, and reads its id, required and error state from a surrounding Field.

## Usage

```tsx
import { InputGroup } from "@/components/ui";

<Field label="Website">
  <InputGroup start="https://" placeholder="example.com" />
</Field>
```

## Props: InputGroup

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `start` | `ReactNode` |  | Before the input. A string ("https://", "$") is set off as a sunken text segment with a hairline; an element (an icon, an IconButton) sits inline. |
| `end` | `ReactNode` |  | After the input, the same way: a unit ("kg", ".com") or a button. |
| `size` | `"sm" \| "md"` | `"md"` |  |
| `invalid` | `boolean` |  | Overrides the Field's invalid state. |
| `mono` | `boolean` |  |  |
| `boxClassName` | `string` |  | Class for the outer box rather than the <input>. |

Also accepts the native attributes of: AriaAttributes, DOMAttributes, HTMLAttributes, InputHTMLAttributes.

## Examples

### Text, icon and button addons

```tsx
<InputGroup start="https://" placeholder="example.com" aria-label="Website" />
<InputGroup start={<IconSearch size={16} />} placeholder="Search invoices" aria-label="Search invoices" />
<InputGroup start="$" end="USD" inputMode="decimal" aria-label="Amount" />
<InputGroup readOnly mono defaultValue="sk_live_4f9a2c" aria-label="API key"
  end={<IconButton size="sm" label="Copy key" icon={<Copy size={14} />} />} />
```

### In a Field

```tsx
<Field label="Workspace address" hint="Letters, numbers and dashes.">
  <InputGroup end=".example.app" placeholder="acme" />
</Field>
<Field label="Weight" error="Enter a weight under 30 kg.">
  <InputGroup end="kg" inputMode="decimal" />
</Field>
```

## Theme tokens

Per-component tokens. Undeclared by default: the default below is the fallback the CSS uses. Set any of them at :root or on a wrapper to restyle every instance.

| Token | Default |
| --- | --- |
| `--field-focus-border` | `rgba(var(--rgb-brand), 0.7)` / `var(--focus-color)` |
| `--field-focus-ring` | `var(--focus-halo)` |
| `--field-focus-ring-invalid` | `0 0 0 4px rgba(var(--rgb-danger), 0.14)` |
| `--field-hover-border` | `rgba(var(--rgb-brand-deep), 0.3)` |
| `--input-addon-bg` | `var(--recess-fill)` |
| `--input-bg` | `var(--surface-solid)` |
| `--input-border` | `var(--line-input)` |
| `--input-border-width` | `var(--border-w-field)` |
| `--input-font-size` | `var(--text-input)` |
| `--input-height-md` | `var(--field-h-md)` |
| `--input-height-sm` | `var(--field-h-sm)` |
| `--input-pad-x` | `calc(14px * var(--density))` |
| `--input-radius` | `var(--r-md)` / `var(--r-sm)` |
| `--input-shadow` | `var(--shadow-input)` |

System tokens it reads: `--border-w`, `--border-w-field`, `--danger-base`, `--danger-field-fill`, `--danger-line`, `--density`, `--dur`, `--ease`, `--field-h-md`, `--field-h-sm`, `--focus-color`, `--focus-halo`, `--font-body`, `--font-mono`, `--ink-body`, `--ink-brand`, `--ink-disabled`, `--ink-muted`, `--ink-placeholder`, `--ink-secondary`, `--line`, `--line-input`, `--r-md`, `--r-sm`, `--r-xs`, `--recess-fill`, `--rgb-brand`, `--rgb-brand-deep`, `--rgb-danger`, `--shadow-input`, `--surface-solid`, `--surface-sunken`, `--text-body`, `--text-input`, `--text-small`, `--wash-active`, `--wash-subtle`.

## Accessibility

- Text addons are visual only (aria-hidden). If the unit matters to understanding, say it in the label or hint as well ("Weight in kg").
- Button addons are real buttons with their own accessible names, after the input in tab order.
- Wrap it in <Field label=…> so the label, hint and error are wired through context.

## Do

- Use a text addon for a fixed part the user should not type.
- Use PrefixedInput when the whole value has a fixed format.

## Don't

- Don't put more than one button in an addon.
- Don't style the <input> directly; theme the box with --input-* tokens.

Related: https://rime.mjsons.net/components/text-input, https://rime.mjsons.net/components/prefixed-input, https://rime.mjsons.net/components/field
