Text Fields - National Design System

Text, number, search, email, password, textarea, and select inputs with validation, status feedback, and interactive controls

Text Input

Standard single-line text field with clear button and validation feedback

<div class="nds-form-container"> <div class="nds-form-header"> <label for="text-input-1"> <span class="nds-label">Full Name</span> </label> </div> <div class="nds-form-control"> <input type="text" id="text-input-1" class="nds-input" placeholder="Enter your full name..."> <div class="nds-form-action"> <button class="nds-btn nds-subtle nds-clear" type="button" aria-label="Clear input" hidden> <i class="nds-icon nds-hgi-cancel-01" aria-hidden="true"></i> </button> </div> </div> </div>

Prefix & Suffix

Text input with inline prefix and suffix labels inside the form control

Prefix
Suffix
<div class="nds-form-container"> <div class="nds-form-header"> <label for="prefix-suffix-1"> <span class="nds-label">Label</span> </label> </div> <div class="nds-form-control"> <div class="nds-form-action nds-prefix"><span class="nds-btn nds-subtle"><span class="nds-label">Prefix</span></span></div> <input type="text" id="prefix-suffix-1" class="nds-input" placeholder="Entered text"> <div class="nds-form-action nds-suffix"><span class="nds-btn nds-subtle"><span class="nds-label">Suffix</span></span></div> </div> </div>

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.

<div class="nds-form-container"> <div class="nds-form-header"> <label for="phone-country-1"> <span class="nds-label">Phone</span> </label> </div> <div class="nds-form-control"> <div class="nds-form-action nds-prefix nds-dropmenu" data-select-name="country-code" data-select-value="+966"> <button type="button" class="nds-btn nds-subtle nds-menu-btn nds-dropmenu-trigger"> <span class="nds-label">+966</span> </button> <div class="nds-dropmenu-menu" hidden> <div class="nds-dropmenu-scroll"> <button type="button" class="nds-btn nds-subtle nds-dropmenu-item" data-value="+966" data-trigger-label="+966"> <span class="nds-label">Saudi Arabia (+966)</span> </button> <button type="button" class="nds-btn nds-subtle nds-dropmenu-item" data-value="+971" data-trigger-label="+971"> <span class="nds-label">United Arab Emirates (+971)</span> </button> <button type="button" class="nds-btn nds-subtle nds-dropmenu-item" data-value="+973" data-trigger-label="+973"> <span class="nds-label">Bahrain (+973)</span> </button> <button type="button" class="nds-btn nds-subtle nds-dropmenu-item" data-value="+974" data-trigger-label="+974"> <span class="nds-label">Qatar (+974)</span> </button> <button type="button" class="nds-btn nds-subtle nds-dropmenu-item" data-value="+965" data-trigger-label="+965"> <span class="nds-label">Kuwait (+965)</span> </button> <button type="button" class="nds-btn nds-subtle nds-dropmenu-item" data-value="+968" data-trigger-label="+968"> <span class="nds-label">Oman (+968)</span> </button> </div> </div> </div> <input type="tel" id="phone-country-1" name="phone" class="nds-input nds-phone" placeholder="00 000 0000" autocomplete="tel-national" inputmode="numeric" required> <div class="nds-form-action"> <button class="nds-btn nds-subtle nds-clear" type="button" aria-label="Clear input" hidden> <i class="nds-icon nds-hgi-cancel-01" aria-hidden="true"></i> </button> </div> </div> </div>

Number Input

Numeric field with increment and decrement buttons for precise value adjustments

<div class="nds-form-container" style="--form-width: 160px"> <div class="nds-form-header"> <label for="number-input-1"> <span class="nds-label">Quantity</span> </label> </div> <div class="nds-form-control"> <div class="nds-form-action nds-prefix"><button class="nds-btn nds-secondary nds-number-increment" type="button" aria-label="Increase value"><i class="hgi hgi-stroke hgi-add-01"></i></button></div> <input type="text" id="number-input-1" class="nds-input nds-center" inputmode="numeric" value="1" min="0" max="1000" step="1" placeholder="0"> <div class="nds-form-action nds-suffix"><button class="nds-btn nds-secondary nds-number-decrement" type="button" aria-label="Decrease value"><i class="hgi hgi-stroke hgi-minus-sign"></i></button></div> </div> </div>

Search Input

Search field with leading icon, voice input, and clear button

