Forms always need a label, both atop and a tip within the field. Users often enter information, get distracted, and then forget what they are doing. The field label assists in this very common error. The label inside of the form should disappear on typing. Forms should be sized appropriately for the information they are expected to receive (e.g. zip code should be around 5-12 characters). Information inputted into forms should be auto-formatted, such as telephone or a date. Errors always need text, an icon, and a color change.

Further, we support helpful prefixes and suffixes to prompt interaction, such as the dollar sign or the percentage sign.

Overview

Be sure to use an appropriate `type` attribute on all inputs (e.g., `email` for email address or `number` for numerical information) to take advantage of newer input controls like email verification, number selection, and more.

Here's a quick example to demonstrate Cirrus's form styles. Keep reading for documentation on required classes, form layout, and more.

We'll never share your email with anyone else.
<form class="w-50">
  <div class="form-group ">
    <label for="exampleInputEmail1">Email address</label>
    <input type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="name@example.com">
    <small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
  </div>
  <div class="form-group">
    <label for="exampleInputPassword1">Password</label>
    <input type="password" class="form-control" id="exampleInputPassword1">
  </div>
  <div class="form-group custom-control custom-checkbox">
    <input type="checkbox" class="custom-control-input" id="customCheck1A">
    <label class="custom-control-label" for="customCheck1A">Check this checkbox</label>
  </div>
  <button type="submit" class="btn btn-primary">Submit</button>
</form>

Form controls

Textual form controls—like `input`s, `select`s, and `textarea`s—are styled with the `.form-control` class. Included are styles for general appearance, focus state, sizing, and more.

<form class="w-50">
  <div class="form-group">
    <label for="exampleFormControlInput1">Email address</label>
    <input type="email" class="form-control" id="exampleFormControlInput1" placeholder="name@example.com">
  </div>
  <div class="form-group">
    <label for="exampleFormControlSelect1">Example select</label>
    <select class="form-control" id="exampleFormControlSelect1">
      <option>1</option>
      <option>2</option>
      <option>3</option>
      <option>4</option>
      <option>5</option>
    </select>
  </div>
  <div class="form-group">
    <label for="exampleFormControlSelect2">Example multiple select</label>
    <select multiple class="form-control" id="exampleFormControlSelect2">
      <option>1</option>
      <option>2</option>
      <option>3</option>
      <option>4</option>
      <option>5</option>
    </select>
  </div>
  <div class="form-group">
    <label for="exampleFormControlTextarea1">Example textarea</label>
    <textarea class="form-control" id="exampleFormControlTextarea1" rows="3"></textarea>
  </div>
</form>

For file inputs, swap the `.form-control` for `.form-control-file`.

<form class="w-50">
  <div class="custom-file">
    <input type="file" class="custom-file-input" id="customFile">
    <label class="custom-file-label" for="customFile">Choose file</label>
  </div>
</form>

Sizing

Set heights using classes like `.form-control-lg` and `.form-control-sm`.

<input class="form-control form-control-lg w-50" type="text" aria-label="" placeholder="Large">
<input class="form-control w-50" type="text" aria-label="" placeholder="Default">
<input class="form-control form-control-sm w-50" type="text" aria-label="" placeholder="Small">
<select class="form-control form-control-lg w-50" aria-label="">
  <option>Large select</option>
</select>
<select class="form-control w-50" aria-label="">
  <option>Default select</option>
</select>
<select class="form-control form-control-sm w-50" aria-label="">
  <option>Small select</option>
</select>

Readonly

Add the `readonly` boolean attribute on an input to prevent modification of the input's value. Read-only inputs appear lighter (just like disabled inputs), but retain the standard cursor.

<input class="form-control w-50" type="text" placeholder="Readonly input here..." aria-label="" readonly>

Readonly plain text

If you want to have `input readonly` elements in your form styled as plain text, use the `.form-control-plaintext` class to remove the default form field styling and preserve the correct margin and padding.

<form class="w-50">
  <div class="form-group row">
    <label for="staticEmail" class="col-sm-2 col-form-label">Email</label>
    <div class="col-sm-10">
      <input type="text" readonly class="form-control-plaintext" id="staticEmail" value="email@example.com">
    </div>
  </div>
  <div class="form-group row">
    <label for="inputPassword" class="col-sm-2 col-form-label">Password</label>
    <div class="col-sm-10">
      <input type="password" class="form-control" id="inputPassword">
    </div>
  </div>
</form>
<form class="form-inline w-50">
  <div class="form-group mb-2">
    <label for="staticEmail2" class="sr-only">Email</label>
    <input type="text" readonly class="form-control-plaintext" id="staticEmail2" value="email@example.com">
  </div>
  <div class="form-group mx-sm-3 mb-2">
    <label for="inputPassword2" class="sr-only">Password</label>
    <input type="password" class="form-control" id="inputPassword2" placeholder="Password">
  </div>
  <button type="submit" class="btn btn-primary mb-2">Confirm identity</button>
</form>

Range Inputs

Sliders are best used when approximating numbers. They are best implemented with set increments, like 0, 25, 50,75, and 100. Sliders should rarely be used when trying to dial in an exact number. Sliders can be used when numbers can be close but do not need to be exact, like when approximating price for a search.

Be careful when using sliders, as they can be imprecise and frustrating if not used correctly. Often, this can be overcome by having vertical ticks to show users where the next increment is, and programmatically, the slider should snap to that tick when clicked.

Set horizontally scrollable range inputs using `.form-control-range`.

<form class="w-50">
  <div class="form-group">
    <label for="customRange1">Example range</label>
    <input type="range" class="custom-range" id="customRange1">
  </div>
</form>

Stair Stepper

Stair steppers are useful when a user needs to quickly add or delete numbers. Quantity is one of the best examples of this, where a user might want to add 1, 2 more of the same item.

However, stair steppers become impractical when users might need to move units in larger quantities, such as 10. Further, stair steppers should support an open field, where a user can type/tap to enter a specific number.

When using the stair stepper, the input field must have a default value

The javascript that powers this is probably unique to the application you are building. For that reason, there is no built in javascript for this type of behavior. The JS that powers this example is below. You can modify it to fit your application's needs.

