Text Input
Standard single-line text field with clear button and validation feedback
Prefix & Suffix
Text input with inline prefix and suffix labels inside the form control
Phone Number with Country Code
Compound input using a prefix-slot dropmenu as a value picker. The dropmenu's data-select-name opt-in captures the selected country code into a hidden input that ships with the form, and updates the trigger label to a compact display variant. Add nds-phone to the input itself to strip non-digits and any leading zero as the user types, since the country code is already prepended via the prefix slot.
Number Input
Numeric field with increment and decrement buttons for precise value adjustments
Search Input
Search field with leading icon, voice input, and clear button
Email Input
Email field with leading icon and built-in format validation
Password Input
Password field with visibility toggle and Arabic character filtering
Textarea
Multi-line text entry for longer content like messages and descriptions
Select Dropdown
Custom dropdown select with keyboard navigation and hidden input for form submission
Native Select
Standard <select> element wrapped in the form container for native mobile picker behavior
Form Status API
Set error, success, warning, or info status on any form container with automatic feedback display
Loading State
Flip data-state="loading" on any form-container / form-group / form-control (via NDS.State.add) and forms handles the visual UX: auto-creates a [data-loading-slot] spinner in .nds-form-action (creating the slot if missing), hides sibling action buttons, and stamps data-state="loading" on the shell — .nds-btn's CSS renders the spinner. On exit the siblings restore; .nds-clear re-computes from the current input value. Zero markup burden — same UX for remote validation, autocomplete, custom fetch, anything
Permanent Feedback
Tips and hints that persist across validation cycles, hidden during errors and restored when cleared
Built-in Features
What you get out of the box with zero configuration
All form inputs auto-initialize on page load. Two-way state binding syncs disabled and data-required between inputs and containers. For dynamic content, call NDS.Forms.init().
Uses HTML5 validity checks but replaces browser popups with styled inline feedback. Error messages auto-clear on blur or change once the field is corrected. Custom messages via data-error-message.
Custom components hook into submit validation by stamping setCustomValidity('message') on their input (empty string when valid) — validateForm blocks the submit and shows the stamped message as-is, no forms changes needed.
Four status types: error, success, warning, info. Feedback messages render into data-feedback-target elements. Permanent hints survive validation cycles with data-permanent.
Focus, active, typing, filled, and disabled states are tracked automatically via data-state. Clear buttons auto-show when the input has a value and hide when empty.
Add a button with nds-toggle-password class to toggle between password and text input. The icon updates automatically to show the current visibility state.
Add a button with nds-voice-input class to enable speech-to-text on any input. Uses the Web Speech API with automatic language detection from the page.
Password fields automatically strip Arabic and RTL characters on input, ensuring passwords contain only compatible characters without manual validation logic.
Add nds-number-increment and nds-number-decrement buttons as prefix/suffix actions. Supports min, max, and step attributes, with accelerated stepping on long press.
Usage Guidelines
When and how to use form inputs effectively
Best Practices
- Use form inputs for any data collection scenario: registration flows, search interfaces, settings pages, and inline editing. Every input type shares the same container structure, so switching between text, email, password, or select requires minimal markup changes.
- Use
nds-search-inputfor search fields where voice input and automatic clear actions improve discoverability. The search variant includes a leading icon and optional microphone button out of the box. - Use the custom select dropdown (
nds-select) for styled option lists that match the design system — it ships with keyboard navigation and a hidden input so it submits with the form. For native mobile picker behavior, wrap a standard<select>in the samends-form-container nds-selectshell as shown in the Native Select demo. - Do not use form inputs for binary choices or toggles. Use switches for instant on/off toggles, checkboxes for multiple selections, or radio buttons for single selection from a set.
- Do not use plain text inputs for specialized data types. Use date picker for dates, OTP input for verification codes, and autocomplete for search-as-you-type with remote data.
- Choose
nds-lg(default) for standalone forms and primary data entry. Usends-mdfor compact layouts like table filters, inline editing, or sidebars where space is limited. - Add
data-requiredto the container rather thanrequiredon the input. The system syncs the two automatically and adds the required indicator to the label. - Use permanent feedback (
data-permanent) for format hints and character requirements. These persist across validation cycles: they hide when an error appears and restore when the error clears. - Use prefix and suffix slots for units, currency symbols, or action buttons that contextualize the input. Prefix/suffix buttons support both subtle and solid styles, and can include icons or dropmenus for compound inputs.
- For compound inputs (phone + country code, amount + currency, measurement + unit), use a prefix-slot dropmenu with
data-select-name. The dropmenu renders a hidden input that submits with the form and keeps the trigger label compact while the dropdown rows stay descriptive. See the Phone Number with Country Code demo for the full pattern. - Always include a
data-feedback-targetelement in the form footer, even if initially hidden. The validation system needs this target to inject error, success, and warning messages. - Set
data-error-messageon the container to override default browser validation messages with context-specific text that guides the user toward correction.
Modifier Classes
| Class | Element | Description |
|---|---|---|
nds-md | nds-form-container | Medium size with reduced height (32px) and smaller font |
nds-lighter | nds-form-container | Filled-background variant using the lighter surface token. Removes the input border so the fill alone defines the field boundary. Not applied to file upload containers. |
nds-darker | nds-form-container | Filled-background variant using the darker surface token. Same border behavior as nds-lighter. Not applied to file upload containers. |
nds-rowView | nds-form-group | Lays out checkbox, radio, or switch group items in a horizontal wrapping row instead of the default vertical column. |
nds-phone | nds-input (tel) | Set on a tel input that sits next to a country-code prefix slot. On every keystroke, strips non-digit characters and any leading zero so the value submitted is the local number ready to concatenate with the prefix. Length is still controlled by the input's maxlength. |
Data Attributes
| Attribute | Description |
|---|---|
data-required | Set on nds-form-container to mark the field as required. Automatically syncs to the input's required attribute and adds the asterisk indicator. |
data-error-message | Set on nds-form-container to override the default browser validation message with custom text. |
data-permanent | Set on a feedback element inside the footer. Permanent feedback hides during validation errors and restores when cleared. |
min | Set on number inputs to define the minimum allowed value (default: 0). |
max | Set on number inputs to define the maximum allowed value (default: 1000). |
step | Set on number inputs to define the increment/decrement size (default: 1). Long press accelerates to 10x this value. |
data-select-name | Set on an nds-dropmenu (including prefix/suffix dropmenus) to turn it into a value picker. Renders a hidden <input> with this name; item clicks write the item's data-value into it and update the trigger label. |
data-select-value | Set on a select-mode dropmenu to pre-select the item whose data-value matches. Takes precedence over any pre-rendered data-state="selected" item. |
data-required (on dropmenu) | Set on a select-mode dropmenu to add required to the hidden input, so native form submission blocks when nothing is picked. |
data-trigger-label | Set on an nds-dropmenu-item to override the trigger label text when that item is chosen. Useful when the dropdown row is descriptive (Saudi Arabia (+966)) but the trigger slot needs something compact (+966). |
data-min-checked | Set on nds-form-group (checkbox groups). The group is invalid unless at least this many checkboxes are checked. Also adds the required asterisk to the group label. |
data-max-checked | Set on nds-form-group (checkbox groups). The group is invalid when more than this many checkboxes are checked. Defaults to the total checkbox count when absent. |
data-ajax | Set on nds-form. When present, the form's submit event is intercepted and preventDefault() is called after validation passes, allowing you to handle submission via fetch or XHR without a page reload. |
CSS Custom Properties
| Property | Default | Description |
|---|---|---|
--form-width | 100% | Controls the width of the form container |
--input-size | 40px | Height of the input field (32px for nds-md) |
--input-radius | var(--radius-sm) | Border radius of the input field |
--nds-input-size | 16px | Size (width and height) of the checkbox and radio control element. Size modifier classes set it to 20px (nds-md) or 24px (nds-lg). Set directly to use a custom size. |
JavaScript API
The NDS.Forms API manages initialization, status feedback, state management, and form validation. For dynamically added form elements, call NDS.Forms.initializeContainer(element) to initialize new inputs.