Skip to main contentSkip to docs navigation

Form Validation

Provide clear, accessible feedback to users with Chassis CSS's form validation styles, utilizing HTML5 validation or custom implementations.

@layer components
MDN Reference
Component tokens

When using client-side custom validation styles and tooltips, additional work is needed to ensure proper accessibility. For better out-of-the-box accessibility, consider using server-side validation or native browser validation.

Understanding form validation

Chassis CSS provides flexible options for implementing form validation:

  • Native validation: Uses the browser's built-in :invalid and :valid pseudo-classes to style form controls based on validation state.
  • Progressive enhancement: Chassis uses the .was-validated class on forms to control when validation styles appear. This prevents invalid styles from showing when the page first loads.
  • Server-side fallback: The .is-invalid and .is-valid classes provide direct control for server-side validation scenarios.
  • Custom validation messages: Add contextual feedback to users with .valid-feedback and .invalid-feedback elements.
  • Opt-in icons: Add .validation-icons to a wrapper (e.g. the <form>) to render success/error glyphs inside every supported input it contains. Without it, only the colors, border, and shadow change.
  • API integration: All validation styles work with the browser's Constraint Validation API, allowing you to create custom validation logic with JavaScript.
  • Accessible feedback: Position validation messages consistently outside input groups for optimal screen reader compatibility.

Client-side validation

For custom Chassis validation styles, add the novalidate attribute to your <form> element. This disables browser default tooltips but still allows JavaScript validation using the Constraint Validation API.

Key features of Chassis validation:

  • Custom feedback styles with contextual colors and icons
  • Support for all form controls including input groups
  • Consistent positioning of feedback messages
  • JavaScript integration for dynamic validation
Looks good!
Looks good!
@
Please choose a username.
Please provide a valid city.
Please select a valid state.
Please provide a valid zip.
You must agree before submitting.
HTML
<form class="row g-medium" data-cx-validate novalidate>
  <div class="medium:col-4">
    <label for="validationCustom01" class="form-label">First name</label>
    <input type="text" class="form-input" id="validationCustom01" value="John" required>
    <div class="valid-feedback">
      Looks good!
    </div>
  </div>
  <div class="medium:col-4">
    <label for="validationCustom02" class="form-label">Last name</label>
    <input type="text" class="form-input" id="validationCustom02" value="Smith" required>
    <div class="valid-feedback">
      Looks good!
    </div>
  </div>
  <div class="medium:col-4">
    <label for="validationCustomUsername" class="form-label">Username</label>
    <div class="input-group">
      <span class="input-addon" id="inputGroupPrepend">@</span>
      <input type="text" class="form-input" id="validationCustomUsername" aria-describedby="inputGroupPrepend" required>
    </div>
    <div class="invalid-feedback">
      Please choose a username.
    </div>
  </div>
  <div class="medium:col-6">
    <label for="validationCustom03" class="form-label">City</label>
    <input type="text" class="form-input" id="validationCustom03" required>
    <div class="invalid-feedback">
      Please provide a valid city.
    </div>
  </div>
  <div class="medium:col-3">
    <label for="validationCustom04" class="form-label">State</label>
    <select class="form-input" id="validationCustom04" required>
      <option selected disabled value="">Choose...</option>
      <option>California</option>
      <option>New York</option>
      <option>Texas</option>
    </select>
    <div class="invalid-feedback">
      Please select a valid state.
    </div>
  </div>
  <div class="medium:col-3">
    <label for="validationCustom05" class="form-label">Zip</label>
    <input type="text" class="form-input" id="validationCustom05" required>
    <div class="invalid-feedback">
      Please provide a valid zip.
    </div>
  </div>
  <div class="col-12">
    <div class="form-check">
      <input class="check-input" type="checkbox" value="" id="invalidCheck" required>
      <label class="check-label" for="invalidCheck">
        Agree to terms and conditions
      </label>
      <div class="invalid-feedback">
        You must agree before submitting.
      </div>
    </div>
  </div>
  <div class="col-12">
    <button class="button primary" type="submit">Submit form</button>
  </div>