<div id="stair-stepper-demo" class="stair-stepper row no-gutters">
  <div class="col-auto">
    <button type="button" class="btn btn-secondary btn-icon" aria-label="Decrease Quantity"  data-change="down"><i class="far fa-minus"></i></button>
  </div><!-- end col auto -->

  <div class="col-auto">
    <input type="number" class="form-control" aria-label="quantity" placeholder="0" value="5" style="max-width: 60px;">
  </div><!-- end col auto -->

  <div class="col-auto">
    <button type="button" class="btn btn-secondary btn-icon" aria-label="Increase Quantity" data-change="up"><i class="far fa-plus"></i></button>
  </div><!-- end col auto -->
</div><!-- end stair stepper demo -->

<script>
  (function () {
    // This self invoking function keeps the below (generic) variables out of the global scope
    // Feel free to build this with unique values for your own project
    var stairStepper = document.getElementById("stair-stepper-demo");
    var buttons = stairStepper.querySelectorAll("button");
    var input = stairStepper.querySelector("input");
    var currentNumber = parseInt(input.value);

    buttons.forEach(function(el) {
      el.addEventListener("click", function() {
        if(el.getAttribute("data-change") === "up") {
          currentNumber++;
        } else {
          currentNumber--;
        }

        input.value = String(currentNumber);
      })
    })
  }());
</script>

Date Picker

A date picker has two elements: a date field; and an icon representing a calendar that when clicked allows a user to select a specific date. We use a fairly standard HTML date picker. Date ranges should always be relevant to the users; they should neither be too far in the past nor too far in the future to be relevant. Further, dates that cannot be selected should be greyed out.

Finally, the date picker field, should the user manually enter a date, should auto-format.

We use a third party library called Pikaday for this. You can read more about their documentation here.

Since this is rarely used on the site, it is not included into the core library. You can reference it via our CDN.

The parent container of the input field must have their CSS position set to 'relative' to keep the icon from floating out into space. The page must also have enough height to create a scrollbar on the side. This is a current bug in the pikaday library. If you dont have a scrollbar, the calendar popup is slightly mis-aligned. It also doesn't have any new positioning abilities if the window is resized. The example below closes the popup if the window is resized. A use case for that would be if a user changed the orientation of their phone.

Pikaday doesnt format the date view for the users very well. In this example, there are two input fields, one called "visual" and one called "data". The visual date field is formatted like "mm/dd/yyyy" while the data version is formatted like "yyyy-mm-dd" for the backend to process correctly.

<style>
  body {
    overflow-y: scroll;
  }
</style>

<div id="pikaday-demo" class="row justify-content-between">
  <div class="col-lg-3 mb-5">
    <div class="position-relative">
      <input class="form-control pikaday-visual-date" id="pikaday-visual-date" type="text" placeholder="mm/dd/yyyy" aria-label="Choose a date" required>
      <i id="pikaday-visual-date-icon" class="far fa-calendar-alt pikaday-visual-date-icon"></i>
      <input type="hidden" name="post-name-here" id="pikaday-data-date">
    </div><!-- holding container for icon -->
  </div><!-- end col -->

  <style>
    .pikaday-multi-select {
      position: relative;
      display: -ms-flexbox;
      display: flex;
      -ms-flex-align: center;
      align-items: center;
      max-width: 300px;
    }

    .pikaday-multi-select .pikaday-visual-date-start {
      border-right: 0;
      border-top-right-radius: 0;
      border-bottom-right-radius: 0;
      max-width: 125px;
    }

    .pikaday-multi-select .pikaday-visual-date-end {
      border-left: 0;
      border-top-left-radius: 0;
      border-bottom-left-radius: 0;
    }

    .pikaday-multi-select .pikaday-multi-select-progress-arrow {
      border-top: 1px solid #222;
      border-bottom: 1px solid #222;
      padding: 8px 0;
      color: #666666;
      font-size: 20px;
    }

  </style>

  <div class="col-lg-6 mb-5">
    <div class="pikaday-multi-select d-flex align-items-center">
      <input class="form-control pikaday-visual-date pikaday-visual-date-start"
             id="pikaday-multi-visual-date-start"
             type="text"
             placeholder="mm/dd/yyyy"
             aria-label="Choose a starting date" required>
      <input type="hidden" name="post-name-here" id="pikaday-multi-data-date-start">

      <i class="fal fa-long-arrow-right pikaday-multi-select-progress-arrow"></i>

      <input class="form-control pikaday-visual-date pikaday-visual-date-end"
             id="pikaday-multi-visual-date-end"
             type="text"
             placeholder="mm/dd/yyyy"
             aria-label="Choose an ending date" required>
      <input type="hidden" name="post-name-here" id="pikaday-multi-data-date-end">

      <i id="pikaday-multi-visual-date-icon" class="far fa-calendar-alt pikaday-visual-date-icon"></i>
    </div>
  </div><!-- end col -->

  <div class="col-lg-3 mb-5"></div>
</div><!-- end stair stepper demo -->