<div class="nds-form-container"> <div class="nds-form-header"> <label for="search-input-1"> <span class="nds-label">Search Services</span> </label> </div> <div class="nds-form-control"> <i class="nds-icon nds-hgi-search-01" aria-hidden="true"></i> <input type="text" id="search-input-1" class="nds-search-input" name="search" placeholder="Search services..."> <div class="nds-form-action"> <button class="nds-btn nds-subtle nds-clear" type="button" aria-label="Clear search" hidden> <i class="nds-icon nds-hgi-cancel-01" aria-hidden="true"></i> </button> <button class="nds-btn nds-subtle nds-voice-input" type="button" aria-label="Voice input"> <i class="nds-icon nds-hgi-mic-01" aria-hidden="true"></i> </button> </div> </div> </div>

Email Input

Email field with leading icon and built-in format validation

<div class="nds-form-container"> <div class="nds-form-header"> <label for="email-input-1"> <span class="nds-label">Email Address</span> </label> </div> <div class="nds-form-control"> <i class="nds-icon nds-hgi-mail-01" aria-hidden="true"></i> <input type="email" id="email-input-1" class="nds-input" placeholder="Enter your email..."> <div class="nds-form-action"> <button class="nds-btn nds-subtle nds-clear" type="button" aria-label="Clear email" hidden> <i class="nds-icon nds-hgi-cancel-01" aria-hidden="true"></i> </button> </div> </div> </div>

Password Input

Password field with visibility toggle and Arabic character filtering. For live strength rules and confirm-match checking, see Password

<div class="nds-form-container"> <div class="nds-form-header"> <label for="password-input-1"> <span class="nds-label">Password</span> </label> </div> <div class="nds-form-control"> <i class="nds-icon nds-hgi-lock-password" aria-hidden="true"></i> <input type="password" id="password-input-1" class="nds-input" placeholder="Enter password..." autocomplete="new-password"> <div class="nds-form-action"> <button class="nds-btn nds-subtle nds-clear" type="button" aria-label="Clear password" hidden> <i class="nds-icon nds-hgi-cancel-01" aria-hidden="true"></i> </button> <button class="nds-btn nds-subtle nds-toggle-password" type="button" aria-label="Show password"> <i class="nds-icon nds-hgi-view-off" aria-hidden="true"></i> </button> </div> </div> </div>

Textarea

Multi-line text entry for longer content like messages and descriptions

<div class="nds-form-container nds-textarea"> <div class="nds-form-header"> <label for="textarea-1"> <span class="nds-label">Message</span> </label> </div> <div class="nds-form-control"> <textarea id="textarea-1" class="nds-textarea" placeholder="Enter your message..." rows="4"></textarea> </div> </div>

Custom Select

Custom dropdown select with keyboard navigation and hidden input for form submission

<div class="nds-form-container nds-select"> <div class="nds-form-header"> <label for="select-input-1"> <span class="nds-label">Region</span> </label> </div> <div class="nds-form-control"> <input type="text" id="select-input-1" class="nds-input nds-select-input" placeholder="Choose an option..." readonly> <input type="hidden" name="selectValue" class="nds-select-value"> <div class="nds-select-dropdown" hidden> <div class="nds-select-options"> <button type="button" class="nds-btn nds-subtle nds-select-option" data-value=""> <span class="nds-option-text"> <span class="nds-label">Choose an option...</span> </span> </button> <button type="button" class="nds-btn nds-subtle nds-select-option" data-value="option1"> <span class="nds-option-text"> <span class="nds-label">Option 1</span> </span> </button> <button type="button" class="nds-btn nds-subtle nds-select-option" data-value="option2"> <span class="nds-option-text"> <span class="nds-label">Option 2</span> </span> </button> <button type="button" class="nds-btn nds-subtle nds-select-option" data-value="option3"> <i class="hgi hgi-stroke hgi-tag-01" aria-hidden="true"></i> <span class="nds-option-text"> <span class="nds-label">Option 3</span> <span class="nds-description">Free decoration is allowed inside an option</span> </span> </button> </div> </div> </div> </div>

Native Select

Standard <select> element wrapped in the form container for native mobile picker behavior. In-form choice fields use the custom select by default, whatever the option count. Use this one only when the design asks for the native picker

