Input
FreeThe foundational single-line text input for forms. Supports placeholder text, prefix/suffix slots, and validation states (default, error, disabled). Pair with a visible label and optional helper or error text.
16 variants
Variants
| Variant | Description | When to use |
|---|---|---|
| Default | Standard text input with border and placeholder. | Use for general-purpose single-line text entry — names, emails, URLs. |
| With Icon | ||
| With Addon | ||
| Error | Input with red border and error message below. | Use when validation fails and the user needs to correct the value. |
Usage Guidelines
Do
Always associate a visible <label> with the input via htmlFor/id.
Don't
Use placeholder text as the only label — it disappears on focus.
Do
Provide placeholder text as a hint, not a replacement for the label.
Don't
Disable the input without explaining why.
Do
Show error messages below the input with aria-describedby linkage.
Don't
Rely solely on colour to indicate error state — include an error message.
Do
Use appropriate input types (email, url, tel) for built-in validation.
Don't
Code
HTML
<div class="lex-field">
<label class="lex-label" for="email-input">Email address</label>
<input
id="email-input"
class="lex-input"
type="email"
placeholder="you@example.com"
aria-required="true"
/>
<span class="lex-field__helper">We'll never share your email.</span>
</div>
<div class="lex-field lex-field--error">
<label class="lex-label" for="email-error">Email address</label>
<input
id="email-error"
class="lex-input lex-input--error"
type="email"
value="not-an-email"
aria-invalid="true"
aria-describedby="email-error-msg"
aria-required="true"
/>
<span class="lex-field__error" id="email-error-msg">
Please enter a valid email address.
</span>
</div>CSS Custom Properties
.lex-input {
width: 100%;
height: var(--lex-input-height, 40px);
padding: 0 var(--lex-input-padding, 12px);
background: var(--lex-input-bg);
border: 1px solid var(--lex-input-border);
border-radius: var(--lex-input-radius, var(--lex-radius-md));
color: var(--lex-input-text);
font-size: var(--lex-font-size-sm);
transition: border-color 150ms ease, box-shadow 150ms ease;
}
.lex-input::placeholder {
color: var(--lex-input-placeholder);
}
.lex-input:focus {
outline: none;
border-color: var(--lex-input-border-focus);
box-shadow: 0 0 0 2px var(--lex-input-ring);
}
.lex-input--error {
border-color: var(--lex-input-border-error);
}
.lex-input--error:focus {
box-shadow: 0 0 0 2px var(--lex-input-ring-error);
}
.lex-input:disabled {
background: var(--lex-input-bg-disabled);
color: var(--lex-input-text-disabled);
cursor: not-allowed;
}React
// Using Lexicon CSS classes with React
export function EmailField() {
return (
<div className="lex-field">
<label className="lex-label" htmlFor="email">Email address</label>
<input
className="lex-input lex-input--md"
id="email"
type="email"
placeholder="you@example.com"
required
/>
<p className="lex-helper-text">We'll never share your email.</p>
</div>
);
}Design Tokens
| Token | Value (dark) | Value (light) | CSS property |
|---|---|---|---|
| --lex-input-bg | — | — | bg |
| --lex-input-border | — | — | border |
| --lex-input-border-focus | — | — | focus |
| --lex-input-radius | — | — | radius |
| --lex-input-padding | — | — | padding |
| --lex-input-font-size | — | — | size |
| --lex-input-placeholder-color | — | — | color |
Accessibility
- Every input must have an associated <label> using htmlFor/id or wrapping the input.
- Error state uses aria-invalid="true" and aria-describedby pointing to the error message element.
- Required fields use aria-required="true" in addition to the required attribute.
- Focus ring must be visible with at least 3:1 contrast against the background.
- Placeholder text must not be the sole label — it is not reliably announced by all screen readers.