<script>
  // start pikaday datepicker code
  // returns d m yy as object
  function formatDate(date) {
    // Calculate day, adding a leading 0 if its a single digit
    var day = String(date.getDate());
    day = day.length === 1 ? "0" + day : day;

    // Calculate month, adding a leading 0 if its a single digit
    var month = String(date.getMonth() + 1);
    month = month.length === 1 ? "0" + month : month;

    // Calculate year
    var year = String(date.getFullYear());

    return {
      day: day,
      month: month,
      year: year
    }
  }

  function formatDateVisual(date) {
    var currentDate = formatDate(date);
    return currentDate.month + "/" + currentDate.day + "/" + currentDate.year;
  }

  function formatDateData(date) {
    var currentDate = formatDate(date);
    return currentDate.year +  "-" + currentDate.month + "-" + currentDate.day;
  }

  var pickerSingle = new Pikaday({
    field: document.getElementById('pikaday-visual-date'),
    reposition: false,
    position: "bottom right",
    // Formats the users input. This acts like a callback
    toString: function(date) {
      // Create a version for the user, and a version for the backend
      var formatVisual = formatDateVisual(date);

      // This value is what gets submitted through the API
      document.getElementById("pikaday-data-date").value = formatDateData(date);

      // This is what the user sees.
      return formatVisual;
    }
  });

  // Set today
  var today = new Date();
  //picker.setDate(today);
  pickerSingle.setMaxDate(today);

  // If orientation changes or screen size is changed, the Pikaday library doesnt automatically update position
  window.addEventListener("resize", function() {
    pickerSingle.hide()
  })


  var pickerMultiStart = new Pikaday({
    field: document.getElementById('pikaday-multi-visual-date-start'),
    reposition: false,
    position: "bottom right",
    // Formats the users input. This acts like a callback
    toString: function(date) {
      // Create a version for the user, and a version for the backend
      var formatVisual = formatDateVisual(date);

      // This value is what gets submitted through the API
      document.getElementById("pikaday-multi-data-date-start").value = formatDateData(date);

      // Lock the min date
      var newMinDate = new Date(date);
      pickerMultiEnd.setMinDate(newMinDate);

      // This is what the user sees.
      return formatVisual;
    }
  });


  var pickerMultiEnd = new Pikaday({
    field: document.getElementById('pikaday-multi-visual-date-end'),
    reposition: false,
    position: "bottom right",
    // Formats the users input. This acts like a callback
    toString: function(date) {
      // Create a version for the user, and a version for the backend
      var formatVisual = formatDateVisual(date);

      // This value is what gets submitted through the API
      document.getElementById("pikaday-multi-data-date-end").value = formatDateData(date);

      // Lock the max date
      var newMinDate = new Date(date);
      pickerMultiStart.setMaxDate(newMinDate);

      // This is what the user sees.
      return formatVisual;
    }
  });
</script>

Checkboxes and Radios

Controls are used for a variety of purposes: making selections; toggling options; adjusting settings. Use the correct control for the correct task. Each has a distinct purpose and function, based on the logic required for filtering or selecting.

Clicking on the label should activate the action. The specifics are mentioned below, but generally: "I can select a few different options" or "I have to agree to something" - Check Box(es) "I can only select one option from a list of options" - Radio button Binary Choices, often settings, like Show/Hide - Toggle. Disabled checkboxes and radios are supported. The `disabled` attribute will apply a lighter color to help indicate the input's state.

Default checkboxes and radios are improved upon with the help of `.form-check`, **a single class for both input types that improves the layout and behavior of their HTML elements**.

Checkboxes and radio buttons support HTML-based form validation and provide concise, accessible labels. As such, our `input`s and `label`s are sibling elements as opposed to an `input` within a `label`. This is slightly more verbose as you must specify `id` and `for` attributes to relate the `input` and `label`.

Default (stacked)

Checkboxes allow the user to select multiple entries from a list that are independent. This means a user can select one box without deselecting another. Generally, checkboxes shouldn’t have more than 6-8 options to lower cognitive load.

Checkboxes, and only checkboxes, use a check inside of a square or a rounded square. Stack vertically with enough whitespace to differentiate options clearly.

By default, any number of checkboxes and radios that are immediate sibling will be vertically stacked and appropriately spaced with `.form-check`.

<div class="custom-control custom-checkbox mb-2">
  <input type="checkbox" class="custom-control-input" id="customCheck3">
  <label class="custom-control-label" for="customCheck3">Check this checkbox</label>
</div>

<div class="custom-control custom-checkbox">
  <input type="checkbox" class="custom-control-input" id="customCheckDisabled2" disabled>
  <label class="custom-control-label" for="customCheckDisabled2">This checkbox is disabled</label>
</div>

Radio Buttons are used to choose one option from a set of mutually exclusive items. Selecting one item deselects other options. Users can only choose one options at a time. Stack vertically as much as possible, with enough whitespace to differentiate between options.

Radio buttons are circles, and selected radio buttons change color with an interior circle to show selection.

<div class="custom-control custom-radio mb-2">
  <input type="radio" id="customRadio1A" name="customRadio" class="custom-control-input">
  <label class="custom-control-label" for="customRadio1A">Toggle this custom radio</label>
</div>

<div class="custom-control custom-radio mb-2">
  <input type="radio" id="customRadio2A" name="customRadio" class="custom-control-input">
  <label class="custom-control-label" for="customRadio2A">Or toggle this other custom radio</label>
</div>

<div class="custom-control custom-radio">
  <input type="radio" name="radioDisabled" id="customRadioDisabled2B" class="custom-control-input" disabled>
  <label class="custom-control-label" for="customRadioDisabled2B">This radio is disabled</label>
</div>

Inline

Because of certain design constraints or other layout conditions, we may have to use inline buttons. If possible, this design style should be avoided, as it can create a scenario where a label looks like it belongs to the checkbox either before or after the text. In order to mitigate this challenge, make sure to space inline buttons far enough apart to make it abundantly clear which label the checkbox or radio control belongs.

Group checkboxes or radios on the same horizontal row by adding `.form-check-inline` to any `.form-check`.

<div class="custom-control custom-checkbox custom-control-inline">
  <input type="checkbox" class="custom-control-input" id="customCheck99">
  <label class="custom-control-label" for="customCheck99">Inline Checkbox</label>
</div>

<div class="custom-control custom-checkbox custom-control-inline">
  <input type="checkbox" class="custom-control-input" id="customCheck991">
  <label class="custom-control-label" for="customCheck991">Inline Checkbox</label>
</div>

<div class="custom-control custom-checkbox custom-control-inline">
  <input type="checkbox" class="custom-control-input" id="customCheck992">
  <label class="custom-control-label" for="customCheck992">Inline Checkbox</label>
</div>
<div class="custom-control custom-radio custom-control-inline">
  <input type="radio" id="customRadioInline5" name="customRadioInline5" class="custom-control-input">
  <label class="custom-control-label" for="customRadioInline5">Toggle this custom radio</label>
</div>
<div class="custom-control custom-radio custom-control-inline">
  <input type="radio" id="customRadioInline6" name="customRadioInline5" class="custom-control-input">
  <label class="custom-control-label" for="customRadioInline6">Or toggle this other custom radio</label>