<div class="nds-form-container nds-select"> <div class="nds-form-header"> <label for="native-select-1"> <span class="nds-label">Region</span> </label> </div> <div class="nds-form-control"> <select id="native-select-1" name="region" class="nds-input"> <option value="" disabled selected>Choose an option...</option> <option value="riyadh">Riyadh</option> <option value="makkah">Makkah</option> <option value="eastern">Eastern Province</option> <option value="asir">Asir</option> </select> </div> </div>

Form Actions

The form-level action row for submit and navigation buttons. The row carries its own top margin and gap

<div class="nds-form-actions"> <button type="button" class="nds-btn nds-secondary-outline"> <span class="nds-label">Back</span> </button> <button type="submit" class="nds-btn nds-primary"> <span class="nds-label">Submit</span> </button> </div>

Form Status API

Set the error status on any form container with automatic feedback display. A field carries error, help, or nothing. Help is neutral gray with a "?" icon, for hint text. Any other status renders as a plain neutral message.

<div class="nds-form-container" id="national-id-field"> <div class="nds-form-header"> <label for="national-id"> <span class="nds-label">National ID</span> </label> </div> <div class="nds-form-control"> <input type="text" id="national-id" class="nds-input" placeholder="Enter your national ID..."> <div class="nds-form-action"> <button class="nds-btn nds-subtle nds-clear" type="button" aria-label="Clear input" hidden> <i class="nds-icon nds-hgi-cancel-01" aria-hidden="true"></i> </button> </div> </div> </div>

Server-rendered Errors

A server-rendered page paints its own errors on load. Loop over the errors from the server, call setStatus for each field, then focus the first one. setStatus sets the status and inserts the message, but it never moves focus — only the caller knows which error comes first. Focus also scrolls the field into view, so an error below the fold reaches the user.

// Errors rendered by the server, in field order var errors = [ { field: 'national-id-field', message: 'This field is required' }, { field: 'email-field', message: 'Enter a valid email address' } ]; var firstError = null; errors.forEach(function(error) { var container = document.getElementById(error.field); if (!container) return; NDS.Forms.setStatus({ element: container, status: 'error', message: error.message }); if (!firstError) firstError = container; }); // Focus the first error. A group container — an OTP fieldset, a radio group — // cannot take focus, so focus the first input inside it. if (firstError) { var target = firstError.querySelector('input, textarea, select') || firstError; target.focus(); }

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

<!-- Author the field as usual. Forms owns the spinner shell — you don't add a <button data-loading-slot> unless you want a custom label / icon; auto-creation covers the default case. --> <div class="nds-form-container" id="username-field"> <div class="nds-form-header"> <label for="username"><span class="nds-label">Username</span></label> </div> <div class="nds-form-control"> <input type="text" id="username" class="nds-input"> <div class="nds-form-action"> <button class="nds-btn nds-subtle nds-clear" type="button" aria-label="Clear input" hidden> <i class="nds-icon nds-hgi-cancel-01" aria-hidden="true"></i> </button> </div> </div> </div>

Permanent Feedback

Tips and hints that persist across validation cycles, hidden during errors and restored when cleared

<div class="nds-form-container"> <div class="nds-form-header"> <label for="username"> <span class="nds-label">Username</span> <span class="nds-info">Permanent tip shows again after error is cleared</span> </label> </div> <div class="nds-form-control"> <input type="text" id="username" class="nds-input" placeholder="Enter username..."> <div class="nds-form-action"> <button class="nds-btn nds-subtle nds-clear" type="button" aria-label="Clear input" hidden> <i class="nds-icon nds-hgi-cancel-01" aria-hidden="true"></i> </button> </div> </div> <div class="nds-form-footer" data-feedback-target> <span class="nds-feedback nds-outline nds-sm" data-status="neutral" data-permanent> <span class="nds-feedback-icon"> <i class="nds-icon" aria-hidden="true"></i> </span> <span class="nds-feedback-message">Use 3-20 characters, letters and numbers only</span> </span> </div> </div>

The data-feedback-target element sets where a field's feedback renders. It is optional: without one, the API creates feedback in the field's container. Place it anywhere inside the container — a nds-form-footer puts messages below the input, a nds-form-header puts them above. A hidden target is shown when a message lands and hidden again when the last message clears.

Built-in Features

What you get out of the box with zero configuration

Auto-initialization

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().

Validation

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.

Extending Validation

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.

Status Feedback

Feedback is created by the API: setStatus places the message in the field's container, or in a data-feedback-target element when one is present. A field shows error or help; any other status renders neutral. Permanent hints survive validation cycles with data-permanent.