</form>

The example below shows the JavaScript needed to implement custom validation with Chassis CSS.

JavaScript
// Example starter JavaScript for preventing form submissions when there are invalid fields
;(() => {
  'use strict'

  const forms = document.querySelectorAll('form[data-cx-validate]')

  for (const form of forms) {
    form.addEventListener('submit', (event) => {
      if (!form.checkValidity()) {
        event.preventDefault()
        event.stopPropagation()
      }

      for (const control of form.elements) {
        if (control.willValidate) {
          control.setAttribute('aria-invalid', String(!control.validity.valid))
        }
      }
    })

    // Clear aria-invalid as users correct individual fields
    form.addEventListener('input', (event) => {
      const control = event.target
      if (control.willValidate && control.hasAttribute('aria-invalid')) {
        control.setAttribute('aria-invalid', String(!control.validity.valid))
      }
    })
  }
})()

Native browser validation

If you prefer simpler implementation, you can use the browser's built-in validation. Chassis CSS works seamlessly with native validation UI, though these styles cannot be customized with CSS.

@
HTML
<form class="row g-medium">
  <div class="medium:col-4">
    <label for="validationDefault01" class="form-label">First name</label>
    <input type="text" class="form-input" id="validationDefault01" value="John" required>
  </div>
  <div class="medium:col-4">
    <label for="validationDefault02" class="form-label">Last name</label>
    <input type="text" class="form-input" id="validationDefault02" value="Smith" required>
  </div>
  <div class="medium:col-4">
    <label for="validationDefaultUsername" class="form-label">Username</label>
    <div class="input-group">
      <span class="input-addon" id="inputGroupPrepend2">@</span>
      <input type="text" class="form-input" id="validationDefaultUsername" aria-describedby="inputGroupPrepend2" required>
    </div>
  </div>
  <div class="medium:col-6">
    <label for="validationDefault03" class="form-label">City</label>
    <input type="text" class="form-input" id="validationDefault03" required>
  </div>
  <div class="medium:col-3">
    <label for="validationDefault04" class="form-label">State</label>
    <select class="form-input" id="validationDefault04" required>
      <option selected disabled value="">Choose...</option>
      <option>California</option>
      <option>New York</option>
      <option>Texas</option>
    </select>
  </div>
  <div class="medium:col-3">
    <label for="validationDefault05" class="form-label">Zip</label>
    <input type="text" class="form-input" id="validationDefault05" required>
  </div>
  <div class="col-12">
    <div class="form-check">
      <input class="check-input" type="checkbox" value="" id="invalidCheck2" required>
      <label class="check-label" for="invalidCheck2">
        Agree to terms and conditions
      </label>
    </div>
  </div>
  <div class="col-12">
    <button class="button primary" type="submit">Submit form</button>
  </div>
</form>

While native browser validation styles cannot be changed with CSS, you can customize the validation message text using JavaScript's setCustomValidity() method.

Server-side validation

For applications that validate on the server, Chassis CSS provides the .is-valid and .is-invalid state classes that can be applied to form controls directly, without needing the .was-validated parent class.

For invalid fields, always connect error messages to their inputs using aria-describedby to ensure screen readers announce feedback properly. Multiple IDs can be referenced if a field has both help text and error messages.