</div>

Without labels

Add `.position-static` to inputs within `.form-check` that don't have any label text. Remember to still provide some form of accessible name for assistive technologies (for instance, using `aria-label`).

<div class="custom-control custom-checkbox">
  <input type="checkbox" class="custom-control-input" id="customCheck12211123" aria-label="">
  <label class="custom-control-label" for="customCheck12211123"></label>
</div>

<div class="custom-control custom-checkbox">
  <input type="checkbox" class="custom-control-input" id="customCheck12232323" aria-label="">
  <label class="custom-control-label" for="customCheck12232323"></label>
</div>

<div class="custom-control custom-radio">
  <input type="radio" id="customRadio1123123232" name="customRadio999" class="custom-control-input" aria-label="">
  <label class="custom-control-label" for="customRadio1123123232"></label>
</div>
<div class="custom-control custom-radio">
  <input type="radio" id="customRadio11231232322" name="customRadio999" class="custom-control-input" aria-label="">
  <label class="custom-control-label" for="customRadio11231232322"></label>
</div>
<div class="custom-control custom-radio">
  <input type="radio" id="customRadio11231232323" name="customRadio999" class="custom-control-input" aria-label="">
  <label class="custom-control-label" for="customRadio11231232323"></label>
</div>

Switches

Toggle switches set an option to be either on/off, which can be thought of as active vs. deactive. An off or deactive state is grey, but an active or on switch is denoted by both a success icon and a color change.

A switch has the markup of a custom checkbox but uses the `.custom-switch` class to render a toggle switch. Switches also support the `disabled` attribute.

<div class="custom-control custom-switch mb-5">
  <input type="checkbox" class="custom-control-input" id="customSwitch1" aria-label="Toggle this switch element">
  <label class="custom-control-label" for="customSwitch1"><span></span></label>
</div>

Pills

Pills are often used for both tagging and filtering methods. For example, a blog post might be tagged under “News” and “Feature”, in which case two pills with those respective attributes would be displayed. Additionally, a user might select from a list both “Pepperoni” and “Mushrooms”, adding and removing other options as they see fit.

Pills can serve as information providers, as well as actionable filtering options. The only difference is whether the user can clear a pill with a standard close action, such as the X. Notably, pills do not use a drop shadow.

There is no default action given to the pill 'close' button. This will be unique per project so this javascript has been left out of the library.

<div>
  <div class="btn-group btn-group-pill mb-5" role="group" aria-label="View or Remove the Product Category">
    <button type="button" class="btn bg-red text-white">Product Category</button>
    <button type="button" class="btn bg-red text-white"><i class="fal fa-times"></i></button>
  </div>
</div>

Layout

Since Cirrus applies `display: block` and `width: 100%` to almost all our form controls, forms will by default stack vertically. Additional classes can be used to vary this layout on a per-form basis.

Form groups

The `.form-group` class is the easiest way to add some structure to forms. It provides a flexible class that encourages proper grouping of labels, controls, optional help text, and form validation messaging. By default it only applies `margin-bottom`, but it picks up additional styles in `.form-inline` as needed. Use it with `fieldset`s, `div`s, or nearly any other element.

<form class="w-50">
  <div class="form-group">
    <label for="formGroupExampleInput">Example label</label>
    <input type="text" class="form-control" id="formGroupExampleInput" placeholder="Example input placeholder">
  </div>
  <div class="form-group">
    <label for="formGroupExampleInput2">Another label</label>
    <input type="text" class="form-control" id="formGroupExampleInput2" placeholder="Another input placeholder">
  </div>
</form>

Form grid

More complex forms can be built using our grid classes. Use these for form layouts that require multiple columns, varied widths, and additional alignment options.

<form class="w-50">
  <div class="form-row">
    <div class="form-group col-md-6">
      <label for="inputEmail4">Email</label>
      <input type="email" class="form-control" id="inputEmail4">
    </div>
    <div class="form-group col-md-6">
      <label for="inputPassword4">Password</label>
      <input type="password" class="form-control" id="inputPassword4">
    </div>
  </div>
  <div class="form-group">
    <label for="inputAddress">Address</label>
    <input type="text" class="form-control" id="inputAddress" placeholder="1234 Main St">
  </div>
  <div class="form-group">
    <label for="inputAddress2">Address 2</label>
    <input type="text" class="form-control" id="inputAddress2" placeholder="Apartment, studio, or floor">
  </div>
  <div class="form-row">
    <div class="form-group col-md-6">
      <label for="inputCity">City</label>
      <input type="text" class="form-control" id="inputCity">
    </div>
    <div class="form-group col-md-4">
      <label for="inputState">State</label>
      <select id="inputState" class="form-control">
        <option selected>Choose...</option>
        <option>...</option>
      </select>
    </div>
    <div class="form-group col-md-2">
      <label for="inputZip">Zip</label>
      <input type="text" class="form-control" id="inputZip">
    </div>
  </div>
  <div class="form-group">
    <div class="custom-control custom-checkbox">
      <input type="checkbox" class="custom-control-input" id="customCheck178678786">
      <label class="custom-control-label" for="customCheck178678786">Check me!</label>
    </div>
  </div>
  <button type="submit" class="btn btn-primary">Sign in</button>
</form>

Horizontal form

Create horizontal forms with the grid by adding the `.row` class to form groups and using the `.col-*-*` classes to specify the width of your labels and controls. Be sure to add `.col-form-label` to your `label`s as well so they're vertically centered with their associated form controls.

At times, you maybe need to use margin or padding utilities to create that perfect alignment you need. For example, we've removed the `padding-top` on our stacked radio inputs label to better align the text baseline.

