Overview
Form text controls stack as .input (label + field + hint), .field (bordered control), and optional .field-group tabs for defined widths. Tokens --field-* set default sizing.
| Layer | Source | Role |
|---|---|---|
| Label | scss/components/_label.scss | .label, required asterisk, tooltip |
| Field | scss/components/_input.scss | .field, states, icons, password |
| Input block | scss/components/_input.scss | .input, .input__hint |
Native select | scss/components/_input.scss | select.field__input inside .field |
| Textarea | scss/components/_textarea.scss | .textarea-wrap, counter |
| Semantics | tokens/semantic.css | Borders, surfaces, --text-error |
Prerequisites
- PimentCSS installed, Installation (input components ship in the default bundle).
- Semantic colors, error and success hues from Colors.
- Layout, optional width utilities; default max width is
--field-width(17.5rem).
Anatomy
Prefer the .input wrapper so label, control, and hint share a 4px gap (--space-1). The native element sits in .field__input without its own border.
<div class="input">
<label class="label" for="x"><span class="label__text">Label</span></label>
<div class="field">
<input class="field__input" id="x" type="text">
</div>
<p class="input__hint">Caption</p>
</div>
Labels
Required fields show an asterisk with aria-hidden="true" on the glyph (announce requirement in the label text or with aria-required on the input). Optional fields may use .label__optional. Tooltip buttons need an explicit aria-label.
<label class="label" for="name"><span class="label__required" aria-hidden="true">*</span><span class="label__text">Full name</span></label>
Field states
States map to modifiers on .field. In production, rely on :hover, :focus-within, and validation attributes. Each variant has its own preview below (responsive grid, no nested scroll).
Empty
Placeholder only, no value.
Filled
A value is already entered.
With icon
Leading icon (search) with placeholder.
State previews use visible <label for> + matching id on each input.
| Modifier | When to use |
|---|---|
.field--active | Doc / forced active border (--border-action-hover) |
.field--focus | Doc preview of focus ring (prefer :focus-within) |
.field--error | Validation failed; pair with aria-invalid="true" |
.field--success | Valid entry confirmed |
.field--disabled | Disabled input inside field |
Input block (label + hint)
Stack label, field, and caption. Error hints use .input__hint--error and --text-error.
Help text below the field (caption).
This value is not valid.
<div class="input">
<label class="label" for="email"><span class="label__required" aria-hidden="true">*</span><span class="label__text">Email</span></label>
<div class="field">
<input class="field__input" id="email" type="email" name="email" autocomplete="email">
</div>
<p class="input__hint" id="email-hint">We never share your email.</p>
</div>
<div class="input">
<label class="label" for="email-err"><span class="label__text">Email</span></label>
<div class="field field--error">
<input class="field__input" id="email-err" type="email" aria-invalid="true" aria-describedby="email-err-hint">
</div>
<p class="input__hint input__hint--error" id="email-err-hint">Enter a valid email address.</p>
</div>
Password
Add .password-toggle inside .field. Toggle updates type, button label, and aria-pressed (wired on this docs site).
<div class="input">
<label class="label" for="pwd"><span class="label__text">Password</span></label>
<div class="field">
<input class="field__input" id="pwd" type="password" autocomplete="current-password">
<button type="button" class="password-toggle focus-visible" aria-pressed="false">Show</button>
</div>
</div>
Native select
Place a native <select> inside .field with class field__input. A chevron is drawn on the field (icon font). For rich listboxes, use Menu & dropdown.
<div class="field">
<select class="field__input" id="country" name="country">
<option value="">Country</option>
<option value="fr">France</option>
</select>
</div>
Textarea
Long text uses .textarea-wrap with optional instructions and a live counter (aria-live="polite").
Instructions for the long field.
0 / 200
<div class="textarea-wrap">
<label class="label" for="bio"><span class="label__text">Bio</span></label>
<div class="textarea-field">
<textarea class="textarea focus-visible" id="bio" rows="5" maxlength="200" aria-describedby="bio-count"></textarea>
</div>
<p class="textarea-wrap__count" id="bio-count" aria-live="polite">0 / 200</p>
</div>
Defined field (prefix / suffix)
Wide controls (--field-width-defined, 26.875rem) use .input--defined and .field-group with pre/post tabs. Active tab uses --surface-action.
<div class="input input--defined">
<label class="label" for="url"><span class="label__text">Website</span></label>
<div class="field-group">
<span class="field-group__tab field-group__tab--pre field-group__tab--active">https://</span>
<div class="field field--defined">
<input class="field__input" id="url" type="text">
</div>
</div>
</div>
Class reference
Optional namespace via Sass $prefix (empty by default).
| Class | Description |
|---|---|
.label | Caption row; pair with for + input id |
.label__required | Visual asterisk (aria-hidden) |
.label__optional | Muted “(optional)” suffix |
.label__tooltip | Icon button; requires aria-label |
.field | Bordered control shell; :focus-within ring |
.field__input | Native input (no border) |
.field__icon | Leading icon slot (aria-hidden) |
.field--error / --success | Validation borders |
.input | Vertical stack: label + field + hint |
.input__hint | Caption; --error / --success variants |
.password-toggle | Show/hide password control |
.textarea-wrap | Textarea stack with hint and counter |
.textarea | Native textarea inside .textarea-field |
.field-group | Prefix/suffix tabs + field |
.field-group__tab--active | Highlighted pre/post segment |
Customize (Sass)
-
Field sizing tokens
Override widths and padding when importing PimentCSS. Values feed--field-*CSS variables.@use "pimentcss-design-system" with ( $field-width: 20rem, $field-width-defined: 28rem, $field-min-height: 2.75rem, ); -
Rebuild CSS
Regeneratedist/pimentcss.min.cssafter Sass changes.npm run build:css
See Customization for partial imports.
Accessibility (RGAA / WCAG)
Forms without barriers
- Labels, every input has a visible label and matching
id. Do not use placeholder as the only label. - Errors, set
aria-invalid="true"and link the message witharia-describedby(hint paragraphid). - Required, use
requiredoraria-required="true"plus text or asterisk explained in copy. - Focus, keyboard users get
:focus-withinring on.field/.textarea-field(--border-focus, 3px). - Password toggle, update
aria-pressedwhen visibility changes; keep button text meaningful (Show / Hide). - Contrast, body and hint text on
--surface-primary≥ 4.5:1; verify in light and dark (Colors).