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.
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.
For file inputs, swap the `.form-control` for `.form-control-file`.
Sizing
Set heights using classes like `.form-control-lg` and `.form-control-sm`.
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.
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.
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`.
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.
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.
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`.
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.
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`.
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`).
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.
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.
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 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.
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.
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`.
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.
Inline text can use any typical inline HTML element (be it a `small`, `span`, or something else) with nothing more than a utility class.
Disabled forms
Add the `disabled` attribute on an input to prevent user interactions and make it appear lighter.
Add the `disabled` attribute to a `fieldset` to disable all the controls within.
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.
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.
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.
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`
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`.
@
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`.
@
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
Radios
Inline
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.
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.
You may also choose from small and large custom selects to match our similarly sized text inputs.
The `multiple` attribute is also supported:
As is the `size` attribute:
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.
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.
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"`.
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.
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.