Radios
<form class="w-50">
  <div class="form-group row">
    <label for="inputEmail3" class="col-sm-2 col-form-label">Email</label>
    <div class="col-sm-10">
      <input type="email" class="form-control" id="inputEmail3" placeholder="name@example.com">
    </div>
  </div>
  <div class="form-group row">
    <label for="inputPassword3" class="col-sm-2 col-form-label">Password</label>
    <div class="col-sm-10">
      <input type="password" class="form-control" id="inputPassword3">
    </div>
  </div>
  <fieldset class="form-group">
    <legend class="col-form-label pt-0">Radios</legend>
    <div class="row">
      <div class="col-sm-10">
        <div class="custom-control custom-radio">
          <input type="radio" id="customRadio1123123" name="customRadio" class="custom-control-input">
          <label class="custom-control-label" for="customRadio1123123">Toggle this custom radio</label>
        </div>
        <div class="custom-control custom-radio">
          <input type="radio" id="customRadio22358872B" name="customRadio" class="custom-control-input">
          <label class="custom-control-label" for="customRadio22358872B">Or toggle this other custom radio</label>
        </div>
        <div class="custom-control custom-radio">
            <input type="radio" id="customRadio2123232C" name="customRadio" class="custom-control-input">
            <label class="custom-control-label" for="customRadio2123232C">Or toggle this other custom radio</label>
        </div>

      </div>
    </div>
  </fieldset>
  <div class="form-group">
    <div class="custom-control custom-checkbox">
      <input type="checkbox" class="custom-control-input" id="customCheck17879879">
      <label class="custom-control-label" for="customCheck17879879">Check this checkbox</label>
    </div>
  </div>
  <div class="form-group row">
    <div class="col-sm-10">
      <button type="submit" class="btn btn-primary">Sign in</button>
    </div>
  </div>
</form>
Horizontal form label sizing

Be sure to use `.col-form-label-sm` or `.col-form-label-lg` to your `label`s or `legend`s to correctly follow the size of `.form-control-lg` and `.form-control-sm`.

<form class="w-50">
  <div class="form-group row">
    <label for="colFormLabelSm" class="col-sm-2 col-form-label col-form-label-sm">Email</label>
    <div class="col-sm-10">
      <input type="email" class="form-control form-control-sm" id="colFormLabelSm" placeholder="Small">
    </div>
  </div>
  <div class="form-group row">
    <label for="colFormLabel" class="col-sm-2 col-form-label">Email</label>
    <div class="col-sm-10">
      <input type="email" class="form-control" id="colFormLabel" placeholder="Default">
    </div>
  </div>
  <div class="form-group row">
    <label for="colFormLabelLg" class="col-sm-2 col-form-label col-form-label-lg">Email</label>
    <div class="col-sm-10">
      <input type="email" class="form-control form-control-lg" id="colFormLabelLg" placeholder="Large">
    </div>
  </div>
</form>
Alternatives to hidden labels

Assistive technologies such as screen readers will have trouble with your forms if you don't include a label for every input. For these inline forms, you can hide the labels using the `.sr-only` class. There are further alternative methods of providing a label for assistive technologies, such as the `aria-label`, `aria-labelledby` or `title` attribute. If none of these are present, assistive technologies may resort to using the `placeholder` attribute, if present, but note that use of `placeholder` as a replacement for other labelling methods is not advised.

Help text

Block-level help text in forms can be created using `.form-text` (previously known as `.help-block` in v3). Inline help text can be flexibly implemented using any inline HTML element and utility classes like `.text-muted`.

Associating help text with form controls

Help text should be explicitly associated with the form control it relates to using the `aria-describedby` attribute. This will ensure that assistive technologies—such as screen readers—will announce this help text when the user focuses or enters the control.

Help text below inputs can be styled with `.form-text`. This class includes `display: block` and adds some top margin for easy spacing from the inputs above.

Your password must be 8-20 characters long, contain letters and numbers, and must not contain spaces, special characters, or emoji.
<label for="inputPassword5">Password</label>
<input type="password" id="inputPassword5" class="form-control w-50" aria-describedby="passwordHelpBlock">
<small id="passwordHelpBlock" class="form-text text-muted">
  Your password must be 8-20 characters long, contain letters and numbers, and must not contain spaces, special characters, or emoji.
</small>

Inline text can use any typical inline HTML element (be it a `small`, `span`, or something else) with nothing more than a utility class.

Must be 8-20 characters long.
<form class="form-inline">
  <div class="form-group">
    <label for="inputPassword6">Password</label>
    <input type="password" id="inputPassword6" class="form-control mx-sm-3" aria-describedby="passwordHelpInline">
    <small id="passwordHelpInline" class="text-muted">
      Must be 8-20 characters long.
    </small>
  </div>
</form>

Disabled forms

Add the `disabled` attribute on an input to prevent user interactions and make it appear lighter.

<input class="form-control" id="disabledInput" type="text" placeholder="Disabled input here..." aria-label="" disabled>

Add the `disabled` attribute to a `fieldset` to disable all the controls within.

<form class="w-50">
  <fieldset disabled>
    <div class="form-group">
      <label for="disabledTextInput">Disabled input</label>
      <input type="text" id="disabledTextInput" class="form-control" placeholder="Disabled input">
    </div>
    <div class="form-group">
      <label for="disabledSelect">Disabled select menu</label>
      <select id="disabledSelect" class="form-control">
        <option>Disabled select</option>
      </select>
    </div>
    <div class="form-group">
      <div class="custom-control custom-checkbox">
        <input type="checkbox" class="custom-control-input" id="customCheckDisabled1A" disabled>
        <label class="custom-control-label" for="customCheckDisabled1A">Check this custom checkbox</label>
      </div>
    </div>
    <button type="submit" class="btn btn-primary">Submit</button>
  </fieldset>
</form>
Caveat with anchors

Browsers treat all native form controls (`input`, `select`, and `button` elements) inside a `fieldset disabled` as disabled, preventing both keyboard and mouse interactions on them.

However, if your form also includes custom button-like elements such as `a ... class="btn btn-*"`, these will only be given a style of `pointer-events: none`. As noted in the section about disabled state for buttons (and specifically in the sub-section for anchor elements), this CSS property is not yet standardized and isn't fully supported in Internet Explorer 10. The anchor-based controls will also still be focusable and operable using the keyboard. You must manually modify these controls by adding `tabindex="-1"` to prevent them from receiving focus and `aria-disabled="disabled"` to signal their state to assistive technologies.

Cross-browser compatibility