Looks good!
Looks good!
@
Please choose a username.
Please provide a valid city.
Please select a valid state.
Please provide a valid zip.
You must agree before submitting.
HTML
<form class="row g-medium">
  <div class="medium:col-4">
    <label for="validationServer01" class="form-label">First name</label>
    <input type="text" class="form-input is-valid" id="validationServer01" value="John" required>
    <div class="valid-feedback">
      Looks good!
    </div>
  </div>
  <div class="medium:col-4">
    <label for="validationServer02" class="form-label">Last name</label>
    <input type="text" class="form-input is-valid" id="validationServer02" value="Smith" required>
    <div class="valid-feedback">
      Looks good!
    </div>
  </div>
  <div class="medium:col-4">
    <label for="validationServerUsername" class="form-label">Username</label>
    <div class="input-group has-validation">
      <span class="input-addon" id="inputGroupPrepend3">@</span>
      <input type="text" class="form-input is-invalid" id="validationServerUsername" aria-describedby="inputGroupPrepend3 validationServerUsernameFeedback" required>
    </div>
    <div id="validationServerUsernameFeedback" class="invalid-feedback">
      Please choose a username.
    </div>
  </div>
  <div class="medium:col-6">
    <label for="validationServer03" class="form-label">City</label>
    <input type="text" class="form-input is-invalid" id="validationServer03" aria-describedby="validationServer03Feedback" required>
    <div id="validationServer03Feedback" class="invalid-feedback">
      Please provide a valid city.
    </div>
  </div>
  <div class="medium:col-3">
    <label for="validationServer04" class="form-label">State</label>
    <select class="form-input is-invalid" id="validationServer04" aria-describedby="validationServer04Feedback" required>
      <option selected disabled value="">Choose...</option>
      <option>California</option>
      <option>New York</option>
      <option>Texas</option>
    </select>
    <div id="validationServer04Feedback" class="invalid-feedback">
      Please select a valid state.
    </div>
  </div>
  <div class="medium:col-3">
    <label for="validationServer05" class="form-label">Zip</label>
    <input type="text" class="form-input is-invalid" id="validationServer05" aria-describedby="validationServer05Feedback" required>
    <div id="validationServer05Feedback" class="invalid-feedback">
      Please provide a valid zip.
    </div>
  </div>
  <div class="col-12">
    <div class="form-check">
      <input class="check-input is-invalid" type="checkbox" value="" id="invalidCheck3" aria-describedby="invalidCheck3Feedback" required>
      <label class="check-label" for="invalidCheck3">
        Agree to terms and conditions
      </label>
      <div id="invalidCheck3Feedback" class="invalid-feedback">
        You must agree before submitting.
      </div>
    </div>
  </div>
  <div class="col-12">
    <button class="button primary" type="submit">Submit form</button>
  </div>
</form>

Supported form elements

Chassis CSS validation styles work consistently across all basic form elements:

