# Input

Package: @commenda/pathfinder@0.1.0
Page: /docs/components/input
Figma: https://www.figma.com/design/dn7BkGYEfIyP0o2zfesfVh/The-System?node-id=68-2

```tsx
import { Input } from '@commenda/pathfinder/input';

export const InputDefault = () => (
  <Input
    label="Entity name"
    placeholder="e.g. Meridian UK Ltd"
    description="Use the legal name on the certificate of incorporation."
  />
);
```


## Installation [#installation]

Import the component in your screen:

```tsx
import { Input } from '@commenda/pathfinder/input';
```

Load the stylesheet once in your app root, after host styles:

```tsx
import '@commenda/pathfinder/styles.css';
```

Pathfinder ships precompiled CSS; no Tailwind setup is required. In Next.js App Router, put interactive state and callbacks in a client component. See [installation](/docs/getting-started/installation) for the workspace setup.

## API reference [#api-reference]

| Prop | Type | Requirement | Behavior |
| --- | --- | --- | --- |
| `type` | `"password" \| "number" \| "search" \| "text" \| "email" \| "url" \| "tel"` | conditional | text |
| `leadingIcon` | `ReactElement<any, string \| React.JSXElementConstructor<any>>` | optional | Decorative 16px slot |
| `prefix` | `string` | optional | Non-editable unit segments |
| `suffix` | `string` | optional | Non-editable unit segments |
| `actionConfig` | `FieldActionConfig \| CopyAction \| { kind: "clear"; label?: string; }` | optional | Reveal is password-only; copy supports onCopyError, reveal supports hideLabel |
| `label` | `string` | conditional | Visible field label. Supply an accessible name when it is omitted. |
| `hideLabel` | `boolean` | optional | Visually hide the label while preserving its accessible name. |
| `aria-label` | `string` | conditional | Accessible name when the field has no visible label. |
| `required` | `boolean` | conditional | Mark the field as required. See validation guidance for the component. |
| `labelConfig` | `{ optionalLabel: string; help?: never; } \| { help: string; optionalLabel?: never; }` | optional | Choose either `optionalLabel` or `help`, never both. `never` excludes the other field. |
| `description` | `string` | optional | Supporting guidance associated with the field. |
| `errorConfig` | `{ message: string; reveal?: "after-blur" \| "immediate"; }` | optional | Validation message and when it becomes visible. |
| `disableConfig` | `DisableConfig` | optional | Disabled state with an optional explanation. |
| `value` | `string` | conditional |  |
| `onValueChange` | `((value: string) => void)` | conditional |  |
| `defaultValue` | `string` | optional |  |

### actionConfig fields

Choose one shape; excluded fields cannot be combined.

#### Option 1

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `kind` | `"copy"` | Yes |  |
| `label` | `string` | No |  |
| `onCopyError` | `((error: Error) => void)` | No |  |

#### Option 2

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `kind` | `"clear"` | Yes |  |
| `label` | `string` | No |  |

#### Option 3

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `kind` | `"reveal"` | Yes |  |
| `label` | `string` | No |  |
| `hideLabel` | `string` | No |  |

### labelConfig fields

Choose one shape; excluded fields cannot be combined.

#### Option 1

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `optionalLabel` | `string` | Yes |  |

Do not supply: `help`.

#### Option 2

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `help` | `string` | Yes |  |

Do not supply: `optionalLabel`.

### errorConfig fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `message` | `string` | Yes |  |
| `reveal` | `"after-blur" \| "immediate"` | No |  |

### disableConfig fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `isDisabled` | `boolean` | Yes |  |
| `reason` | `string` | No |  |

## Shared field props [#shared-field-props]

| Prop            | Type                                                | Behavior                                                     |
| --------------- | --------------------------------------------------- | ------------------------------------------------------------ |
| `label`         | `string`                                            | Visible accessible label; hideLabel hides it visually.       |
| `aria-label`    | `string`                                            | Accessible name when a visible label is not supplied.        |
| `value`         | `string`                                            | Controlled value; provide the corresponding change callback. |
| `defaultValue`  | `string`                                            | Initial value for an uncontrolled control.                   |
| `onValueChange` | `(value: string) => void`                           | Immediate editing changes; no implicit parsing.              |
| `description`   | `string`                                            | Visible supporting guidance.                                 |
| `required`      | `boolean`                                           | Native required semantics and visual asterisk.               |
| `labelConfig`   | `{ optionalLabel }` or `{ help }`                   | One annotation; cannot combine with required.                |
| `errorConfig`   | `{ message, reveal?: 'after-blur' \| 'immediate' }` | Replaces guidance after blur by default.                     |
| `disableConfig` | `{ isDisabled, reason? }`                           | Disables editing; reason stays visible.                      |
| `readOnly`      | `boolean`                                           | Preserves focus, selection, and form submission.             |