While Cirrus will apply these styles in all browsers, Internet Explorer 11 and below don't fully support the `disabled` attribute on a `fieldset`. Use custom JavaScript to disable the fieldset in these browsers.

Validation

Error messages should be obvious, near the field where the error occurred. Second, error messages should give some explanation on how to fix the error or tell the user why the error exists.

Note the three main elements of the error messaging below: a surrounding color change; a useful and clear icon; and descriptive text. It is almost never okay to present errors in one big box at the top/bottom of the screen, devoid of location and context. Avoid vague language like "An error occurred."

We are aware that currently the client-side custom validation styles and tooltips are not accessible, since they are not exposed to assistive technologies. While we work on a solution, we'd recommend either using the server-side option or the default browser validation method.

Input group validation

Input groups have difficulty with validation styles, unfortunately. Our recommendation is to place feedback messages as sibling elements of the `.input-group` that has `.is-{valid|invalid}`. Placing feedback messages within input groups breaks the `border-radius`.

How it works

Here's how form validation works with Cirrus:

  • HTML form validation is applied via CSS's two pseudo-classes, `:invalid` and `:valid`. It applies to `input`, `select`, and `textarea` elements.
  • Cirrus scopes the `:invalid` and `:valid` styles to parent `.was-validated` class, usually applied to the `form`. Otherwise, any required field without a value shows up as invalid on page load. This way, you may choose when to activate them (typically after form submission is attempted).
  • To reset the appearance of the form (for instance, in the case of dynamic form submissions using AJAX), remove the `.was-validated` class from the `form` again after submission.
  • As a fallback, `.is-invalid` and `.is-valid` classes may be used instead of the pseudo-classes for server side validation. They do not require a `.was-validated` parent class.
  • Due to constraints in how CSS works, we cannot (at present) apply styles to a `label` that comes before a form control in the DOM without the help of custom JavaScript.
  • All modern browsers support the constraint validation API, a series of JavaScript methods for validating form controls.
  • Feedback messages may utilize the browser defaults (different for each browser, and unstylable via CSS) or our custom feedback styles with additional HTML and CSS.
  • You may provide custom validity messages with `setCustomValidity` in JavaScript.

With that in mind, consider the following demos for our custom form validation styles, optional server side classes, and browser defaults.

Browser defaults

Try submitting the form below. Depending on your browser and OS, you'll see a slightly different style of feedback. One limitation of browser default messaging is that only one field will highlight at a time.

While these feedback styles cannot be styled with CSS, you can still customize the feedback text through JavaScript.

<form class="w-50" novalidate>
  <div class="form-row">
    <div class="col-md-6 mb-3">
      <label for="validationDefault01">First name</label>
      <input type="text" class="form-control" id="validationDefault01" value="Mark" required>
    </div>
    <div class="col-md-6 mb-3">
      <label for="validationDefault02">Last name</label>
      <input type="text" class="form-control" id="validationDefault02" value="Otto" required>
    </div>
  </div>
  <div class="form-row">
    <div class="col-md-6 mb-3">
      <label for="validationDefault03">City</label>
      <input type="text" class="form-control" id="validationDefault03" required>
    </div>
    <div class="col-md-3 mb-3">
      <label for="validationDefault04">State</label>
      <select class="custom-select" id="validationDefault04" required>
        <option selected disabled value="">Choose...</option>
        <option>...</option>
      </select>
    </div>
    <div class="col-md-3 mb-3">
      <label for="validationDefault05">Zip</label>
      <input type="text" class="form-control" id="validationDefault05" required>
    </div>
  </div>
  <div class="form-group mb-5">
    <div class="custom-control custom-checkbox">
      <input type="checkbox" class="custom-control-input" id="invalidCheck2" required>
      <label class="custom-control-label" for="invalidCheck2">Agree to terms and conditions</label>
    </div>
  </div>
  <button class="btn btn-primary" type="submit">Submit form</button>
</form>

Custom styles

For custom Cirrus form validation messages, you'll need to add the `novalidate` boolean attribute to your `form`. This disables the browser default feedback tooltips, but still provides access to the form validation APIs in JavaScript. Try to submit the form below; our JavaScript will intercept the submit button and relay feedback to you. When attempting to submit, you'll see the `:invalid` and `:valid` styles applied to your form controls.

Custom feedback styles apply custom colors, borders, focus styles, and background icons to better communicate feedback. Background icons for `select`s are only available with `.custom-select`, and not `.form-control`.

Submit this form to try out the custom validation.

Looks good!
Looks good!
Please provide a valid city.
Please select a valid state.
Please provide a valid zip.
<form class="needs-validation w-50" novalidate>
  <div class="form-row">
    <div class="col-md-6 mb-3">
      <label for="validationCustom01">First name</label>
      <input type="text" class="form-control" id="validationCustom01" value="Mark" required>
      <div class="valid-feedback">
        Looks good!
      </div>
    </div>
    <div class="col-md-6 mb-3">
      <label for="validationCustom02">Last name</label>
      <input type="text" class="form-control" id="validationCustom02" value="Otto" required>
      <div class="valid-feedback">
        Looks good!
      </div>
    </div>
  </div>
  <div class="form-row">
    <div class="col-md-6 mb-3">
      <label for="validationCustom03">City</label>
      <input type="text" class="form-control" id="validationCustom03" required>
      <div class="invalid-feedback">
        Please provide a valid city.
      </div>
    </div>
    <div class="col-md-3 mb-3">
      <label for="validationCustom04">State</label>
      <select class="custom-select" id="validationCustom04" required>
        <option selected disabled value="">Choose...</option>
        <option>...</option>
      </select>
      <div class="invalid-feedback">
        Please select a valid state.
      </div>
    </div>
    <div class="col-md-3 mb-3">
      <label for="validationCustom05">Zip</label>
      <input type="text" class="form-control" id="validationCustom05" required>
      <div class="invalid-feedback">
        Please provide a valid zip.
      </div>
    </div>
  </div>
  <div class="form-group">
  <div class="custom-control custom-checkbox">
    <input type="checkbox" class="custom-control-input" id="customCheck19" required>
    <label class="custom-control-label" for="customCheck19">Check this checkbox</label>
  </div>

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

