Input
Inputs capture a single line of free-text or numeric data.
Components · Form
An input captures a single value typed by the user — text, a number, a date, a currency amount, a color. Namirial inputs are built from --nm-input-* tokens defined in namirial-theme.css, ship in five sizes, and share the same .nm-input__label and .nm-input__help parts used by select and search.
Overview
An input is the default control for collecting a single line of free-form text or data, such as a name, email address, amount, or date. Always pair it with a clear label. When a specific format is expected, provide helpful guidance before users enter their information.
When to use
Use an input when:
- You need to collect a single line of free-form data and the range of valid responses is too broad to present as a predefined list.
- The input is part of a composite field, paired with a unit, symbol, or another control (for example, a currency selector or measurement unit).
Consider another component when:
- Users need to choose from a fixed set of options (use Select, Radio buttons, or Checkboxes).
- Users need to enter longer or multi-line content (use Textarea).
- The value requires a specialised input pattern or interaction (e.g. Code, Search).
Required fields
Do not indicate required fields with an asterisk (*). Use the label “(required)” instead.
If all or most fields in a form are mandatory, indicate this at the beginning of the form by using one of the following statements:
- “All fields are required.”
- “All fields are required unless otherwise indicated.”
In forms where only some fields are optional, mark those fields with “(optional)”.
Anatomy
An input is a label, a bordered container, and an optional help text — three independently-styled parts, each with its own token group. Icons and prefixes live inside the container, flanking the field.
Tokens and values that drive each part
| Part | Role | Source |
|---|---|---|
| Label | Text color | --nm-input-label-fg |
| Required mark | Color | --nm-input-label-required |
| Optional mark | Color | --nm-input-label-optional |
| Container | Fill | --nm-input-bg |
| Container | Fill on hover | --nm-input-bg-hover |
| Container | Fill, read-only | --nm-input-bg-readonly |
| Container | Border | --nm-input-border |
| Container | Radius | border-radius — 6 / 10 / 12 / 12 px (xs → xl) |
| Field | Text color | --nm-input-text |
| Field | Placeholder color | --nm-input-placeholder |
| Leading / trailing icon | Color | --nm-input-text — neutral, unaffected by validation intent |
| Status icon | Color | --nm-input-icon — recolored by validation intent |
| Help text | Color | --nm-input-help-fg |
| Focus ring | Color | --nm-input-focus-ring, box-shadow 2px, no offset |
Sizes
Input ships in five sizes, matching button's xs–xl scale. Match the size to the density of the surrounding form.
Choosing a size
xs— dense tables, inline editing, filter barssm— default for forms and dialogsmd— standard content forms, cardslg— prominent single-field forms, onboardingxl— signing surfaces, marketing lead-capture
Content types
The field between the label and the help text adapts to the kind of value it captures. All variants share the same .nm-input__container; only the contents change.
Default
Icon left / Icon right
Use .nm-input__icon-leading for an icon that identifies the field's format (an envelope for email). Use .nm-input__icon-trailing for a secondary content icon, like a chevron for select. Both always render in the same neutral color as the field's own text — they identify or decorate, they don't report validity. For that, use the dedicated .nm-input__icon-status (see Interaction & validation states) — it's the only icon element that changes color with nm-input--error / nm-input--warning / nm-input--success, and it can appear alongside a leading or trailing content icon rather than replacing it.
Clear
A trailing icon becomes an actual clear action once the field has a value — render it as a <button>, not a decorative icon.
Label
.nm-input__label accepts an optional required or optional marker. Mark a field required only when the user must actively provide it — most fields don't need either marker.
Bare · required · optional
Pattern examples
Common recipes built by composing the same .nm-input parts differently — nothing here is a new component.
Currency / Percentage
Currency and percentage both render their affix as an icon rather than literal text — .nm-input__icon-leading for the prefix, .nm-input__icon-trailing for the suffix — so they pick up the same per-size icon dimensions as every other icon in the system, with no divider or boxed compartment involved.
Color picker
.nm-input__color-swatch renders the current value; pair it with a visually-hidden native <input type="color"> (.nm-input__color-native) so the platform color picker still opens on click.
Date
Native <input type="date"> is restyled to hide the default calendar icon so a leading calendar icon from the system's icon set can take its place.
A content icon (like calendar) always stays neutral — it identifies the field's format, not its validity. A validation icon uses the separate .nm-input__icon-status slot, which is the only icon element recolored by nm-input--error / nm-input--warning / nm-input--success. Both slots can be present in the same container at once.
Interaction & validation states
Every input responds to hover and focus, can be locked read-only, and carries three validation intents that recolor the border, help text, and required mark together.
Default → hover → focus → read-only
Error · warning · success — nm-input--error, nm-input--warning, nm-input--success
No disabled state
Like every Namirial control, inputs never ship a browser-default disabled look. Use read-only (nm-input--readonly) when a value can't change right now but should still be visible and still submit with the form.
Focus
Focus is rendered as a 2px box-shadow ring in --nm-input-focus-ring, not an outline — it sits flush against the container instead of floating outside it. It triggers on :focus-within, so clicking anywhere in the container (including a leading icon) focuses the field.
Light & dark theme
Input tokens are remapped under .dark. Contrast and hover directionality are handled for you.
Both previews are locked — they don't follow the page theme switcher.
Do & don't
Accessibility
Semantics
- Always associate the label with the field — wrap both in
.nm-inputand usefor/id, or nest the<input>inside the<label>. - Use the correct
type(email,tel,number,date) so mobile keyboards and browser validation match the data.
Help & error text
- Connect help and error text to the field with
aria-describedbyso screen readers announce it alongside the label. - On error, add
aria-invalid="true"to the<input>in addition to thenm-input--errorclass — the class is visual only.
Keyboard
- Tab moves focus into the field; all native text-editing shortcuts work unmodified.
- Focus uses
:focus-withinon the container, so clicking a leading/trailing icon also focuses the field.
Labeling
- Icon-only trailing actions (like Clear) must be a real
<button>with anaria-label, never a bare icon with a click handler. - Never rely on the border color alone to communicate validation state — pair it with visible help text.
Playground
Token reference
| Token | Role |
|---|---|
| --nm-input-bg | Container fill, default |
| --nm-input-bg-hover | Container fill, hover |
| --nm-input-bg-readonly | Container fill, read-only |
| --nm-input-border | Container border — recolored by --error / --warning / --success |
| --nm-input-focus-ring | Focus box-shadow color |
| --nm-input-text | Field value color |
| --nm-input-placeholder | Placeholder color |
| --nm-input-icon | Status icon color — recolored by validation intent |
| --nm-input-label-fg | Label text color |
| --nm-input-label-required | Required mark color — recolored by validation intent |
| --nm-input-label-optional | Optional mark color |
| --nm-input-help-fg | Help text color — recolored by validation intent |
Implementation
No JavaScript is required for a plain input — it's native HTML styled by CSS classes.
<link rel="stylesheet" href="namirial-theme.css">
<div class="nm-input nm-input--md">
<label class="nm-input__label">
Email address
<span class="nm-input__required">(required)</span>
</label>
<div class="nm-input__container">
<input class="nm-input__field" placeholder="you@company.com">
</div>
<span class="nm-input__help">We'll never share your email.</span>
</div>CSS classes
| Class | Values | Default |
|---|---|---|
nm-input--{size} | xs · sm · md · lg · xl | sm |
nm-input--{intent} | error · warning · success | — |
nm-input--readonly | Locks the container against edits | — |
nm-input__container--combo | Splits the container into a fixed compartment and the field, separated by nm-input__combo-divider | — |
nm-input__icon-leading / nm-input__icon-trailing | Positions a neutral content icon inside the container | — |
nm-input__icon-status | Positions the validation icon — the only one recolored by nm-input--error / nm-input--warning / nm-input--success; can coexist with a leading/trailing icon | — |
nm-input__required / nm-input__optional | Marks the label | — |