Example invalid select feedback
Example invalid textarea feedback
Checkbox
Example invalid checkbox legacy feedback
Example invalid checkbox modern feedback
Radio
Example invalid radio feedback
Switch
Example invalid legacy switch feedback
Example invalid modern switch feedback
Example invalid form file feedback
Example invalid color feedback
Example invalid range feedback
Floating label
Example invalid floating label feedback
@
Example invalid input group feedback
Example invalid input help feedback
Example
Example invalid chip input feedback
Example invalid combobox feedback
HTML
<form class="vstack gap-medium">
  <div class="form-field">
    <label for="validationSelect" class="form-label">Select</label>
    <select class="form-input is-invalid" id="validationSelect" required aria-describedby="validationSelectFeedback">
      <option value="">Choose an option</option>
      <option value="1">Option One</option>
      <option value="2">Option Two</option>
      <option value="3">Option Three</option>
    </select>
    <div class="invalid-feedback" id="validationSelectFeedback">Example invalid select feedback</div>
  </div>

  <div class="form-field">
    <label for="validationTextarea" class="form-label">Textarea</label>
    <textarea class="form-input is-invalid" id="validationTextarea" placeholder="Required example textarea" required aria-describedby="validationTextareaFeedback"></textarea>
    <div class="invalid-feedback" id="validationTextareaFeedback">
      Example invalid textarea feedback
    </div>
  </div>

  <div class="form-field">
    <div class="form-label">Checkbox</div>
    <div class="form-check">
      <input type="checkbox" class="check-input is-invalid" id="validationCheckboxLegacy" name="checkbox-stacked-legacy" required aria-describedby="validationCheckboxLegacyFeedback">
      <label for="validationCheckboxLegacy">Check this legacy checkbox</label>
      <div class="invalid-feedback" id="validationCheckboxLegacyFeedback">Example invalid checkbox legacy feedback</div>
    </div>
    <div class="form-check">
      <span class="check-input default">
        <input type="checkbox" class="is-invalid" id="validationCheckboxModern" name="checkbox-stacked" required aria-describedby="validationCheckboxModernFeedback">
      </span>
      <label for="validationCheckboxModern">Check this modern checkbox</label>
      <div class="invalid-feedback" id="validationCheckboxModernFeedback">Example invalid checkbox modern feedback</div>
    </div>
  </div>

  <div class="form-field">
    <div class="form-label">Radio</div>
    <div class="form-check">
      <input type="radio" class="check-input is-invalid" id="validationRadioLegacy" name="radio-stacked" required aria-describedby="validationRadioFeedback">
      <label for="validationRadioLegacy">Toggle this legacy radio</label>
    </div>
    <div class="form-check">
      <span class="check-input default">
        <input type="radio" class="is-invalid" id="validationRadioModern" name="radio-stacked" required aria-describedby="validationRadioFeedback">
      </span>
      <label for="validationRadioModern">Toggle this modern radio</label>
    </div>
    <div class="invalid-feedback" id="validationRadioFeedback">Example invalid radio feedback</div>
  </div>

  <div class="form-field">
    <div class="form-label">Switch</div>
    <div class="form-check">
      <input type="checkbox" class="check-input is-invalid" role="switch" id="validationSwitchLegacy" required aria-describedby="validationSwitchLegacyFeedback">
      <label for="validationSwitchLegacy">Check this legacy switch</label>
      <div class="invalid-feedback" id="validationSwitchLegacyFeedback">Example invalid legacy switch feedback</div>
    </div>
    <div class="form-check">
      <span class="check-input default">
        <input type="checkbox" class="is-invalid" role="switch" id="validationSwitchModern" required aria-describedby="validationSwitchModernFeedback">
      </span>
      <label for="validationSwitchModern">Check this modern switch</label>
      <div class="invalid-feedback" id="validationSwitchModernFeedback">Example invalid modern switch feedback</div>
    </div>
  </div>

  <div class="form-field">
    <label for="validationFile" class="form-label">File input</label>
    <input type="file" class="form-input is-invalid" id="validationFile" aria-label="file example" required aria-describedby="validationFileFeedback">
    <div class="invalid-feedback" id="validationFileFeedback">Example invalid form file feedback</div>
  </div>

  <div class="form-field">
    <label for="validationColor" class="form-label">Color input</label>
    <input type="color" class="form-input is-invalid" id="validationColor" aria-label="color example" aria-describedby="validationColorFeedback">
    <div class="invalid-feedback" id="validationColorFeedback">Example invalid color feedback</div>
  </div>

  <div class="form-field">
    <label for="validationRange" class="form-label">Range input</label>
    <input type="range" class="form-range is-invalid" id="validationRange" aria-label="range example" aria-describedby="validationRangeFeedback">
    <div class="invalid-feedback" id="validationRangeFeedback">Example invalid range feedback</div>
  </div>

  <div class="form-field">
    <div class="form-label">Floating label</div>
    <div class="form-floating">
      <label for="validationFloating">Email address</label>
      <input type="email" class="form-input is-invalid" id="validationFloating" placeholder="name@example.com" value="test@example.com" aria-describedby="validationFloatingFeedback">
    </div>
    <div id="validationFloatingFeedback" class="invalid-feedback">Example invalid floating label feedback</div>
  </div>

  <div class="form-field">
    <label for="validationInputGroup" class="form-label">Input group</label>
    <div class="input-group">
      <span class="input-addon">@</span>
      <input type="text" class="form-input is-invalid" id="validationInputGroup" placeholder="Username" aria-describedby="validationInputGroupFeedback">
    </div>
    <div id="validationInputGroupFeedback" class="invalid-feedback">Example invalid input group feedback</div>
  </div>

  <div class="form-field">
    <label for="validationInputHelp" class="form-label">Input Help</label>
    <div class="form-input">
      <div class="input-help">
        <svg class="icon" aria-hidden="true"><use href="#search-outline"></use></svg>
      </div>
      <input type="text" class="ghost-input is-invalid" id="validationInputHelp" placeholder="Search..." aria-describedby="validationInputHelpFeedback" required>
    </div>
    <div class="invalid-feedback" id="validationInputHelpFeedback">Example invalid input help feedback</div>
  </div>

  <div class="form-field">
    <label for="validationChipInput" class="form-label">Chip Input</label>
    <div class="form-input chip-input" data-cx-chips="default smooth">
      <span class="chip" data-cx-chip-value="example">Example</span>
      <input type="text" class="ghost-input is-invalid" placeholder="Add more…" id="validationChipInput" aria-describedby="validationChipInputFeedback" required>
    </div>
    <div class="invalid-feedback" id="validationChipInputFeedback">Example invalid chip input feedback</div>
  </div>

  <div class="form-field">
    <label for="validationCombobox" class="form-label">Role</label>
    <div class="form-input combobox"
         data-cx-toggle="combobox"
         data-cx-name="role">
      <input type="text" class="combobox-value is-invalid" placeholder="Choose an option…" autocomplete="off" id="validationCombobox" aria-describedby="validationComboboxFeedback" required>
    </div>
    <div class="menu" role="listbox">
      <button class="menu-item" type="button" role="option" data-cx-value="admin">Admin</button>
      <button class="menu-item" type="button" role="option" data-cx-value="editor">Editor</button>
      <button class="menu-item" type="button" role="option" data-cx-value="viewer">Viewer</button>
    </div>
    <div class="invalid-feedback" id="validationComboboxFeedback">Example invalid combobox feedback</div>
  </div>

  <div>
    <button class="button primary" type="submit" disabled>Submit form</button>
  </div>