<script>
// Example starter JavaScript for disabling form submissions if there are invalid fields
(function() {
  'use strict';
  window.addEventListener('load', function() {
    // Fetch all the forms we want to apply custom Cirrus validation styles to
    var forms = document.getElementsByClassName('needs-validation');
    // Loop over them and prevent submission
    Array.prototype.filter.call(forms, function(form) {
      form.addEventListener('submit', function(event) {
        if (form.checkValidity() === false) {
          event.preventDefault();
          event.stopPropagation();
        }
        form.classList.add('was-validated');
      }, false);
    });
  }, false);
})();
</script>

Server side

We recommend using client-side validation, but in case you require server-side validation, you can indicate invalid and valid form fields with `.is-invalid` and `.is-valid`. Note that `.invalid-feedback` is also supported with these classes.

For invalid fields, ensure that the invalid feedback/error message is associated with the relevant form field using `aria-describedby`. This attribute allows more than one `id` to be referenced, in case the field already points to additional form text.

Looks good!
Looks good!
Please provide a valid city.
Please select a valid state.
Please provide a valid zip.
<form class="w-50">
  <div class="form-row">
    <div class="col-md-6 mb-3">
      <label for="validationServer01">First name</label>
      <input type="text" class="form-control is-valid" id="validationServer01" value="Mark" required>
      <div class="valid-feedback">
        Looks good!
      </div>
    </div>
    <div class="col-md-6 mb-3">
      <label for="validationServer02">Last name</label>
      <input type="text" class="form-control is-valid" id="validationServer02" value="Otto" required>
      <div class="valid-feedback">
        Looks good!
      </div>
    </div>
  </div>
  <div class="form-row">
    <div class="col-md-6 mb-3">
      <label for="validationServer03">City</label>
      <input type="text" class="form-control is-invalid" id="validationServer03" aria-describedby="validationServer03Feedback" required>
      <div id="validationServer03Feedback" class="invalid-feedback">
        Please provide a valid city.
      </div>
    </div>
    <div class="col-md-3 mb-3">
      <label for="validationServer04">State</label>
      <select class="custom-select is-invalid" id="validationServer04" aria-describedby="validationServer04Feedback" required>
        <option selected disabled value="">Choose...</option>
        <option>...</option>
      </select>
      <div id="validationServer04Feedback" class="invalid-feedback">
        Please select a valid state.
      </div>
    </div>
    <div class="col-md-3 mb-3">
      <label for="validationServer05">Zip</label>
      <input type="text" class="form-control is-invalid" id="validationServer05" aria-describedby="validationServer05Feedback" required>
      <div id="validationServer05Feedback" class="invalid-feedback">
        Please provide a valid zip.
      </div>
    </div>
  </div>

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

Supported elements

Interact with the components below to see their status change from invalid to valid.

Validation styles are available for the following form controls and components:

  • `input`s and `textarea`s with `.form-control`
  • `select`s with `.form-control` or `.custom-select`
  • `.form-check`s
  • `.custom-checkbox`s and `.custom-radio`s
  • `.custom-file`
Please enter a message in the textarea.
Example invalid feedback text
More example invalid feedback text
Example invalid custom select feedback
@
Example invalid input group feedback
Example invalid input group feedback
Example invalid input group feedback
<form class="was-validated w-50">
  <div class="mb-3">
    <label for="validationTextarea">Textarea</label>
    <textarea class="form-control is-invalid" id="validationTextarea" placeholder="Required example textarea" required></textarea>
    <div class="invalid-feedback">
      Please enter a message in the textarea.
    </div>
  </div>

  <div class="custom-control custom-checkbox mb-3">
    <input type="checkbox" class="custom-control-input" id="customControlValidation1" required>
    <label class="custom-control-label" for="customControlValidation1">Check this checkbox</label>
    <div class="invalid-feedback">Example invalid feedback text</div>
  </div>

  <div class="custom-control custom-radio">
    <input type="radio" class="custom-control-input" id="customControlValidation2" name="radio-stacked" required>
    <label class="custom-control-label" for="customControlValidation2">Toggle this custom radio</label>
  </div>
  <div class="custom-control custom-radio mb-3">
    <input type="radio" class="custom-control-input" id="customControlValidation3" name="radio-stacked" required>
    <label class="custom-control-label" for="customControlValidation3">Or toggle this other custom radio</label>
    <div class="invalid-feedback">More example invalid feedback text</div>
  </div>

  <div class="mb-3">
    <select class="custom-select" aria-label="" required>
      <option value="">Choose...</option>
      <option value="1">One</option>
      <option value="2">Two</option>
      <option value="3">Three</option>
    </select>
    <div class="invalid-feedback">Example invalid custom select feedback</div>
  </div>

  <div class="mb-3">
    <div class="input-group is-invalid">
      <div class="input-group-prepend">
        <span class="input-group-text" id="validatedInputGroupPrepend">@</span>
      </div>
      <input type="text" class="form-control is-invalid" aria-describedby="validatedInputGroupPrepend" aria-label="" required>
    </div>
    <div class="invalid-feedback">
      Example invalid input group feedback
    </div>
  </div>

  <div class="mb-3">
    <div class="input-group is-invalid">
      <div class="input-group-prepend">
        <label class="input-group-text" for="validatedInputGroupSelect">Options</label>
      </div>
      <select class="custom-select" id="validatedInputGroupSelect" required>
        <option value="">Choose...</option>
        <option value="1">One</option>
        <option value="2">Two</option>
        <option value="3">Three</option>
      </select>
    </div>
    <div class="invalid-feedback">
      Example invalid input group feedback
    </div>
  </div>
  <div class="invalid-feedback">
    Example invalid input group feedback
  </div>
</form>

Input group validation workaround

We're unable to resolve the broken `border-radius` of input groups with validation due to selector limitations, so manual overrides are required. When you're using a standard input group and don't customize the default border radius values, add `.rounded-right` to the elements with the broken `border-radius`.

