Input
Overview
Input components are essential UI elements that allow users to enter and edit text. They come in two main variants: single-line text inputs and multi-line text areas. Each type supports various states including basic, error, custom error messages, and disabled states.
Example Usage
Single-line Text Inputs
Basic Input
The basic text input includes a label, placeholder text, and optional helper text. The width will default to 100%, but can restrained using additional CSS as is done in these examples.
Helper text
Error message
<div class="skin-form input-wrapper w-64 wrapper group">
<div class="input-label-wrapper">
<label class="input-label text-body font-semi-bold" for="basic-input"
>Label
</label
>
<span class="input-status text-sm font-light soft" aria-hidden="true"
>(Optional status)
</span
>
</div>
<div class="input-field-wrapper">
<input
type="text"
class="input-field !text-body placeholder:soft interactive"
id="basic-input"
name="basic-input"
placeholder="Placeholder text"
aria-invalid="false"
aria-describedby="basic-helper basic-status"
aria-errormessage="basic-message"
autocomplete="off"
spellcheck="true"
/>
</div>
<div class="input-helper-wrapper">
<p class="input-helper text-sm soft" id="basic-helper">Helper text
</p>
<div class="skin-error input-error-content">
<svg
class="input-error-icon"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
focusable="false"
>
<path
fill-rule="evenodd"
d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"
clip-rule="evenodd"
/>
</svg>
<p class="input-error text-sm font-bold" id="basic-message" role="alert">
Error message
</p>
</div>
</div>
</div>
Error State
The error state indicates invalid input with red borders and an error message. It can be applied by changing the aria-invalid attribute to true.
Helper text
Error message
<div class="skin-form input-wrapper w-64 wrapper group">
<div class="input-label-wrapper">
<label class="input-label text-body font-semi-bold" for="basic-input"
>Label
</label
>
<span class="input-status text-sm font-light soft" aria-hidden="true"
>(Optional status)
</span
>
</div>
<div class="input-field-wrapper">
<input
type="text"
class="input-field !text-body placeholder:soft interactive"
id="basic-input"
name="basic-input"
placeholder="Placeholder text"
aria-invalid="true"
aria-describedby="basic-helper basic-status"
aria-errormessage="basic-message"
autocomplete="off"
spellcheck="true"
/>
</div>
<div class="input-helper-wrapper wrapper">
<p class="input-helper text-sm soft" id="basic-helper">Helper text
</p>
<div class="skin-error input-error-content">
<svg
class="input-error-icon"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
focusable="false"
>
<path
fill-rule="evenodd"
d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"
clip-rule="evenodd"
/>
</svg>
<p class="input-error text-sm font-bold" id="basic-message" role="alert">
Error message
</p>
</div>
</div>
</div>
Disabled State
The disabled attribute can be applied to the <input/> element to style the whole component to its disabled state.
Helper text
Error message
<div class="skin-form input-wrapper w-64 wrapper group">
<div class="input-label-wrapper">
<label class="input-label text-body font-semi-bold" for="basic-input"
>Label
</label
>
<span class="input-status text-sm font-light soft" aria-hidden="true"
>(Optional status)
</span
>
</div>
<div class="input-field-wrapper">
<input
type="text"
class="input-field !text-body placeholder:soft interactive"
id="basic-input"
name="basic-input"
placeholder="Placeholder text"
aria-invalid="false"
aria-describedby="basic-helper basic-status"
aria-errormessage="basic-message"
autocomplete="off"
spellcheck="true"
disabled
/>
</div>
<div class="input-helper-wrapper wrapper">
<p class="input-helper text-sm soft" id="basic-helper">Helper text
</p>
<div class="skin-error input-error-content">
<svg
class="input-error-icon"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
focusable="false"
>
<path
fill-rule="evenodd"
d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"
clip-rule="evenodd"
/>
</svg>
<p class="input-error text-sm font-bold" id="basic-message" role="alert">
Error message
</p>
</div>
</div>
</div>
Clearable Input
Inputs can include a clear button to quickly remove entered text, although the functionality for this must be added in javascript. The clear button appears when the text input contains text.
Helper text
Error message
<div class="skin-form input-wrapper w-64 wrapper group">
<div class="input-label-wrapper">
<label class="input-label text-body font-semi-bold" for="basic-input"
>Label
</label
>
<span class="input-status text-sm font-light soft" aria-hidden="true"
>(Optional status)
</span
>
</div>
<div class="input-field-wrapper">
<input
type="text"
class="input-field !text-body placeholder:soft interactive"
id="basic-input"
name="basic-input"
placeholder="Placeholder text"
aria-invalid="false"
aria-describedby="basic-helper basic-status"
aria-errormessage="basic-message"
autocomplete="off"
spellcheck="true"
/>
<button class="skin-control input-clear" aria-label="Clear input">
<svg
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
focusable="false"
>
<path
fill-rule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clip-rule="evenodd"
/>
</svg>
</button>
</div>
<div class="input-helper-wrapper">
<p class="input-helper text-sm soft" id="basic-helper">Helper text
</p>
<div class="skin-error input-error-content">
<svg
class="input-error-icon"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
focusable="false"
>
<path
fill-rule="evenodd"
d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"
clip-rule="evenodd"
/>
</svg>
<p class="input-error text-sm font-bold" id="basic-message" role="alert">
Error message
</p>
</div>
</div>
</div>
Text Areas
Basic Text Area
Text areas provide multi-line input capabilities with optional character limits. They behave in the same way as the <input/> above, but use a <textarea/> element instead.
Error message
<div class="skin-form input-wrapper w-120 wrapper group">
<div class="input-label-wrapper">
<label class="input-label text-body font-semi-bold" for="basic-textarea"
>Label
</label
>
<span class="input-status text-sm font-light soft" aria-hidden="true"
>(Optional status)
</span
>
</div>
<div class="input-field-wrapper">
<textarea
class="input-field !text-body placeholder:soft interactive"
id="basic-textarea"
name="basic-textarea"
placeholder="Placeholder text"
aria-invalid="false"
aria-describedby="basic-textarea-error basic-textarea-char-limit"
aria-errormessage="basic-textarea-error"
autocomplete="off"
spellcheck="true"
minlength="0"
maxlength="500"
rows="3"
>
</textarea>
<svg class="resize-handle" viewBox="0 0 16 16" fill="currentColor">
<path
d="M12.9627 2L2 12.9843L3.01375 14L13.9764 3.01575L12.9627 2Z"
fill="#666666"
/>
<path
d="M6.50295 12.9843L12.9862 6.48819L14 7.50394L7.5167 14L6.50295 12.9843Z"
fill="#666666"
/>
<path
d="M11.0766 12.937L12.9862 11.0236L14 12.0394L12.0904 13.9528L11.0766 12.937Z"
fill="#666666"
/>
</svg>
</div>
<div class="input-helper-wrapper">
<div class="input-helper-content">
<div class="skin-error input-error-content">
<svg
class="input-error-icon"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
focusable="false"
>
<path
fill-rule="evenodd"
d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"
clip-rule="evenodd"
/>
</svg>
<p
class="input-error text-sm font-bold"
id="basic-textarea-error"
role="alert"
>
Error message
</p>
</div>
</div>
<p
class="input-char-limit text-sm soft"
id="basic-textarea-char-limit"
aria-hidden="true"
>
0/500
</p>
</div>
</div>
Error State
Text areas support the same error states as single-line inputs. Like in the single line input, this is activated by changing the aria-invalid attribute to true.
Error message
<div class="skin-form input-wrapper w-120 wrapper group">
<div class="input-label-wrapper">
<label class="input-label text-body font-semi-bold" for="basic-textarea"
>Label
</label
>
<span class="input-status text-sm font-light soft" aria-hidden="true"
>(Optional status)
</span
>
</div>
<div class="input-field-wrapper">
<textarea
class="input-field !text-body placeholder:soft interactive"
id="basic-textarea"
name="basic-textarea"
placeholder="Placeholder text"
aria-invalid="true"
aria-describedby="basic-textarea-error basic-textarea-char-limit"
aria-errormessage="basic-textarea-error"
autocomplete="off"
spellcheck="true"
minlength="0"
maxlength="500"
rows="3"
>
</textarea>
<svg class="resize-handle" viewBox="0 0 16 16" fill="currentColor">
<path
d="M12.9627 2L2 12.9843L3.01375 14L13.9764 3.01575L12.9627 2Z"
fill="#666666"
/>
<path
d="M6.50295 12.9843L12.9862 6.48819L14 7.50394L7.5167 14L6.50295 12.9843Z"
fill="#666666"
/>
<path
d="M11.0766 12.937L12.9862 11.0236L14 12.0394L12.0904 13.9528L11.0766 12.937Z"
fill="#666666"
/>
</svg>
</div>
<div class="input-helper-wrapper">
<div class="input-helper-content">
<div class="skin-error input-error-content">
<svg
class="input-error-icon"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
focusable="false"
>
<path
fill-rule="evenodd"
d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"
clip-rule="evenodd"
/>
</svg>
<p
class="input-error text-sm font-bold"
id="basic-textarea-error"
role="alert"
>
Error message
</p>
</div>
</div>
<p
class="input-char-limit text-sm soft"
id="basic-textarea-char-limit"
aria-hidden="true"
>
0/500
</p>
</div>
</div>
Disabled State
Text areas also support the disabled state. Like in the single line input, this is activated by applying the disabled attribute to the <textarea/>.
Error message
<div class="skin-form input-wrapper w-120 wrapper group">
<div class="input-label-wrapper">
<label class="input-label text-body font-semi-bold" for="basic-textarea"
>Label
</label
>
<span class="input-status text-sm font-light soft" aria-hidden="true"
>(Optional status)
</span
>
</div>
<div class="input-field-wrapper">
<textarea
class="input-field !text-body placeholder:soft interactive"
id="basic-textarea"
name="basic-textarea"
placeholder="Placeholder text"
aria-invalid="false"
aria-describedby="basic-textarea-error basic-textarea-char-limit"
aria-errormessage="basic-textarea-error"
autocomplete="off"
spellcheck="true"
minlength="0"
maxlength="500"
rows="3"
disabled
>
</textarea>
<svg class="resize-handle" viewBox="0 0 16 16" fill="currentColor">
<path
d="M12.9627 2L2 12.9843L3.01375 14L13.9764 3.01575L12.9627 2Z"
fill="#666666"
/>
<path
d="M6.50295 12.9843L12.9862 6.48819L14 7.50394L7.5167 14L6.50295 12.9843Z"
fill="#666666"
/>
<path
d="M11.0766 12.937L12.9862 11.0236L14 12.0394L12.0904 13.9528L11.0766 12.937Z"
fill="#666666"
/>
</svg>
</div>
<div class="input-helper-wrapper">
<div class="input-helper-content">
<div class="skin-error input-error-content">
<svg
class="input-error-icon"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
focusable="false"
>
<path
fill-rule="evenodd"
d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"
clip-rule="evenodd"
/>
</svg>
<p
class="input-error text-sm font-bold"
id="basic-textarea-error"
role="alert"
>
Error message
</p>
</div>
</div>
<p
class="input-char-limit text-sm soft"
id="basic-textarea-char-limit"
aria-hidden="true"
>
0/500
</p>
</div>
</div>
CSS Variables
The input system can be customized using the below CSS variables:
| Variable | Default Value | Description |
|---|---|---|
--input-spacing | 0.25rem | Gap between input elements |
--input-padding-x | 0.5rem | Horizontal padding |
--input-padding-y | 0.75rem | Vertical padding |
--input-border-width | 1px | Default border width |
--input-border-width-active | 2px | Border width when active |
--input-border-width-invalid | 2px | Border width in error state |
--input-error-border-color | var(--color-error) | Border color in error state |
--input-radius | var(--radius-site) | Border radius for inputs |
--input-radius-textarea | var(--radius-site) | Border radius for textareas |
Accessibility
The input system is built following WCAG guidelines and WAI-ARIA best practices for form inputs, implementing key accessibility features through semantic HTML and ARIA attributes.
Current Implementation Features
Structure and Labeling
Each input includes:
- Semantic HTML with proper
<label>elements explicitly linked to inputs viaforattributes aria-invalidattribute to communicate validation statearia-describedbyto associate helper text with inputsaria-errormessageto link error messages with inputs- Visible labels that remain visible in all states
- Helper text for additional context
- Error messages that are announced by screen readers via
role="alert"
Visual States
The system provides clear visual indicators for:
- Default state with distinct borders
- Focus state with visible outlines
- Error state with high-contrast borders and background
- Disabled state with muted colors and disabled cursor
- Hover state with enhanced borders
Screen Reader Support
Each input variant includes:
- Clear labeling of all interactive elements
- Helper text linked via
aria-describedby - Error messages linked via
aria-errormessage - Character count updates for text areas using
aria-live="polite" - Clear button with descriptive
aria-label - Error icons hidden from screen readers with
aria-hidden="true"
Keyboard Navigation
The native HTML elements provide:
- Tab key to navigate between inputs
- Space/Enter to activate the clear button
- Arrow keys for cursor movement within inputs
- Standard text editing shortcuts (when implemented by the browser)
Mobile Accessibility
For mobile devices:
- Touch targets meet minimum size requirements
- Clear visual feedback for all states
- Native input behaviors maintained
- Proper viewport zooming supported
- Clear button positioned for easy touch access
JavaScript Functionality
While the components are built to be functional without JavaScript, some features require JavaScript for full accessibility:
Character Counter
For text areas with character limits:
- Update the character count text as the user types
- Announce updates to screen readers via the
aria-liveregion
Clear Button
For inputs with clear functionality:
- Reset focus to the input field after clearing
- Announce the clear action to screen readers
Error Handling
For proper error management:
- Set
aria-invalid="true"when validation fails - Update error messages dynamically
- Focus the first invalid field when form submission fails
- Announce error messages to screen readers
Implementation Guidelines
When implementing inputs in your project, ensure you:
HTML Structure
- Maintain the semantic structure of labels, inputs, and helper text
- Keep error messages in the correct order for screen reader announcement
- Preserve all ARIA attributes and relationships
Error Handling
- Implement client-side validation where appropriate
- Ensure error messages are clear and actionable
- Maintain proper focus management during validation