</form>

Tooltip validation

For compact layouts, Chassis CSS provides tooltip-style validation feedback. Simply replace .valid-feedback and .invalid-feedback with .valid-tooltip and .invalid-tooltip classes.

Tooltips require a parent element with position: relative. The column classes in our examples already have this, but you may need to add it to your own layouts.

Looks good!
Looks good!
@
Please choose a unique and valid username.
Please provide a valid city.
Please select a valid state.
Please provide a valid zip.
HTML
<form class="row g-medium" data-cx-validate novalidate>
  <div class="medium:col-4 position-relative">
    <label for="validationTooltip01" class="form-label">First name</label>
    <input type="text" class="form-input" id="validationTooltip01" value="John" required>
    <div class="valid-tooltip">
      Looks good!
    </div>
  </div>
  <div class="medium:col-4 position-relative">
    <label for="validationTooltip02" class="form-label">Last name</label>
    <input type="text" class="form-input" id="validationTooltip02" value="Smith" required>
    <div class="valid-tooltip">
      Looks good!
    </div>
  </div>
  <div class="medium:col-4 position-relative">
    <label for="validationTooltipUsername" class="form-label">Username</label>
    <div class="input-group has-validation">
      <span class="input-addon" id="validationTooltipUsernamePrepend">@</span>
      <input type="text" class="form-input" id="validationTooltipUsername" aria-describedby="validationTooltipUsernamePrepend" required>
    </div>
    <div class="invalid-tooltip">
      Please choose a unique and valid username.
    </div>
  </div>
  <div class="medium:col-6 position-relative">
    <label for="validationTooltip03" class="form-label">City</label>
    <input type="text" class="form-input" id="validationTooltip03" required>
    <div class="invalid-tooltip">
      Please provide a valid city.
    </div>
  </div>
  <div class="medium:col-3 position-relative">
    <label for="validationTooltip04" class="form-label">State</label>
    <select class="form-input" id="validationTooltip04" required>
      <option selected disabled value="">Choose...</option>
      <option>California</option>
      <option>New York</option>
      <option>Texas</option>
    </select>
    <div class="invalid-tooltip">
      Please select a valid state.
    </div>
  </div>
  <div class="medium:col-3 position-relative">
    <label for="validationTooltip05" class="form-label">Zip</label>
    <input type="text" class="form-input" id="validationTooltip05" required>
    <div class="invalid-tooltip">
      Please provide a valid zip.
    </div>
  </div>
  <div class="col-12">
    <button class="button primary" type="submit">Submit form</button>
  </div>