Interactive States

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.

Password Toggle

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.

Voice Input

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.

Arabic Character Filtering

Password fields automatically strip Arabic and RTL characters on input, ensuring passwords contain only compatible characters without manual validation logic.

Number Input Controls

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.
  • A nds-form element draws no box of its own — it is a validation hook, not a layout container. Give its fields a gapped wrapper: nds-card-content or nds-flex nds-col. Fields in a bare container render flush against each other.
  • Use nds-search-input for 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.
  • Wrap a form's submit and navigation buttons in nds-form-actions. The plural class is the form-level row and carries its own top margin. The singular nds-form-action is the per-input slot inside nds-form-control. Never use a section action slot for a form's buttons.
  • Use the custom select dropdown (nds-select) for in-form choice fields, whatever the option count — a native select beside styled fields breaks the form's look. It ships with keyboard navigation and a hidden input so it submits with the form. Wrap a standard <select> in the same nds-form-container nds-select shell only when the design calls for the native mobile picker, as shown in the Native Select demo. With data-required on the container, form validation reads the hidden value input at submit, the same as any other field.
  • 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. Use nds-md for compact layouts like table filters, inline editing, or sidebars where space is limited.
  • Add data-required to the container rather than required on 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.
  • Validation feedback needs no markup of its own: setStatus and submit validation create the message element and place it in the field's container. Add a data-feedback-target element only when the design wants a permanent feedback position — see the Permanent Feedback section.
  • Set data-error-message on the container to override default browser validation messages with context-specific text that guides the user toward correction.

Modifier Classes

ClassElementDescription
nds-selectnds-form-containerRequired on a container that holds a native <select>. It hides the browser's own arrow so the NDS chevron is the only one, and sizes the control for the select. Without it both arrows render
nds-textareands-form-containerRequired on a container that holds a <textarea>. Without it the control keeps single-line input sizing, and the textarea loses its padding and minimum height
nds-mdnds-form-containerMedium size with reduced height (32px) and smaller font
nds-lighternds-form-containerFilled-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-darkernds-form-containerFilled-background variant using the darker surface token. Same border behavior as nds-lighter. Not applied to file upload containers.
nds-rowViewnds-form-groupLays out checkbox, radio, or switch group items in a horizontal wrapping row instead of the default vertical column.
nds-phonends-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.

Validation Attributes

Validation runs on the browser's own constraint API. These are standard HTML attributes, not NDS ones, and they go on the <input>. NDS supplies the message text in Arabic and English and places it in the field footer.

AttributeMessage when the value fails it
requiredThis field is required. Prefer data-required on the container: it sets this attribute for you and adds the asterisk.
type="email"Please enter a valid email address
type="url"Please enter a valid URL
minlengthInput is too short (minimum N characters)
maxlengthInput is too long (maximum N characters). The browser also blocks typing past the limit, so this message appears only for a value set in code or pasted.
patternPlease match the requested format. The value must match the whole expression, so pattern="[0-9]{10}" accepts exactly ten digits and nothing else. Give the field a data-error-message that states the rule: the default text does not say what the format is.
min / maxValue must be at least N / Value must be no more than N. On number inputs these also bound the stepper buttons: see the Data Attributes table below.

Any other failure reads Invalid input. A field that a component validates itself, such as a date picker, carries that component's own message instead.

Data Attributes

AttributeDescription
data-requiredSet 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-messageSet on the <input> to replace every validation message that field would otherwise show, whichever constraint failed. For a checkbox or radio group, set it on the nds-form-group instead. It is read from those two elements only, not from the container.
data-permanentSet on a feedback element inside the footer. Permanent feedback hides during validation errors and restores when cleared.
minSet on number inputs to define the minimum allowed value (default: 0).
maxSet on number inputs to define the maximum allowed value (default: 1000).
stepSet on number inputs to define the increment/decrement size (default: 1). Long press accelerates to 10x this value.
data-select-nameSet 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-valueSet 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-labelSet 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-checkedSet 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-checkedSet 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-required (on a rating group)Set on an nds-form-group that holds a Rating. The group is invalid while data-rating is 0, and the first star takes focus. Picking a star clears the message, the way typing in an input does; submit re-checks the rule. The stars are buttons, not inputs, so add a hidden input if the score must post with the form.
data-ajaxSet 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

