Single-line text entry with labels, hints and honest validation.
| Prop | Type | Default | Description |
|---|---|---|---|
| value / defaultValue | string | — | Controlled or uncontrolled content |
| type | text · email · password · number… | text | Native input type |
| label | string | — | Linked by htmlFor/id; always visible |
| hint | string | — | Helper text below the field |
| status | error · success | — | Validation border + message colour |
| icon / affix | ReactNode / string | — | Leading glyph or trailing adornment |
| size | l · m · s | m | 44 / 36 / 28px |
| disabled / readOnly | boolean | false | Blocks editing; read-only keeps focus |
| Event | Payload | Fires when |
|---|---|---|
| onChange | ChangeEvent | Every keystroke, paste and cut |
| onFocus / onBlur | FocusEvent | Focus enters / leaves; validate on blur |
| Enter | — | Submits the surrounding form |
| aria-invalid | boolean | Set true whenever status is error |
Keystrokes are debounced 500ms; only the last one hits the network. A stale reply never overwrites a fresher one.
Fully controlled: the count and the limit warning react to every keystroke, never lagging the value.
Keep the label visible above the field. It stays legible once the field is filled.
Don't rely on the placeholder as the label. It vanishes the moment someone types.
Say what is wrong and how to fix it, in plain words, right under the field.
Never make the reader decode a status code. The field is not a server log.
Stacked fields, labels above, consistent width. One idea per row.
Leading icon, no label needed. The placeholder carries the intent here.
Units and adornments sit inside as an affix, never in the value.
| Token | Value | Role |
|---|---|---|
| --in-height-l/m/s | 44 / 36 / 28px | Matches the button scale |
| --in-radius | 8px | 6px at size S |
| --in-bg | #0E0F12 | Inset field surface |
| --in-border | #242428 | Resting border |
| --in-text | #E7E9EE | Entered value |
| --in-placeholder | #565A62 | Placeholder hint |
| --in-focus | rgba(255,122,0,.6) + .14 glow | Warm ring on focus |
| --in-error | #EB4A4F | Invalid border & message |
| --in-success | #1FA35C | Valid border & message |
| --in-pad-icon | 38px | Left pad with a leading icon |
<label class="o-field"> <span class="o-field__label">Work email</span> <input class="o-input o-input--m" type="email" placeholder="you@company.com" /> <span class="o-field__hint">We only use it to send receipts.</span> </label> <input class="o-input o-input--m is-error" aria-invalid="true" />