</form>

Validation icons

Validation icons are opt-in at the wrapper level. Add the .validation-icons class to any ancestor — typically the <form>, but it also works on .form-field, .input-group, or even a single .form-input — and every supported descendant input renders the success or error glyph when it enters a validation state. Without an ancestor with this class, inputs still pick up validation colors, border, and shadow — only the icon is omitted.

Icons appear on text inputs, single-value <select> elements, color inputs, .ghost-input (used by .chip-input and the .input-help wrapper), and .combobox-value. Textareas, checkboxes, radios, switches, file, and range inputs do not receive icons by design.

Looks good!
Please select a valid state.
Looks good!
$ USD
Enter an amount greater than zero.
HTML CSS
Add at least one more skill.
Please pick a role.
HTML
<form class="vstack gap-medium validation-icons" data-cx-validate novalidate>
  <div class="form-field">
    <label for="validationIconsText" class="form-label">Text input</label>
    <input type="text" class="form-input" id="validationIconsText" value="John" required>
    <div class="valid-feedback">Looks good!</div>
  </div>

  <div class="form-field">
    <label for="validationIconsSelect" class="form-label">Select</label>
    <select class="form-input" id="validationIconsSelect" required>
      <option selected disabled value="">Choose...</option>
      <option>California</option>
      <option>New York</option>
      <option>Texas</option>
    </select>
    <div class="invalid-feedback">Please select a valid state.</div>
  </div>

  <div class="form-field">
    <label for="validationIconsColor" class="form-label">Color</label>
    <input type="color" class="form-input" id="validationIconsColor" value="#3366ff">
    <div class="valid-feedback">Looks good!</div>
  </div>

  <div class="form-field">
    <label for="validationIconsInputHelp" class="form-label">Input help</label>
    <div class="form-input">
      <span class="input-help">$</span>
      <input type="number" class="ghost-input" id="validationIconsInputHelp" placeholder="0.00" min="1" required>
      <span class="input-help">USD</span>
    </div>
    <div class="invalid-feedback">Enter an amount greater than zero.</div>
  </div>

  <div class="form-field">
    <label for="validationIconsChipInput" class="form-label">Chip input</label>
    <div class="form-input chip-input" data-cx-chips="primary smooth">
      <span class="chip" data-cx-chip-value="HTML">HTML</span>
      <span class="chip" data-cx-chip-value="CSS">CSS</span>
      <input type="text" class="ghost-input" id="validationIconsChipInput" placeholder="Add a skill…" required>
    </div>
    <div class="invalid-feedback">Add at least one more skill.</div>
  </div>

  <div class="form-field">
    <label for="validationIconsCombobox" class="form-label">Combobox</label>
    <div class="form-input combobox" data-cx-toggle="combobox" data-cx-name="role">
      <input type="text" class="combobox-value" id="validationIconsCombobox" placeholder="Choose a role…" autocomplete="off" required>
    </div>
    <div class="menu" role="listbox">
      <button class="menu-item" type="button" role="option" data-cx-value="admin">Admin</button>
      <button class="menu-item" type="button" role="option" data-cx-value="editor">Editor</button>
      <button class="menu-item" type="button" role="option" data-cx-value="viewer">Viewer</button>
    </div>
    <div class="invalid-feedback">Please pick a role.</div>
  </div>

  <div>
    <button class="button primary" type="submit">Submit form</button>
  </div>
</form>

Set the Sass option $enable-validation-icons: false to strip the icon rules from the compiled bundle entirely. The .validation-icons class becomes a no-op when icons are disabled at compile time.

Persistent Form help

The .form-help component is hidden by default when validation feedback is present so the active message takes precedence. Add the .always-show class to keep help text visible alongside validation feedback. Always place form help and validation feedback elements as siblings after the input or input group.