Native `name`, `form`, autocomplete, focus/blur handlers, constraints, IDs, ARIA attributes, and typed refs are supported. `className` styles the outer field. Use `onValueChange` for text changes. Form reset restores uncontrolled defaults; controlled values remain application-owned.

## Usage rules [#usage-rules]

Use noun labels in sentence case, examples as placeholders, and actionable error messages. One authored 28px size with radius 6. Copy remains available when editing is disabled. Clear never mutates disabled/read-only input. Password reveal changes visibility without changing the value. Numeric input retains native string editing; currency formatting is not included.

### Segments [#segments]


```tsx
import { Input } from '@commenda/pathfinder/input';

export const InputSegments = () => (
  <Input
    label="Annual revenue"
    prefix="$"
    suffix="USD"
    inputMode="decimal"
    placeholder="e.g. 250000"
  />
);
```


### Actions [#actions]


```tsx
import { Input } from '@commenda/pathfinder/input';

export const InputActions = () => (
  <div style={{ display: 'grid', gap: 20 }}>
    <Input
      label="Entity name"
      defaultValue="Meridian UK Ltd"
      actionConfig={{ kind: 'clear' }}
    />
    <Input
      label="Registration number"
      defaultValue="GB123456"
      readOnly
      actionConfig={{ kind: 'copy' }}
    />
    <Input
      label="Password"
      type="password"
      autoComplete="new-password"
      actionConfig={{ kind: 'reveal' }}
    />
  </div>
);
```


### States [#states]


```tsx
import { Input } from '@commenda/pathfinder/input';

export const InputStates = () => (
  <div style={{ display: 'grid', gap: 20 }}>
    <Input label="Required name" required />
    <Input label="Trading name" labelConfig={{ optionalLabel: '(optional)' }} />
    <Input
      label="Jurisdiction"
      labelConfig={{ help: 'The country in which this entity is registered.' }}
    />
    <Input
      label="Entity name"
      errorConfig={{
        message: 'Enter the registered legal name.',
        reveal: 'immediate',
      }}
    />
    <Input
      label="Registration number"
      defaultValue="GB123456"
      disableConfig={{
        isDisabled: true,
        reason: 'Managed by your administrator.',
      }}
      actionConfig={{ kind: 'copy' }}
    />
  </div>
);
```


## Design rules [#design-rules]

Figma guidance and reviewed implementation decisions for this component. Rule IDs remain stable for source comments and AI references.

### Source rules [#source-rules]

| Rule      | Source           | Contract                                                                      |
| --------- | ---------------- | ----------------------------------------------------------------------------- |
| INPUT-001 | 68:225, 158:222  | 28px field, radius 6, pearl control border, blue focus glow.                  |
| INPUT-002 | 158:318, 158:819 | One label annotation; units use prefix/suffix, actions use their own segment. |
| INPUT-003 | 158:807, 158:816 | Sentence-case noun label; actionable error replaces guidance after blur.      |
| INPUT-004 | 158:482          | Copy remains available for disabled values.                                   |

All source IDs refer to [The System](https://www.figma.com/design/dn7BkGYEfIyP0o2zfesfVh/The-System?node-id=68-2).

### Explicit implementation decisions [#explicit-implementation-decisions]

True native readOnly remains distinct from disabled despite Figma 158:822 using disabled appearance for non-editable fields. Disabled values do not submit; read-only values do. Disabled editing does not disable the independent copy/reveal actions. Clear cannot modify a disabled/read-only field.

Error reveal defaults to after-blur; immediate reveal supports submission/server errors. Soft-limit feedback is immediate. Applications own validation schemas and server error clearing. Additional required/help guidance belongs in description when the single label annotation is already occupied.

Optional-label text uses pearl 800 instead of the authored pearl 700: browser axe measured the latter at 3.38:1 on white, below the 4.5:1 requirement for this text.