<div class="input-group">
  <div class="input-group-prepend">
    <span class="input-group-text">@</span>
  </div>
  <input type="text" class="form-control rounded-right" aria-label="Username" required>
  <div class="invalid-feedback">
    Please choose a username.
  </div>
</div>
@
Please choose a username.

When you are using a small or large input group or customizing the default `border-radius` values, add custom CSS to the element with the busted `border-radius`.

/* Change values to match the radius of your form control */
.fix-rounded-right {
  border-top-right-radius: .2rem !important;
  border-bottom-right-radius: .2rem !important;
}
<div class="input-group input-group-sm">
  <div class="input-group-prepend">
    <span class="input-group-text">@</span>
  </div>
  <input type="text" class="form-control fix-rounded-right" aria-label="Username" required>
  <div class="invalid-feedback">
    Please choose a username.
  </div>
</div>
@
Please choose a username.

Custom Forms Explained

Checkboxes and radios

Each checkbox and radio `input` and `label` pairing is wrapped in a `div` to create our custom control. Structurally, this is the same approach as our default `.form-check`.

We use the sibling selector (`~`) for all our `input` states—like `:checked`—to properly style our custom form indicator. When combined with the `.custom-control-label` class, we can also style the text for each item based on the `input`'s state.

We hide the default `input` with `opacity` and use the `.custom-control-label` to build a new custom form indicator in its place with `::before` and `::after`. Unfortunately we can't build a custom one from just the `input` because CSS's `content` doesn't work on that element.

In the checked states, we use icons from FontAwesome. This provides us the best control for styling and positioning across browsers and devices.

Checkboxes

<div class="custom-control custom-checkbox">
  <input type="checkbox" class="custom-control-input" id="customCheck18">
  <label class="custom-control-label" for="customCheck18">Check this checkbox</label>
</div>

Radios

<div class="custom-control custom-radio">
  <input type="radio" id="customRadio159820829" name="customRadio" class="custom-control-input">
  <label class="custom-control-label" for="customRadio159820829">Toggle this custom radio</label>
</div>
<div class="custom-control custom-radio">
  <input type="radio" id="customRadio2D" name="customRadio" class="custom-control-input">
  <label class="custom-control-label" for="customRadio2D">Or toggle this other custom radio</label>
</div>

Inline

<div class="custom-control custom-radio custom-control-inline">
  <input type="radio" id="customRadioInline1" name="customRadioInline1" class="custom-control-input">
  <label class="custom-control-label" for="customRadioInline1">Toggle this custom radio</label>
</div>
<div class="custom-control custom-radio custom-control-inline">
  <input type="radio" id="customRadioInline2" name="customRadioInline1" class="custom-control-input">
  <label class="custom-control-label" for="customRadioInline2">Or toggle this other custom radio</label>
</div>

Disabled

Custom checkboxes and radios can also be disabled. Add the `disabled` boolean attribute to the `input` and the custom indicator and label description will be automatically styled.

<div class="custom-control custom-checkbox">
  <input type="checkbox" class="custom-control-input" id="customCheckDisabled1B" disabled>
  <label class="custom-control-label" for="customCheckDisabled1B">Check this checkbox</label>
</div>

<div class="custom-control custom-radio">
  <input type="radio" name="radioDisabled" id="customRadioDisabled2A" class="custom-control-input" disabled>
  <label class="custom-control-label" for="customRadioDisabled2A">Toggle this custom radio</label>
</div>

Select menu

Custom `select` menus need only a custom class, `.custom-select` to trigger the custom styles. Custom styles are limited to the `select`'s initial appearance and cannot modify the `option`s due to browser limitations.

<select class="custom-select w-50" aria-label="">
  <option selected>Open this select menu</option>
  <option value="1">One</option>
  <option value="2">Two</option>
  <option value="3">Three</option>
</select>

You may also choose from small and large custom selects to match our similarly sized text inputs.

<select class="custom-select custom-select-lg mb-3 w-50" aria-label="">
  <option selected>Open this select menu</option>
  <option value="1">One</option>
  <option value="2">Two</option>
  <option value="3">Three</option>
</select>

<select class="custom-select custom-select-sm w-50" aria-label="">
  <option selected>Open this select menu</option>
  <option value="1">One</option>
  <option value="2">Two</option>
  <option value="3">Three</option>
</select>

The `multiple` attribute is also supported:

<select class="custom-select w-50" multiple aria-label="">
  <option selected>Open this select menu</option>
  <option value="1">One</option>
  <option value="2">Two</option>
  <option value="3">Three</option>
</select>

As is the `size` attribute:

<select class="custom-select w-50" size="3" aria-label="">
  <option selected>Open this select menu</option>
  <option value="1">One</option>
  <option value="2">Two</option>
  <option value="3">Three</option>
</select>

Range

Create custom `input type="range"` controls with `.custom-range`. The track (the background) and thumb (the value) are both styled to appear the same across browsers. As only IE and Firefox support "filling" their track from the left or right of the thumb as a means to visually indicate progress, we do not currently support it.

<label for="customRange2">Example range</label>
<input type="range" class="custom-range w-50" id="customRange2">

Range inputs have implicit values for `min` and `max`—`0` and `100`, respectively. You may specify new values for those using the `min` and `max` attributes.

<label for="customRange3">Example range</label>
<input type="range" class="custom-range" min="0" max="5" id="customRange3">

By default, range inputs "snap" to integer values. To change this, you can specify a `step` value. In the example below, we double the number of steps by using `step="0.5"`.

<label for="customRange9">Example range</label>
<input type="range" class="custom-range" min="0" max="5" step="0.5" id="customRange9">

File browser

Enter at your own peril...

The recommended plugin to animate custom file input: bs-custom-file-input, that's what we are using currently here in our docs.

The file input is the most gnarly of the bunch and requires additional JavaScript if you'd like to hook them up with functional *Choose file...* and selected file name text.

      <div class="custom-file w-50">
        <input type="file" class="" id="customFile2">
        <label class="custom-file-label" for="customFile2">Choose file</label>
      </div>
      

We hide the default file `input` via `opacity` and instead style the `label`. The button is generated and positioned with `::after`. Lastly, we declare a `width` and `height` on the `input` for proper spacing for surrounding content.