Accessibility

Validation feedback only works for every user when it's wired up with the right ARIA attributes. The example below demonstrates the recommended pattern for dynamic, JavaScript-driven validation — connecting the input to its feedback with aria-describedby, announcing changes through a live region, and toggling aria-invalid in sync with .is-invalid.

Because .invalid-feedback and .valid-feedback are hidden with display: none when inactive, you can safely list all relevant IDs in a single static aria-describedby — screen readers skip display: none elements, so only the currently visible description is announced on focus. Add aria-live="polite" to feedback elements so errors injected via JavaScript are announced immediately without requiring the user to re-focus the field. Always set aria-invalid="true" on the input when it is invalid, and remove it when the error is cleared.

@
Enter your username
HTML
<div class="input-group">
  <span class="input-addon">@</span>
  <input type="text" class="form-input" id="username-input"
    aria-describedby="username-feedback username-help" placeholder="Username" required>
  </div>
  <div class="invalid-feedback" id="username-feedback" aria-live="polite"></div>
  <div class="form-help always-show" id="username-help">Enter your username</div>
  <div class="mt-medium">
    <button class="button primary" type="button" onclick="validateUsername()">Validate username</button>
</div>
JavaScript
function validateUsername() {
  const input = document.getElementById("username-input")
  const feedback = document.getElementById("username-feedback")
  const invalid = !input.validity.valid

  input.classList.toggle("is-invalid", invalid)
  if (invalid) {
    input.setAttribute("aria-invalid", "true")
    feedback.textContent = input.validationMessage
  } else {
    input.removeAttribute("aria-invalid")
    feedback.textContent = ""
  }
}

CSS

Chassis CSS uses a powerful combination of Sass and CSS variables to create a flexible form system that adapts to themes and design requirements.

Custom properties

Check the overview page to see the CSS variables shared by all form elements.

Sass variables

This component uses Sass variables in scss/config/_defaults.scss to define its defaults.

$form-valid-class:                          is-valid;
$form-invalid-class:                        is-invalid;

$form-valid-tooltip-fg-color:               var(--success-fg-solid);
$form-valid-tooltip-bg-color:               var(--success-bg-solid);

$form-invalid-tooltip-fg-color:             var(--danger-fg-solid);
$form-invalid-tooltip-bg-color:             var(--danger-bg-solid);

$form-valid-focus-ring-color:               light-dark(to-opacity(var(--success-cue-main), var(--focus-ring-light-opacity)), to-opacity(var(--success-cue-main), var(--focus-ring-dark-opacity)));
$form-invalid-focus-ring-color:             light-dark(to-opacity(var(--danger-cue-main), var(--focus-ring-light-opacity)), to-opacity(var(--danger-cue-main), var(--focus-ring-dark-opacity)));

$form-feedback-icon-size:                   var(--icon-size);

$form-feedback-icon-input-position:         center var(--logical-position-end, right) var(--padding-x);
$form-feedback-icon-input-padding-e:        calc($form-feedback-icon-size + var(--padding-x) + var(--gap));
$form-feedback-icon-select-position:        center var(--logical-position-end, right) calc(var(--padding-x) + var(--gap) * .5 + var(--caret-size));
$form-feedback-icon-select-padding-e:       calc($form-feedback-icon-size + var(--padding-x) + var(--gap) * 1.5 + var(--caret-size));

Design tokens

This component consumes design tokens from Chassis Tokens with the $cx- prefix. — See thedesign tokens page.


The following design tokens define the validation icons and sizes:

$form-feedback-valid-icon:                $cx-icon-form-input-feedback-valid;
$form-feedback-invalid-icon:              $cx-icon-form-input-feedback-invalid;
$form-medium-icon-size:                   $cx-size-form-input-medium-icon;
$form-large-icon-size:                    $cx-size-form-input-large-icon;
$form-small-icon-size:                    $cx-size-form-input-small-icon;

Check the overview page to see the design tokens shared by all form elements.