PropertyDefaultDescription
--form-width100%Controls the width of the form container
--input-size40pxHeight of the input field (32px for nds-md)
--input-radiusvar(--radius-sm)Border radius of the input field
--nds-input-size16pxSize (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.

// ── Initialization ────────────────────────────────── // All forms auto-initialize on page load NDS.Forms.init(); // Initialize a specific container (for dynamic content) NDS.Forms.initializeContainer(containerElement); // Initialize a dynamic <form> element (attaches submit validation) NDS.Forms.initForm(formElement); // ── Status API ────────────────────────────────────── // A field carries 'error', 'help' or nothing. Anything else renders neutral. NDS.Forms.setStatus({ element: el, status: 'error', message: 'Required field' }); NDS.Forms.setStatus({ element: el, status: 'help', message: 'Use 3-20 characters' }); NDS.Forms.setStatus({ element: el, status: 'neutral', message: 'Hint text' }); // Permanent feedback (survives validation cycles) NDS.Forms.setStatus({ element: el, status: 'neutral', message: 'Tip text', permanent: true }); // Clear status (restores permanent feedback if present) NDS.Forms.clearStatus(el); // Get current status NDS.Forms.getStatus(el); // Returns: { status: 'error', message: '...', isValid: false } // setStatus never moves focus. For server-rendered errors, focus the first // field yourself — see the Form Status API section. // ── Field Sync ────────────────────────────────────── // Re-sync a form-control's chrome after programmatic value/checked changes. // Call this (or dispatch input/change) when setting a field's value from JS; // raw assignment alone no longer notifies the field. NDS.Forms.syncState(inputElement); // Set or clear a checkbox's indeterminate state. // Stamps data-state="indeterminate" on the container and fires nds:indeterminateChange. NDS.Forms.setIndeterminate(checkboxElement, true); // ── State Management ──────────────────────────────── // Set disabled or required state programmatically NDS.Forms.setState(el, 'disabled', true); NDS.Forms.setState(el, 'required', true); // ── Custom Select: programmatic value ─────────────── // The same write sequence as a user pick: display label, hidden // .nds-select-value, selected markers, input/change events (field sync), // and the nds:customselect:change dispatch. Pass the .nds-form-control or the // .nds-select-input. setValue returns false when no option carries the // value — display and submit value are never desynced. Works before the // dropdown is first built and while its menu is open. NDS.CustomSelect.setValue(formControl, 'option1'); NDS.CustomSelect.clear(formControl); // ── Form Validation ───────────────────────────────── // Validate entire form (returns { valid, invalidFields, errors }) NDS.Forms.validateForm(formElement); // Validate specific groups (used on checkbox, radio, OTP pages) NDS.Forms.validateCheckboxGroup(group); NDS.Forms.validateRadioGroup(group); NDS.Forms.validateOtpGroup(group); // ── Events ────────────────────────────────────────── // Status change on any .nds-form-container element.addEventListener('nds:statusChange', function(e) { // e.detail: { status: 'error'|'success'|...|null, message: '...'|null } }); // Form validation events on .nds-form elements form.addEventListener('nds:formValidate', function(e) { // Fires after validation runs // e.detail: { valid: true|false, invalidFields: [...], errors: [...] } }); form.addEventListener('nds:formValid', function(e) { // Fires when form passes validation on submit }); form.addEventListener('nds:formInvalid', function(e) { // Fires when form fails validation on submit // e.detail: { invalidFields: [...], errors: [...] } }); // Select dropdown change event on .nds-form-control formControl.addEventListener('nds:customselect:change', function(e) { // e.detail: { value: 'option1', text: 'Option 1' } }); // Switch toggle event on .nds-switch-element switchElement.addEventListener('nds:switchChange', function(e) { // e.detail: { checked: true|false, value: '...', input: switchInputElement } }); // Checkbox indeterminate state change on the checkbox element checkbox.addEventListener('nds:indeterminateChange', function(e) { // e.detail: { indeterminate: true|false } }); // ── Dropmenu select-mode (prefix/suffix pickers) ──── // Any .nds-dropmenu with data-select-name captures the picked value into // a hidden input and fires these two events. dropmenu.addEventListener('nds:dropmenu:selected', function(e) { // e.detail: { dropmenu, item, value } }); // Hidden input change — use this if you only care about the new value hiddenInput.addEventListener('change', function(e) { // e.target.value holds the selected data-value });
Last Modified Date: 20/09/2026 - 11:12 PM
Was this page useful?
60% of users said Yes from 2843 Feedbacks