Forms
Examples and usage guidelines for form control styles, layout options, and custom components for creating a wide variety of forms.
Overview
Figuration's form controls expand on the Rebooted form styles with classes. Use these classes to opt into their customized displays for a more consistent rendering across browsers and devices.
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.
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.
Text Inputs
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.
<div class="form-group">
<label for="example-input">Example text input</label>
<input type="text" class="form-control" id="example-input" placeholder="Input text here">
</div>
<div class="form-group">
<label for="example-select-1">Example select</label>
<select class="form-control" id="example-select-1">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</select>
</div>
<div class="form-group">
<label for="example-select-2">Example multiple select</label>
<select multiple class="form-control" id="example-select-2">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</select>
</div>
<div class="form-group">
<label for="example-texarea">Example textarea</label>
<textarea class="form-control" id="example-texarea" rows="3"></textarea>
</div>
Control Sizing
By default, .form-control
uses em
units for sizing so that they will scale with their explicit font-size
.
You can also set heights and font-sizes using component sizing classes, such as:
.form-control-xsmall
.form-control-small
.form-control-large
.form-control-xlarge
<input class="form-control form-control-xlarge" type="text" placeholder="Extra large form control" aria-label=".form-control-xlarge size example">
<input class="form-control form-control-large" type="text" placeholder="Large form control" aria-label=".form-control-large size example">
<input class="form-control" type="text" placeholder="Default form control" aria-label="default size example">
<input class="form-control form-control-small" type="text" placeholder="Small form control" aria-label=".form-control-small size example">
<input class="form-control form-control-xsmall mb-2" type="text" placeholder="Extra small form control" aria-label=".form-control-xsmall size example">
<input class="form-control fs-large" type="text" placeholder="Explicit large font size" aria-label=".fs-large size example">
<input class="form-control fs-small" type="text" placeholder="Explicit small font size" aria-label=".fs-small size example">
The sizing classes also work on other inputs such as <select>
s and <textarea>
s.
<select class="form-control form-control-xlarge">
<option>Extra large select</option>
</select>
<select class="form-control form-control-large">
<option>Large select</option>
</select>
<select class="form-control">
<option>Default select</option>
</select>
<select class="form-control form-control-small">
<option>Small select</option>
</select>
<select class="form-control form-control-xsmall">
<option>Extra small select</option>
</select>
Readonly Inputs
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" type="text" placeholder="Readonly input" aria-label="Readonly input example" readonly>
Static Inputs
When you want to have readonly
fields in your form styled as plain text, use the .form-control-static
class to remove the default form field styling and preserve the correct margin and padding.
<div class="form-group row">
<label for="static-email" class="col-sm-2 form-label">Email</label>
<div class="col-sm-10">
<input type="text" readonly class="form-control-static" id="static-email" value="email@example.com">
</div>
</div>
<div class="form-group row">
<label for="static-password" class="col-sm-2 form-label">Password</label>
<div class="col-sm-10">
<input type="password" class="form-control" id="static-password" placeholder="Password">
</div>
</div>
<form class="form-inline">
<div class="form-group me-0_5">
<label for="inputEmail2" class="sr-only">Email</label>
<input type="text" readonly class="form-control-static" id="inputEmail2" value="email@example.com">
</div>
<div class="form-group me-0_5">
<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">Confirm identity</button>
</form>
Label, Legend, and Static Sizing
Just like sizing the form inputs, you can size <label>
s, <legends>
, and static controls with:
.form-label-xsmall
.form-label-small
.form-label-large
.form-label-xlarge
<div class="form-group flex-items-center row">
<label class="col-sm-2 form-label form-label-xsmall" for="labelstatic-1">Email</label>
<div class="col-sm-10">
<input type="text" class="form-control-static form-label-xsmall" id="labelstatic-1" value="email@example.com">
</div>
</div>
<div class="form-group flex-items-center row">
<label class="col-sm-2 form-label form-label-small" for="labelstatic-2">Email</label>
<div class="col-sm-10">
<input type="text" class="form-control-static form-label-small" id="labelstatic-2" value="email@example.com">
</div>
</div>
<div class="form-group flex-items-center row">
<label class="col-sm-2 form-label" for="labelstatic-3">Email</label>
<div class="col-sm-10">
<input type="text" class="form-control-static" id="labelstatic-3" value="email@example.com">
</div>
</div>
<div class="form-group flex-items-center row">
<label class="col-sm-2 form-label form-label-large" for="labelstatic-4">Email</label>
<div class="col-sm-10">
<input type="text" class="form-control-static form-label-large" id="labelstatic-4" value="email@example.com">
</div>
</div>
<div class="form-group flex-items-center row">
<label class="col-sm-2 form-label form-label-xlarge" for="labelstatic-5">Email</label>
<div class="col-sm-10">
<input type="text" class="form-control-static form-label-xlarge" id="labelstatic-5" value="email@example.com">
</div>
</div>
Disabled States
Add the disabled
attribute on an input to prevent user interactions and make it appear lighter in color.
<input class="form-control" id="disabled-input" type="text" placeholder="Disabled input" aria-label="Disabled input example" disabled>
<select class="form-control" aria-label="Disable select example" disabled>
<option>Disabled select example</option>
</select>
<textarea class="form-control" id="disabled-input" type="text" placeholder="Disabled textarea" aria-label="Disabled textarea example" disabled></textarea>
Add the disabled
attribute to a <fieldset>
to disable all the controls within.
<form>
<fieldset disabled>
<legend>Disabled fieldset example</legend>
<div class="form-group">
<label for="disabled-text">Disabled input</label>
<input type="text" id="disabled-text" class="form-control" placeholder="Disabled input">
</div>
<div class="form-group">
<label for="disabled-select">Disabled select menu</label>
<select id="disabled-select" class="form-control">
<option>Disabled select</option>
</select>
</div>
<div class="form-group form-check">
<input type="checkbox" class="form-check-input" id="disabled-check">
<label class="form-check-label" for="disabled-check">Can't check this</label>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</fieldset>
</form>
Caveat About Link Functionality of <a>
By default, browsers will 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 <a ... class="btn btn-*">
elements, these will only be given a style of pointer-events: none
. As noted in the accessibility section about disabled anchors, 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 Figuration 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.
Help Text
Block-level help text in forms can be created using .form-text
. 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.
<label for="help-pass">Password</label>
<input type="password" id="help-pass" class="form-control" aria-describedby="help-pass-text">
<div id="help-pass-text" class="form-text">
Your password must be 8-20 characters long, contain letters and numbers, and must not contain spaces, special characters, or emoji.
</div>
Inline text can use any typical inline HTML element (be it a <span>
, <small>
, or something else), optionally using utility classes.
<div class="form-inline">
<div class="form-group">
<label for="inline-pass" class="me-0_5">Password</label>
<input type="password" id="inline-pass" class="form-control me-0_5" aria-describedby="inline-pass-text">
<small id="inline-pass-text" class="text-muted">
Must be 8-20 characters long.
</small>
</div>
</div>
Checkboxes and Radios
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 are for selecting one or several options in a list, while radios are for selecting one option from many.
Disabled checkboxes and radios are supported by using the disabled
attribute on the <input>
, or by being inside a disabled <fieldset>
, and will lighten the text color of a sibling .form-check-label
to help indicate the input's state.
The .form-check
container has a padding-left
so that the siblings of the <input>
(such as <label>
, help text, or validation feedback) are easily aligned.
By default, any number of checkboxes and radios that are immediate sibling will be vertically stacked and appropriately spaced with .form-check
.
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="default-checkbox-1">
<label class="form-check-label" for="default-checkbox-1">Default checkbox</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="default-checkbox-2">
<label class="form-check-label" for="default-checkbox-2">Default checkbox</label>
<small class="text-muted d-block">Some additional help text could appear right here.</small>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="default-checkbox-3" disabled>
<label class="form-check-label" for="default-checkbox-3">Disabled checkbox</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="default-radio" value="" id="default-radio-1">
<label class="form-check-label" for="default-radio-1">First default radio</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="default-radio" value="" id="default-radio-2">
<label class="form-check-label" for="default-radio-2">Second default radio</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="default-radio" value="" id="default-radio-3" disabled>
<label class="form-check-label" for="default-radio-3">Disabled radio</label>
</div>
Custom Style
For even more customization and cross browser consistency, use our stylized form elements to replace the browser defaults.
Add the .form-checkradio
modifier class to a .form-check
to enable the stylized inputs. The visual treatment is determined from the type
attribute on the <input>
.
We hide the default <input>
with opacity
and use a <label>
element with .form-check-label
to build the stylized indicator in its place with ::before
and ::after
. Unfortunately we can't build a custom one from just the <input>
because CSS does not support content
, appearance
in IE 11, or pseudo-elements on that element.
We also use the sibling selector (~
) and the <input>
states—like :checked
—to style the form indicator and <label>
text for each item.
Stylized checkboxes can also utilize the :indeterminate
pseudo class when manually set via JavaScript (there is no available HTML attribute for specifying it).
In the checked and indeterminate states, we use icons from Open Iconic. This provides us the best control for styling and positioning across browsers and devices.
<div class="form-check form-checkradio">
<input class="form-check-input" type="checkbox" id="check0" checked>
<label class="form-check-label" for="check0">Custom checkbox</label>
<small class="text-muted d-block">Some additional help text could appear right here.</small>
</div>
<div class="form-check form-checkradio">
<input class="form-check-input" type="checkbox" id="check1">
<label class="form-check-label" for="check1">Indeterminate custom checkbox</label>
<small class="text-muted d-block">Indeterminate checkboxes must be toggled via JavaScript—there's no HTML attribute for this.</small>
</div>
<div class="form-check form-checkradio">
<input class="form-check-input" type="checkbox" id="check2">
<label class="form-check-label" for="check2">Custom checkbox with a really long label to see what layout becomes when the text content wraps to the next line, but this needs a large amount of text to make sure the wrapping occurs.</label>
</div>
<div class="form-check form-checkradio">
<input class="form-check-input" type="checkbox" id="check3" disabled>
<label class="form-check-label" for="check3">Disabled custom checkbox</label>
</div>
<div class="form-check form-checkradio">
<input class="form-check-input" type="checkbox" id="check4" disabled checked>
<label class="form-check-label" for="check4">Disabled checked custom checkbox</label>
</div>
<fieldset class="mt-1" disabled>
<legend class="form-label">Disabled fieldset</legend>
<div class="form-check form-checkradio">
<input class="form-check-input" type="checkbox" id="check5">
<label class="form-check-label" for="check5">Custom checkbox in a disabled fieldset</label>
</div>
<div class="form-check form-checkradio">
<input class="form-check-input" type="checkbox" id="check6" checked>
<label class="form-check-label" for="check6">Checked custom checkbox in a disabled fieldset</label>
</div>
</fieldset>
<div class="form-check form-checkradio">
<input class="form-check-input" type="radio" id="radio0" name="radios" checked>
<label class="form-check-label" for="radio0">Custom radio</label>
</div>
<div class="form-check form-checkradio">
<input class="form-check-input" type="radio" id="radio1" name="radios">
<label class="form-check-label" for="radio1">Custom radio</label>
</div>
<div class="form-check form-checkradio">
<input class="form-check-input" type="radio" id="radio2" name="radios" disabled>
<label class="form-check-label" for="radio2">Disabled custom radio</label>
</div>
<div class="form-check form-checkradio">
<input class="form-check-input" type="radio" id="radio3" name="radiosdis" disabled checked>
<label class="form-check-label" for="radio3">Disabled checked custom radio</label>
</div>
<fieldset class="mt-1" disabled>
<legend class="form-label">Disabled fieldset</legend>
<div class="form-check form-checkradio">
<input class="form-check-input" type="radio" id="radio4" name="radiosfsdisfs">
<label class="form-check-label" for="radio4">Custom radio in a disabled fieldset</label>
</div>
<div class="form-check form-checkradio">
<input class="form-check-input" type="radio" id="radio5" name="radiosfsdisfs" checked>
<label class="form-check-label" for="radio5">Checked custom radio in a disabled fieldset</label>
</div>
</fieldset>
For the indeterminate checkbox above we are using the following script:
document.getElementById("check1").indeterminate = true;
Switch
In similar fashion to the stylized checkbox and radio input, transform either of the input types to a stylized toggle switch by the using the .form-switch
modifier class instead.
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="switch0">
<label class="form-check-label" for="switch0">Custom switch checkbox</label>
<small class="text-muted d-block">Some additional help text could appear right here.</small>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="switch1" disabled>
<label class="form-check-label" for="switch1">Disabled custom switch checkbox</label>
<small class="text-muted d-block">Some additional help text could appear right here.</small>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="switch2" disabled checked>
<label class="form-check-label" for="switch2">Disabled checked custom switch checkbox</label>
<small class="text-muted d-block">Some additional help text could appear right here.</small>
</div>
<fieldset class="mt-1" disabled>
<legend class="form-label">Disabled fieldset</legend>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="switch3">
<label class="form-check-label" for="switch3">Custom switch checkbox in a disabled fieldset</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="switch4" checked>
<label class="form-check-label" for="switch4">Checked switch checkbox in a disabled fieldset</label>
</div>
</fieldset>
<div class="form-check form-switch">
<input class="form-check-input" type="radio" id="switchradio0" name="switchradio" checked>
<label class="form-check-label" for="switchradio0">Custom switch radio</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="radio" id="switchradio1" name="switchradio">
<label class="form-check-label" for="switchradio1">Custom switch radio</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="radio" id="switchradiods0" name="switchradiods" disabled checked>
<label class="form-check-label" for="switchradiods0">Disabled checked custom switch radio</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="radio" id="switchradiods1" name="switchradiods" disabled>
<label class="form-check-label" for="switchradiods1">Disabled custom switch radio</label>
</div>
<fieldset class="mt-1" disabled>
<legend class="form-label">Disabled fieldset</legend>
<div class="form-check form-switch">
<input class="form-check-input" type="radio" id="switchradiofs0" name="switchradiofs" checked>
<label class="form-check-label" for="switchradiofs0">Custom switch radio in a disabled fieldset</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="radio" id="switchradiofs1" name="switchradiofs">
<label class="form-check-label" for="switchradiofs1">Custom switch radio in a disabled fieldset</label>
</div>
</fieldset>
Without Labels
You can use .form-check
without labels, but you will still need to provide some form of label for assistive technologies (for instance, using aria-label
).
However, the stylized inputs will need to keep their label in order for the visual input to appear.
<div class="form-check">
<input class="form-check-input" type="checkbox" id="nolabel0" value="" aria-label="...">
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="nolabel-radio" id="nolabel-1" value="" aria-label="...">
</div>
<div class="form-check form-checkradio">
<input class="form-check-input" type="checkbox" id="nolabel2" value="">
<label class="form-check-label" for="nolabel2">
<span class="sr-only">Visually hidden label text</span>
</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="nolabel3" value="">
<label class="form-check-label" for="nolabel3">
<span class="sr-only">Visually hidden label text</span>
</label>
</div>
External Labels
Label can be supplied outside of the .form-check
container also.
<div class="row gx-0_5">
<label class="col-md-auto" for="extlabel0">External label</label>
<div class="col">
<div class="form-check form">
<input class="form-check-input" type="checkbox" id="extlabel0" checked>
<label class="form-check-label" for="extlabel0"></label>
</div>
</div>
</div>
<div class="row gx-0_5">
<label class="col-md-auto" for="extlabel1">External label</label>
<div class="col">
<div class="form-check form-checkradio">
<input class="form-check-input" type="checkbox" id="extlabel1" checked>
<label class="form-check-label" for="extlabel1"></label>
</div>
</div>
</div>
<div class="row gx-0_5">
<label class="col-md-auto" for="extlabel2">External label</label>
<div class="col">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="extlabel2" checked>
<label class="form-check-label" for="extlabel2"></label>
</div>
</div>
</div>
Inline
Group checkboxes or radios on the same horizontal row by using display
and margin
utility classes, such as .d-inline-block
and .me-1
.
<div class="form-check d-inline-block me-1">
<input class="form-check-input" type="checkbox" id="inline0" checked>
<label class="form-check-label" for="inline0">Inline checkbox</label>
</div>
<div class="form-check form-checkradio d-inline-block me-1">
<input class="form-check-input" type="checkbox" id="inline1" checked>
<label class="form-check-label" for="inline1">Inline custom checkbox</label>
</div>
<div class="form-check form-checkradio d-inline-block me-1">
<input class="form-check-input" type="radio" id="inline2" checked>
<label class="form-check-label" for="inline2">Inline custom radio</label>
</div>
<div class="form-check form-switch d-inline-block">
<input class="form-check-input" type="checkbox" id="inline3" checked>
<label class="form-check-label" for="inline3">Inline custom switch</label>
</div>
Sizing
Our custom checkbox, radio, and switch inputs use em
for sizing, so that they will scale with their explicit or inherited font-size
.
<div class="form-check form-checkradio fs-xlarge">
<input class="form-check-input" type="checkbox" id="resize0" checked>
<label class="form-check-label" for="resize0">Resized custom checkbox</label>
</div>
<div class="form-check form-checkradio">
<input class="form-check-input" type="radio" id="resize1" checked>
<label class="form-check-label fs-large" for="resize1">Resized custom radio</label>
</div>
<div class="fs-small">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="resize2" checked>
<label class="form-check-label" for="resize2">Resized custom switch</label>
</div>
</div>
You can also set a font-size
on the .form-check
container, then reset font-size
of content inside the <label>
.
<div class="form-check form-checkradio fs-xlarge">
<input class="form-check-input" type="checkbox" id="resizealt0" checked>
<label class="form-check-label" for="resize0"><span class="fs-base">Resized custom checkbox</span></label>
</div>
File Browser
Using .form-file
as a wrapper, we hide the default file <input>
via opacity
and instead style the <label>
with some additional child elements to recreate the filename text and button portions of the input. A width
and height
are also set on the <input>
for proper spacing for surrounding content.
The file input requires additional JavaScript if you would like to have a functional Choose file... and selected file name text.
<div class="form-file">
<input type="file" class="form-file-input" id="formFile">
<label class="form-file-label" for="formFile">
<span class="form-file-text">Choose file...</span>
<span class="form-file-button">Browse</span>
</label>
</div>
Add the disabled
attribute to the <input>
and the custom markup will be updated to appear disabled.
<div class="form-file">
<input type="file" class="form-file-input" id="formFileDisabled" disabled>
<label class="form-file-label" for="formFileDisabled">
<span class="form-file-text">Choose file...</span>
<span class="form-file-button">Browse</span>
</label>
</div>
Longer filename text is truncated and an ellipsis is added when there's not enough space.
<div class="form-file">
<input type="file" class="form-file-input" id="formFileLong">
<label class="form-file-label" for="formFileLong">
<span class="form-file-text">Lorem ipsum posuere consectetur est at lobortis nulla vitae elit libero a pharetra augue fusce dapibus tellus ac cursus commodo tortor mauris condimentum nibh ut fermentum massa justo sit amet risus cras mattis consectetur purus sit amet fermentum</span>
<span class="form-file-button">Browse</span>
</label>
</div>
Sizing
Use .form-file-*
sizing modifiers to adjust the file input to match the textual inputs.
<div class="form-file form-file-xsmall mb-1">
<input type="file" class="form-file-input" id="formFileSize0">
<label class="form-file-label" for="formFileSize0">
<span class="form-file-text">Extra small choose file...</span>
<span class="form-file-button">Browse</span>
</label>
</div>
<div class="form-file form-file-small mb-1">
<input type="file" class="form-file-input" id="formFileSize1">
<label class="form-file-label" for="formFileSize1">
<span class="form-file-text">Small choose file...</span>
<span class="form-file-button">Browse</span>
</label>
</div>
<div class="form-file form-file-large mb-1">
<input type="file" class="form-file-input" id="formFileSize2">
<label class="form-file-label" for="formFileSize2">
<span class="form-file-text">Large choose file...</span>
<span class="form-file-button">Browse</span>
</label>
</div>
<div class="form-file form-file-xlarge">
<input type="file" class="form-file-input" id="formFileSize3">
<label class="form-file-label" for="formFileSize3">
<span class="form-file-text">Extra large choose file...</span>
<span class="form-file-button">Browse</span>
</label>
</div>
Range
Create custom <input type="range">
controls with .form-range
. The track (the background) and thumb (the value) are both styled to appear the same across browsers. As only IE, Edge Legacy, 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. We also hide the tooltip provided only by IE to maintain cross-browser consistency.
<label for="customRange1">Example range</label>
<input type="range" class="form-range" id="customRange1">
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="customRange2">Example range</label>
<input type="range" class="form-range" min="0" max="5" id="customRange2">
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="customRange3">Example range</label>
<input type="range" class="form-range" min="0" max="5" step="0.5" id="customRange3">
Add the disabled
attribute to the <input>
and the custom markup will be updated to appear disabled.
<label for="customRange4">Disabled example range</label>
<input type="range" class="form-range" min="0" max="5" step="0.5" id="customRange4" disabled>
Color Picker
<input type="color">
element need only a custom class, .form-color
to trigger the custom styles.
Browser Compatibility
While Figuration supports styling <input type="color">
elements, some browsers don't. Use custom JavaScript to handle it in these browsers. For support details, see Can I Use.
<label for="customColor">Example color</label>
<input class="form-color" type="color" value="#117dba" id="customColor">
Layout
Since Figuration 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.
<div class="form-group">
<label class="form-label" for="formGroupExampleInput">Example label</label>
<input type="text" class="form-control" id="formGroupExampleInput" placeholder="Example input">
</div>
<div class="form-group">
<label class="form-label" for="formGroupExampleInput2">Another label</label>
<input type="text" class="form-control" id="formGroupExampleInput2" placeholder="Another input">
</div>
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.
Grid-based form layouts also support control sizing.
<div class="row">
<div class="col">
<label for="formGridExampleInput">First Name</label>
<input type="text" class="form-control" id="formGridExampleInput" placeholder="First name">
</div>
<div class="col">
<label for="formGridExampleInput2">Last Name</label>
<input type="text" class="form-control" id="formGridExampleInput2" placeholder="Last name">
</div>
</div>
Form Row
You may also swap .row
for .form-row
, a variation of our standard grid row that overrides the default column gutters for tighter and more compact layouts.
<div class="form-row">
<div class="col">
<input type="text" class="form-control" placeholder="First name" aria-label="First name">
</div>
<div class="col">
<input type="text" class="form-control" placeholder="Last name" aria-label="Last name">
</div>
</div>
More complex layouts can also be created with the grid system.
<form>
<div class="form-row">
<div class="form-group col-md-6">
<label for="grid-email">Email</label>
<input type="email" class="form-control" id="grid-email" placeholder="Email">
</div>
<div class="form-group col-md-6">
<label for="grid-pass">Password</label>
<input type="password" class="form-control" id="grid-pass" placeholder="Password">
</div>
</div>
<div class="form-group">
<label for="grid-address-1">Address</label>
<input type="text" class="form-control" id="grid-address-1" placeholder="1234 Main St">
</div>
<div class="form-group">
<label for="grid-address-2">Address 2</label>
<input type="text" class="form-control" id="grid-address-2" placeholder="Apartment, studio, or floor">
</div>
<div class="form-row">
<div class="form-group col-md-6">
<label for="grid-city">City</label>
<input type="text" class="form-control" id="grid-city">
</div>
<div class="form-group col-md-4">
<label for="grid-state">State</label>
<select id="grid-state" class="form-control">
<option selected>Choose...</option>
<option>...</option>
</select>
</div>
<div class="form-group col-md-2">
<label for="grid-zip">Zip</label>
<input type="text" class="form-control" id="grid-zip">
</div>
</div>
<div class="form-group form-check">
<input class="form-check-input" type="checkbox" id="grid-check">
<label class="form-check-label" for="grid-check">
Check me out
</label>
</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 .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.
<form>
<div class="form-group row">
<label for="hform-email" class="col-sm-2 form-label">Email</label>
<div class="col-sm-10">
<input type="email" class="form-control" id="hform-email" placeholder="Email">
</div>
</div>
<div class="form-group row">
<label for="hform-pass" class="col-sm-2 form-label">Password</label>
<div class="col-sm-10">
<input type="password" class="form-control" id="hform-pass" placeholder="Password">
</div>
</div>
<fieldset class="form-group row">
<legend class="form-label col-sm-2 pt-0">Radios</legend>
<div class="col-sm-10">
<div class="form-check">
<input class="form-check-input" type="radio" name="hform-radio" id="gridRadios1" value="option1" checked>
<label class="form-check-label" for="gridRadios1">
First radio
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="hform-radio" id="hform-radio-2" value="option2">
<label class="form-check-label" for="hform-radio-2">
Second radio
</label>
</div>
<div class="form-check disabled">
<input class="form-check-input" type="radio" name="hform-radio" id="hform-radio-3" value="option3" disabled>
<label class="form-check-label" for="hform-radio-3">
Third disabled radio
</label>
</div>
</div>
</fieldset>
<div class="form-group row">
<div class="col-sm-10 offset-sm-2">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="hform-check">
<label class="form-check-label" for="hform-check">
Example checkbox
</label>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-10 offset-sm-2">
<button type="submit" class="btn btn-primary">Sign in</button>
</div>
</div>
</form>
Column Sizing
As shown in the previous examples, our grid system allows you to place any number of .col
s within a .row
. They will split the available width equally between them. You may also pick a subset of your columns to take up more or less space, while the remaining .col
s equally split the rest, with specific column classes like .col-7
.
<div class="form-row">
<div class="col-7">
<input type="text" class="form-control" placeholder="City" aria-label="City">
</div>
<div class="col">
<input type="text" class="form-control" placeholder="State" aria-label="State">
</div>
<div class="col">
<input type="text" class="form-control" placeholder="Zip" aria-label="Zip">
</div>
</div>
Auto-sizing
The example below uses a flexbox utility to vertically center the contents and changes .col
to .col-auto
so that your columns only take up as much space as needed. Put another way, the column sizes itself based on the contents.
<form>
<div class="form-row flex-items-center">
<div class="col-auto mb-0_5">
<label class="sr-only" for="autosize-name">Name</label>
<input type="text" class="form-control" id="autosize-name" placeholder="Jane Doe">
</div>
<div class="col-auto mb-0_5">
<label class="sr-only" for="autosize-user">Username</label>
<div class="input-group">
<div class="input-group-text">@</div>
<input type="text" class="form-control" id="autosize-user" placeholder="Username">
</div>
</div>
<div class="col-auto mb-0_25">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="autosize-check">
<label class="form-check-label" for="autosize-check">Remember me</label>
</div>
</div>
<div class="col-auto mb-0_5">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
</form>
Here is the same form, but this time using specified column widths
<form>
<div class="form-row flex-items-center">
<div class="col-sm-3 mb-0_5">
<label class="sr-only" for="specsize-name">Name</label>
<input type="text" class="form-control" id="specsize-name" placeholder="Jane Doe">
</div>
<div class="col-sm-3 mb-0_5">
<label class="sr-only" for="specsize-user">Username</label>
<div class="input-group">
<div class="input-group-text">@</div>
<input type="text" class="form-control" id="specsize-user" placeholder="Username">
</div>
</div>
<div class="col-auto mb-0_25">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="specsize-check">
<label class="form-check-label" for="specsize-check">Remember me</label>
</div>
</div>
<div class="col-auto mb-0_5">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
</form>
<form>
<div class="form-row flex-items-center">
<div class="col mb-0_5">
<label class="sr-only" for="form-select">Preference</label>
<select class="form-control" id="form-select">
<option selected>Choose...</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
</div>
<div class="col-auto mb-0_25">
<div class="form-check form-checkradio">
<input type="checkbox" class="form-check-input" id="customize-check">
<label class="form-check-label" for="customize-check">Remember my preference</label>
</div>
</div>
<div class="col-auto mb-0_5">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
</form>
Inline Forms
Use the .form-inline
class to display a series of labels, form controls, and buttons on a single horizontal row. Form controls within inline forms vary slightly from their default states.
- Controls are
display: flex
, collapsing any HTML white space and allowing you to provide alignment control with spacing and flexbox utilities. - Controls and input groups receive
width: auto
to override the Figuration defaultwidth: 100%
. - Controls only appear inline in viewports that are at least 36em/576px wide to account for narrow viewports on mobile devices.
You may need to manually address the width and alignment of individual form controls with spacing utilities (as shown below). Lastly, as shown below, you should always include a <label>
with each form control, even if you need to hide it from non-screenreader users with .sr-only
.
<form class="form-inline">
<label class="sr-only" for="inline-name">Name</label>
<input type="text" class="form-control mb-0_5 me-sm-0_5" id="inline-name" placeholder="Jane Doe">
<label class="sr-only" for="inline-user">Username</label>
<div class="input-group mb-0_5 me-sm-0_5">
<div class="input-group-text">@</div>
<input type="text" class="form-control" id="inline-user" placeholder="Username">
</div>
<div class="form-check mb-0_5 me-sm-0_5">
<input class="form-check-input" type="checkbox" id="inline-checkbox-4">
<label class="form-check-label" for="inline-checkbox-4">Remember me</label>
</div>
<button type="submit" class="btn btn-primary mb-0_5">Submit</button>
</form>
Custom form controls and selects are also supported.
<form class="form-inline">
<label class="mb-0_5 me-0_5" for="inlineFormSelectPref">Preference</label>
<select class="form-control mb-0_5 me-sm-0_5" id="inlineFormSelectPref">
<option selected>Choose...</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
<div class="form-check form-checkradio mb-0_5 me-sm-0_5">
<input type="checkbox" class="form-check-input" id="customControlInline">
<label class="form-check-label" for="customControlInline">Remember my preference</label>
</div>
<button type="submit" class="btn btn-primary mb-0_5">Submit</button>
</form>
Validation
Provide valuable, actionable feedback to your users with HTML5 form validation–available in all our supported browsers. Choose from the browser default validation feedback, or implement custom messages with our built-in classes and starter JavaScript.
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.
How It Works
Here's how form validation works:
- HTML form validation is applied via CSS's two pseudo-classes,
:invalid
and:valid
. It applies to<input>
,<select>
, and<textarea>
elements. - The
:invalid
and:valid
styles are scoped to a 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.
Custom Styles
For custom 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 the form controls.
Custom feedback styles apply custom colors, borders, focus styles, feedback messages, and optional background icons to better communicate feedback.
<form class="needs-validation" novalidate>
<div class="form-row">
<div class="col-md-4 mb-1">
<label for="validate-custom-1">First name</label>
<input type="text" class="form-control" id="validate-custom-1" placeholder="First name" value="John" required>
<div class="valid-feedback">Looks good!</div>
</div>
<div class="col-md-4 mb-1">
<label for="validate-custom-2">Last name</label>
<input type="text" class="form-control" id="validate-custom-2" placeholder="Last name" value="Smith" required>
<div class="valid-feedback">Looks good!</div>
</div>
<div class="col-md-4 mb-1">
<label for="validate-custom-3">Username</label>
<div class="input-group">
<span class="input-group-text" id="validate-custom-4">@</span>
<input type="text" class="form-control input-group-end" id="validate-custom-3" placeholder="Username" aria-describedby="validate-custom-4" required>
<div class="invalid-feedback">Please choose a username.</div>
</div>
</div>
</div>
<div class="form-row">
<div class="col-md-6 mb-1">
<label for="validate-custom-5">City</label>
<input type="text" class="form-control" id="validate-custom-5" placeholder="City" required>
<div class="invalid-feedback">Please provide a valid city.</div>
</div>
<div class="col-md-3 mb-1">
<label for="validate-custom-6">State</label>
<input type="text" class="form-control" id="validate-custom-6" placeholder="State" required>
<div class="invalid-feedback">Please provide a valid state.</div>
</div>
<div class="col-md-3 mb-1">
<label for="validate-custom-7">Zip</label>
<input type="text" class="form-control" id="validate-custom-7" placeholder="Zip" required>
<div class="invalid-feedback">Please provide a valid zip.</div>
</div>
</div>
<div class="form-group">
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="validate-custom-8" required>
<label class="form-check-label" for="validate-custom-8">Agree to terms and conditions</label>
<div class="invalid-feedback">You must agree before submitting.</div>
</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 validation styles to
var forms = document.getElementsByClassName('needs-validation');
// Loop over them and prevent submission
var validation = 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>
Browser Defaults
Not interested in custom validation feedback messages or writing JavaScript to change form behaviors? All good, you can use the browser defaults. Try submitting the form below. Depending on your browser and OS, you'll see a slightly different style of feedback.
While these feedback styles cannot be styled with CSS, you can still customize the feedback text through JavaScript.
<form>
<div class="form-row">
<div class="col-md-4 mb-1">
<label for="validate-browser-1">First name</label>
<input type="text" class="form-control" id="validate-browser-1" placeholder="First name" value="John" required>
</div>
<div class="col-md-4 mb-1">
<label for="validate-browser-2">Last name</label>
<input type="text" class="form-control" id="validate-browser-2" placeholder="Last name" value="Smith" required>
</div>
<div class="col-md-4 mb-1">
<label for="validate-browser-3">Username</label>
<div class="input-group">
<span class="input-group-text" id="validate-browser-4">@</span>
<input type="text" class="form-control input-group-end" id="validate-browser-3" placeholder="Username" aria-describedby="validate-browser-4" required>
</div>
</div>
</div>
<div class="form-row">
<div class="col-md-6 mb-1">
<label for="validate-browser-5">City</label>
<input type="text" class="form-control" id="validate-browser-5" placeholder="City" required>
</div>
<div class="col-md-3 mb-1">
<label for="validate-browser-6">State</label>
<input type="text" class="form-control" id="validate-browser-6" placeholder="State" required>
</div>
<div class="col-md-3 mb-1">
<label for="validate-browser-7">Zip</label>
<input type="text" class="form-control" id="validate-browser-7" placeholder="Zip" required>
</div>
</div>
<div class="form-group">
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="validate-browser-8" required>
<label class="form-check-label" for="validate-browser-8">Agree to terms and conditions</label>
</div>
</div>
<button class="btn btn-primary" type="submit">Submit form</button>
</form>
Server Side
When using 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
(noting that this attribute allows more than one id
to be referenced, in case the field already points to additional form text).
<form>
<div class="form-row">
<div class="col-md-4 mb-1">
<label for="validate-server-1">First name</label>
<input type="text" class="form-control is-valid" id="validate-server-1" placeholder="First name" value="John" required>
<div class="valid-feedback">Looks good!</div>
</div>
<div class="col-md-4 mb-1">
<label for="validate-server-2">Last name</label>
<input type="text" class="form-control is-valid" id="validate-server-2" placeholder="Last name" value="Smith" required>
<div class="valid-feedback">Looks good!</div>
</div>
<div class="col-md-4 mb-1">
<label for="validate-server-3">Username</label>
<div class="input-group">
<span class="input-group-text" id="validate-server-4">@</span>
<input type="text" class="form-control input-group-end is-invalid" id="validate-server-3" placeholder="Username" aria-describedby="validate-server-4 validate-server-4-fb" required>
<div id="validate-server-4-fb" class="invalid-feedback">Please choose a username.</div>
</div>
</div>
</div>
<div class="form-row">
<div class="col-md-6 mb-1">
<label for="validate-server-5">City</label>
<input type="text" class="form-control is-invalid" id="validate-server-5" placeholder="City" aria-describedby="validate-server-5-fb" required>
<div id="validate-server-5-fb" class="invalid-feedback">Please provide a valid city.</div>
</div>
<div class="col-md-3 mb-1">
<label for="validate-server-6">State</label>
<input type="text" class="form-control is-invalid" id="validate-server-6" placeholder="State" aria-describedby="validate-server-6-fb" required>
<div id="validate-server-6-fb" class="invalid-feedback">Please provide a valid state.</div>
</div>
<div class="col-md-3 mb-1">
<label for="validate-server-7">Zip</label>
<input type="text" class="form-control is-invalid" id="validate-server-7" placeholder="Zip" aria-describedby="validate-server-7-fb" required>
<div id="validate-server-7-fb" class="invalid-feedback">Please provide a valid zip.</div>
</div>
</div>
<div class="form-group">
<div class="form-check">
<input class="form-check-input is-invalid" type="checkbox" value="" id="validate-server-8" aria-describedby="validate-server-8-fb" required>
<label class="form-check-label" for="validate-server-8">Agree to terms and conditions</label>
<div id="validate-server-8-fb" class="invalid-feedback">You must agree before submitting.</div>
</div>
</div>
<button class="btn btn-primary" type="submit">Submit form</button>
</form>
Supported Elements
Validation styles are supported for the following form controls and components:
<input>
s,<textarea>
s, and<select>
s using.form-control
(only supports one.form-control
in input groups).form-check
s with either native of custom variants.form-file
<form class="was-validated">
<div class="mb-1">
<label for="validate-textarea">Textarea</label>
<textarea class="form-control" id="validate-textarea" rows="3" placeholder="Required example textarea" required></textarea>
<div class="invalid-feedback">Please enter a message in the textarea.</div>
</div>
<div class="form-check mb-1">
<input type="checkbox" class="form-check-input" id="validate-support-1" required>
<label class="form-check-label" for="validate-support-1">Check this native checkbox</label>
<div class="invalid-feedback">Example invalid feedback text</div>
</div>
<div class="form-check">
<input type="radio" class="form-check-input" id="validate-support-2" name="radio-stacked0" required>
<label class="form-check-label" for="validate-support-2">Toggle this native radio</label>
</div>
<div class="form-check mb-1">
<input type="radio" class="form-check-input" id="validate-support-3" name="radio-stacked0" required>
<label class="form-check-label" for="validate-support-3">Or toggle this other native radio</label>
<div class="invalid-feedback">More example invalid feedback text</div>
</div>
<div class="form-check form-checkradio mb-1">
<input type="checkbox" class="form-check-input" id="validate-support-4" required>
<label class="form-check-label" for="validate-support-4">Check this custom checkbox</label>
<div class="invalid-feedback">Example invalid feedback text</div>
</div>
<div class="form-check form-checkradio">
<input type="radio" class="form-check-input" id="validate-support-5" name="radio-stacked1" required>
<label class="form-check-label" for="validate-support-5">Toggle this custom radio</label>
</div>
<div class="form-check form-checkradio mb-1">
<input type="radio" class="form-check-input" id="validate-support-6" name="radio-stacked1" required>
<label class="form-check-label" for="validate-support-6">Or toggle this other custom radio</label>
<div class="invalid-feedback">More example invalid feedback text</div>
</div>
<div class="form-check form-switch mb-1">
<input type="checkbox" class="form-check-input" id="validate-support-7" required>
<label class="form-check-label" for="validate-support-7">Check this custom checkbox</label>
<div class="invalid-feedback">Example invalid feedback text</div>
</div>
<div class="form-group">
<select class="form-control" aria-label="Select example" required>
<option value="">Open this select menu</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="form-group mb-1">
<div class="form-file">
<input type="file" class="form-file-input" id="validatedCustomFile" required>
<label class="form-file-label" for="validatedCustomFile">
<span class="form-file-text">Choose file...</span>
<span class="form-file-button">Browse</span>
</label>
<div class="invalid-feedback">Example invalid custom file feedback</div>
</div>
</div>
<div class="input-group mb-1">
<span class="input-group-text" id="validate-support-ig">@</span>
<input type="text" class="form-control input-group-end" placeholder="Username" aria-label="Username" aria-describedby="validate-support-ig" required>
<div class="invalid-feedback">Please choose a unique and valid username.</div>
</div>
<button class="btn btn-primary" type="submit" disabled>Submit form</button>
</form>
Tooltips
If your form layout allows it, you can swap the .{valid|invalid}-feedback
classes for .{valid|invalid}-tooltip
classes to display validation feedback in a styled tooltip. Be sure to have a parent with position: relative
on it for tooltip positioning. In the example below, our column classes have this already, but your project may require an alternative setup.
<form class="needs-validation" novalidate>
<div class="form-row">
<div class="col-md-4 mb-1">
<label for="validate-tooltip-1">First name</label>
<input type="text" class="form-control" id="validate-tooltip-1" placeholder="First name" value="John" required>
<div class="valid-tooltip">Looks good!</div>
</div>
<div class="col-md-4 mb-1">
<label for="validate-tooltip-2">Last name</label>
<input type="text" class="form-control" id="validate-tooltip-2" placeholder="Last name" value="Smith" required>
<div class="valid-tooltip">Looks good!</div>
</div>
<div class="col-md-4 mb-1">
<label for="validate-tooltip-3">Username</label>
<div class="input-group">
<span class="input-group-text" id="validate-tooltip-4">@</span>
<input type="text" class="form-control input-group-end" id="validate-tooltip-3" placeholder="Username" aria-describedby="validate-tooltip-4" required>
<div class="invalid-tooltip">Please choose a unique and valid username.</div>
</div>
</div>
</div>
<div class="form-row">
<div class="col-md-6 mb-1">
<label for="validate-tooltip-5">City</label>
<input type="text" class="form-control" id="validate-tooltip-5" placeholder="City" required>
<div class="invalid-tooltip">Please provide a valid city.</div>
</div>
<div class="col-md-3 mb-1">
<label for="validate-tooltip-6">State</label>
<input type="text" class="form-control" id="validate-tooltip-6" placeholder="State" required>
<div class="invalid-tooltip">Please provide a valid state.</div>
</div>
<div class="col-md-3 mb-1">
<label for="validate-tooltip-7">Zip</label>
<input type="text" class="form-control" id="validate-tooltip-7" placeholder="Zip" required>
<div class="invalid-tooltip">Please provide a valid zip.</div>
</div>
</div>
<button class="btn btn-primary" type="submit">Submit form</button>
</form>
Icons
Optional visual icon representations of the validation state can be added to textual <input class="form-control">
, <textarea class="form-control">
, and <select class="form-control">
elements by adding a .has-validation-icon
class.
- Validation icons are
url()
s configured via Sass variables that are applied tobackground-image
rules for each state. - You may use your own base64 PNGs or SVGs by updating the Sass variables and recompiling.
- Icons can also be disabled entirely by setting the
$enable-form-validation-icon
variable tofalse
in the Sass.
<form class="was-validated">
<div class="form-row">
<div class="col-md-4 mb-1">
<label for="validate-icon-1">First name</label>
<input type="text" class="form-control has-validation-icon" id="validate-icon-1" placeholder="First name" value="John" required>
<div class="valid-feedback">Looks good!</div>
</div>
<div class="col-md-4 mb-1">
<label for="validate-icon-2">Last name</label>
<input type="text" class="form-control has-validation-icon" id="validate-icon-2" placeholder="Last name" value="Smith" required>
<div class="valid-feedback">Looks good!</div>
</div>
<div class="col-md-4 mb-1">
<label for="validate-icon-3">Username</label>
<div class="input-group">
<span class="input-group-text" id="validate-icon-4">@</span>
<input type="text" class="form-control has-validation-icon input-group-end" id="validate-icon-3" placeholder="Username" aria-describedby="validate-icon-4" required>
<div class="invalid-feedback">Please choose a unique and valid username.</div>
</div>
</div>
</div>
<div class="form-row">
<div class="col-md-6 mb-1">
<label for="validate-icon-5">City</label>
<input type="text" class="form-control has-validation-icon" id="validate-icon-5" placeholder="City" required>
<div class="invalid-feedback">Please provide a valid city.</div>
</div>
<div class="col-md-3 mb-1">
<label for="validate-icon-6">State</label>
<input type="text" class="form-control has-validation-icon" id="validate-icon-6" placeholder="State" required>
<div class="invalid-feedback">Please provide a valid state.</div>
</div>
<div class="col-md-3 mb-1">
<label for="validate-icon-7">Zip</label>
<input type="text" class="form-control has-validation-icon" id="validate-icon-7" placeholder="Zip" required>
<div class="invalid-feedback">Please provide a valid zip.</div>
</div>
</div>
<div class="form-group">
<label for="validate-icon-8">Options</label>
<select class="form-control has-validation-icon" id="validate-icon-8" required>
<option value="">Choose one...</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="form-group">
<label for="validate-icon-9">Options</label>
<select class="form-control has-validation-icon" id="validate-icon-9" size="4" required>
<option value="">Choose one...</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="form-group">
<label for="validate-icon-10">Textarea</label>
<textarea class="form-control has-validation-icon" id="validate-icon-10" rows="3" placeholder="Required example textarea" required></textarea>
<div class="invalid-feedback">Please enter a message in the textarea.</div>
</div>
<button class="btn btn-primary" type="submit" disabled>Submit form</button>
</form>
Customizing
Validation states can be customized via Sass with the $form-validation-states
map. Located in our _settings.scss
file, this Sass map is used to generate the default valid
/invalid
validation states. Included is a nested map for customizing each state's color, icon, tooltip colors, and focus shadow. While no other states are supported by browsers, those using custom styles can easily add more complex form feedback.
Please note that we do not recommend customizing these values without also modifying the form-validation-state
mixin.
This is the Sass map from _settings.scss
. Override this and recompile your Sass to generate different states:
$form-validation-states: (
"valid": (
"color": $form-feedback-valid-color,
"icon": $form-feedback-icon-valid-image
),
"invalid": (
"color": $form-feedback-invalid-color,
"icon": $form-feedback-icon-invalid-image
)
);
Maps of $form-validation-states
can contain three optional parameters to override tooltips and focus styles.
The optional parameters are tooltip-color
and tooltip-bg
for setting the foreground and background colors of the tooltip, and focus-box-shadow
for adjusting the box-shadow on the input field when in a focus state.
$form-validation-states: (
"valid": (
"color": $form-feedback-valid-color,
"icon": $form-feedback-icon-valid-image,
"tooltip-color": color-auto-contrast(#0f0),
"tooltip-bg": rgba(#0f0, .9),
"focus-box-shadow": $input-focus-box-shadow-size rgba(#0f0, $input-focus-box-shadow-alpha)
),
"invalid": (
"color": $form-feedback-invalid-color,
"icon": $form-feedback-icon-invalid-image,
"tooltip-color": color-auto-contrast(#f00),
"tooltip-bg": rgba(#f00, .9),
"focus-box-shadow": $input-focus-box-shadow-size rgba(#f00, $input-focus-box-shadow-alpha)
)
);
This is the loop from forms/_form-validation.scss
. Any modifications to the above Sass map will be reflected in your compiled CSS via this loop:
@each $state, $data in $form-validation-states {
@include form-validation-state($state, data...);
}
Accessibility
Ensure that all form controls have an appropriate accessible name so that their purpose can be conveyed to users of assistive technologies. The simplest way to achieve this is to use a <label>
element, or—in the case of buttons—to include sufficiently descriptive text as part of the <button>...</button>
content.
For situations where it's not possible to include a visible <label>
or appropriate text content, there are alternative ways of still providing an accessible name, such as:
<label>
elements hidden using the.sr-only
class- Pointing to an existing element that can act as a label using
aria-labelledby
- Providing a
title
attribute - Explicitly setting the accessible name on an element using
aria-label
If none of these are present, assistive technologies may resort to using the placeholder
attribute as a fallback for the accessible name on <input>
and <textarea>
elements. The examples in this section provide a few suggested, case-specific approaches.
While using visually hidden content (.sr-only
, aria-label
, and even placeholder
content, which disappears once a form field has content) will benefit assistive technology users, a lack of visible label text may still be problematic for certain users. Some form of visible label is generally the best approach, both for accessibility and usability.
SASS Reference
Variables
The available Customization options, or Sass variables, that can be customized for forms.
Name | Type | Default | Description |
---|---|---|---|
$enable-form |
boolean | true |
Enable the generation of the form classes.
Smaller segements of the form classes can be disabled with the following $enable-* variables.
|
$enable-form-control |
boolean | true |
Enable the generation of the textual form control rules. |
$enable-form-control-sizing |
boolean | true |
Enable the generation of the sizing classes for form controls. |
$enable-form-control-static |
boolean | true |
Enable the generation of the static form control class. |
$enable-form-label |
boolean | true |
Enable the generation of the form control label class. |
$enable-form-label-sizing |
boolean | true |
Enable the generation of the sizing classes for form control labels. |
$enable-form-text |
boolean | true |
Enable the generation of the form text class. |
$enable-form-check |
boolean | true |
Enable the generation of the stacking layout for checkbox and radio inputs. |
$enable-form-check-checkradio |
boolean | true |
Enable the generation of the custom styles for checkbox and radio inputs. |
$enable-form-check-switch |
boolean | true |
Enable the generation of the custom switch styles for checkbox and radio inputs. |
$enable-form-file |
boolean | true |
Enable the generation of the classes for custom file inputs. |
$enable-form-file-sizing |
boolean | true |
Enable the generation of the sizing classes for custom file inputs. |
$enable-form-range |
boolean | true |
Enable the generation of the classes for custom range inputs. |
$enable-form-color |
boolean | true |
Enable the generation of the classes for custom color inputs. |
$enable-form-group |
boolean | true |
Enable the generation of the form group classes. |
$enable-form-row |
boolean | true |
Enable the generation of the form row classes. |
$enable-form-inline |
boolean | true |
Enable the generation of the inline form classes. |
$enable-form-validation |
boolean | true |
Enable the generation of the form validation classes. |
$enable-form-validation-feeback |
boolean | true |
Enable the generation of the form validation text feedback classes. |
$enable-form-validation-tooltip |
boolean | true |
Enable the generation of the form validation tooltip classes. |
$enable-form-validation-icon |
boolean | true |
Enable the generation of the form validation icon class. |
$input-font-family |
string | null |
Base input font family. |
$input-font-size |
string | $btn-font-size |
Base input font size. |
$input-font-weight |
string | $font-weight-normal |
Base input font weight. |
$input-line-height |
string | $btn-line-height |
Base input line height. |
$input-padding-y |
string | $btn-padding-y |
Base input vertical padding. |
$input-padding-x |
string | $btn-padding-x |
Base input horizontal padding. |
$input-sizes |
string | $component-sizes |
Input size variants. |
$input-bg |
string | $white |
Input background color for inactive state. |
$input-color |
string | $uibase-700 |
Input text color for inactive state. |
$input-border-color |
string | $uibase-200 |
Input border color for inactive state. |
$input-border-width |
string | $border-width |
Base input border-width. |
$input-border-radius |
string | $border-radius |
Base input border-radius. |
$input-box-shadow |
string | map-get($shadows, "i1") |
Input inner box shadow for inactive state. |
$input-focus-bg |
string | $input-bg |
Input background color for focus state. |
$input-focus-color |
string | $input-color |
Input text color for focus state. |
$input-focus-border-color |
string | palette($primary, 300) |
Input border color for focus state. |
$input-focus-box-shadow-size |
string | 0 0 0 .1875rem |
Input box shadow dimensions for focus state. |
$input-focus-box-shadow-alpha |
float | .35 |
Input box shadow alpha, opacity, value for focus state. |
$input-focus-box-shadow |
string | $input-focus-box-shadow-size rgba($component-active-bg, $input-focus-box-shadow-alpha) |
Input box shadow for focus state. |
$input-disabled-bg |
string | $uibase-50 |
Input background color for disabled state. |
$input-disabled-color |
string | $component-disabled-color |
Input text color for disabled state. |
$input-disabled-border-color |
string | null |
Border color for disabled state. |
$input-disabled-opacity |
string | 1 |
Opacity value for disabled state. |
$input-placeholder-color |
string | #999 |
Input placheholder text color. |
$input-static-color |
string | $body-color |
Text color for static readonly inputs. |
$form-label-font-weight |
string | $font-weight-normal |
Font weight for .form-label .
|
$form-text-margin-top |
string | .25rem |
Vertical spacing between input and support text. |
$form-text-font-size |
string | .$small-font-size |
Font size for support text. |
$form-text-font-style |
string | null |
Font style for support text. |
$form-text-font-weight |
string | null |
Font weight for support text. |
$form-text-color |
string | $text-muted |
Color for support text. |
$form-group-margin-bottom |
string | 1rem |
Vertical spacing for form group. |
$form-row-gutter-width |
string | .625rem |
Gutter spacing for form row. |
$form-inline-breakpoint |
breakpoint | sm |
Breakpoint where inline froms switch from vertical to horizontal layout. |
$form-inline-check-margin-x |
string | .25rem |
Horizontal spacing for form check when inline. |
$form-check-gutter |
string | 1.25rem |
Reserved spacing width for default inputs within .form-check .
|
$form-check-margin-bottom |
string | .3125rem |
Vertical adjustment for inputs within .form-check .
|
$form-check-label-color |
string | null |
Text color for `.form-check-label`. |
$form-check-label-font-weight |
string | null |
Font weight for `.form-check-label`. |
$form-check-label-cursor |
string | null |
Pointer cursor for `.form-check-label`. |
$form-check-label-disabled-opacity |
string | .6 |
Opacity for `.form-check-label` when the input is disabled. |
$form-checkradio-size |
string | 1em |
Height and width for custom styled checkbox and radio inputs. |
$form-checkradio-gutter |
string | calc(#{$form-checkradio-size} + .375em) |
Reserved spacing width for custom styled checkbox and radio inputs. |
$form-checkradio-bg |
string | $white |
Background color for custom styled checkbox and radio inputs in inactive state. |
$form-checkradio-border-width |
string | $border-width |
Border width for custom styled checkbox and radio inputs |
$form-checkradio-border-color |
string | $uibase-300 |
Border color for custom styled checkbox and radio inputs in inactive state. |
$form-checkradio-box-shadow |
string | map-get($shadows, "i1") |
Box shadow for custom styled checkbox and radio inputs in inactive state. |
$form-checkradio-icon-size |
string | calc(#{$form-checkradio-size} - .375em) |
Height and width of the background image icon for custom styled checkbox and radio input. |
$form-checkradio-focus-border-color |
string | $input-border-color |
Border color for custom styled checkbox and radio inputs in focused state. |
$form-checkradio-focus-box-shadow |
string | $input-focus-box-shadow |
Box shadow for custom styled checkbox and radio inputs in focused state. |
$form-checkradio-checked-bg |
string | $primary |
Background color for custom styled checkbox and radio inputs in checked state. |
$form-checkradio-checked-color |
string | $white |
Icon color for custom styled checkbox and radio inputs in checked state. |
$form-checkradio-checked-border-color |
string | $primary |
Border color for custom styled checkbox and radio inputs in checked state. |
$form-checkradio-checked-box-shadow |
string | map-get($shadows, "i1") |
Box shadow for custom styled checkbox and radio inputs in checked state. |
$form-checkradio-checkbox-border-radius |
string | $border-radius |
Border radius for custom styled checkbox inputs. |
$form-checkradio-checkbox-icon |
string | encode-svg(url("data:image/svg+xml,")) |
Icon for custom styled checkbox inputs when in checked state. |
$form-checkradio-radio-border-radius |
string | 50% |
Border radius for custom styled radio inputs. |
$form-checkradio-radio-icon |
string | encode-svg(url("data:image/svg+xml,")) |
Icon for custom styled radio inputs when in checked state. |
$form-checkradio-indeterminate-bg |
string | $primary |
Background color for custom styled checkbox inputs when in indeterminate state. |
$form-checkradio-indeterminate-border-color |
string | $form-checkradio-indeterminate-bg |
Border color for custom styled checkbox inputs when in indeterminate state. |
$form-checkradio-indeterminate-icon |
string | encode-svg(url("data:image/svg+xml,")) |
Icon for custom styled checkbox inputs when in indeterminate state. |
$form-switch-width |
string | 1.75em |
Width for custom styled switch inputs. |
$form-switch-gutter |
string | calc(#{$form-switch-width} + .375em) |
Reserved spacing width for custom styled checkbox and radio inputs. |
$form-switch-track-height |
string | 1em |
Height for track of custom styled switch inputs. |
$form-switch-track-bg |
string | $white |
Background color for track of custom styled switch inputs in inactive state. |
$form-switch-track-border-width |
string | $input-border-width |
Border width for track of custom styled switch inputs. |
$form-switch-track-border-color |
string | $uibase-300 |
Border color for track of custom styled switch inputs in inactive state. |
$form-switch-track-border-radius |
string | $white |
Border radius for track of custom styled switch inputs. |
$form-switch-track-box-shadow |
string | map-get($shadows, "i1") |
Box shadow for track of custom styled switch inputs. |
$form-switch-track-focus-bg |
string | null |
Background color for track of custom styled switch inputs in focused state. |
$form-switch-track-focus-border-color |
string | $input-focus-border-color |
Border color for track of custom styled switch inputs in focused state. |
$form-switch-track-focus-box-shadow |
string | $input-focus-box-shadow |
Box shadow for track of custom styled switch inputs in focused state. |
$form-switch-track-checked-bg |
string | $primary |
Background color for track of custom styled switch inputs in checked state. |
$form-switch-track-checked-border-color |
string | $primary |
Border color for track of custom styled switch inputs in checked state. |
$form-switch-track-checked-box-shadow |
string | $form-switch-track-box-shadow |
Box shadow for track of custom styled switch inputs in checked state. |
$form-switch-thumb-offset |
string | calc(.25em - #{$form-switch-track-border-width}) |
Horizontal offset for thumb of custom styled switch inputs. |
$form-switch-thumb-width |
string | .625em |
Width for thumb of custom styled switch inputs. |
$form-switch-thumb-height |
string | .625em |
Height for thumb of custom styled switch inputs. |
$form-switch-thumb-bg |
string | $uibase-300 |
Background color for thumb of custom styled switch inputs in inactive state. |
$form-switch-thumb-border-width |
string | $border-width |
Border width for thumb of custom styled switch inputs. |
$form-switch-thumb-border-color |
string | $form-switch-thumb-bg |
Border color for thumb of custom styled switch inputs. |
$form-switch-thumb-border-radius |
string | 50% |
Border radius for thumb of custom styled switch inputs. |
$form-switch-thumb-box-shadow |
string | none |
Box shadow for thumb of custom styled switch inputs. |
$form-switch-thumb-focus-bg |
string | palette($primary, 300) |
Background color for thumb of custom styled switch inputs in focused state. |
$form-switch-thumb-focus-border-color |
string | $form-switch-thumb-focus-bg |
Border color for thumb of custom styled switch inputs in focused state. |
$form-switch-thumb-focus-border-color |
string | $form-switch-thumb-focus-bg |
Border color for thumb of custom styled switch inputs in focused state. |
$form-switch-thumb-focus-box-shadow |
string | null |
Box shadow for thumb of custom styled switch inputs in focused state. |
$form-switch-thumb-checked-bg |
string | $white |
Background color for thumb of custom styled switch inputs in checked state. |
$form-switch-thumb-checked-border-color |
string | $white |
Border color for thumb of custom styled switch inputs in checked state. |
$form-switch-thumb-checked-box-shadow |
string | null |
Box shadow for thumb of custom styled switch inputs in checked state. |
$form-select-indicator-offset |
string | .375rem |
Additional horizontal spacing of visual indicator for custom select input. |
$form-select-indicator-width |
string | .75em |
Width of visual indicator for custom select input. |
$form-select-indicator-height |
string | .75em |
Height of visual indicator for custom select input. |
$form-select-indicator-color |
string | .rgba($uibase-700, .85) |
Color of visual indicator for custom select input. |
$form-select-indicator-image |
string | encode-svg(url("data:image/svg+xml,")) |
Icon for visual indicator of custom select input. |
$form-select-indicator-position |
string | right $form-select-indicator-offset center |
Position for visual indicator of custom select input. |
$form-file-button-color |
string | $uibase-600 |
Button text color for file input. |
$form-file-button-bg |
string | $uibase-50 |
Button background color for file input. |
$form-file-button-font-family |
string | $btn-font-family |
Button font-family for file input. |
$form-file-button-font-weight |
string | $btn-font-weight |
Button font-weight for file input. |
$form-file-button-disabled-color |
string | $component-disabled-color |
Button text color for file input when in disabled state. |
$form-file-button-disabled-bg |
string | $uibase-50 |
Button background color for file input when in disabled state. |
$form-file-button-disabled-opacity |
string | 1 |
Button opacity for file input when in disabled state. |
$form-range-track-height |
string | .5em |
Height of track for custom range input. |
$form-range-track-cursor |
string | pointer |
Pointer style of track for custom range input. |
$form-range-track-bg |
string | $uibase-100 |
Background color of track for custom range input. |
$form-range-track-border |
string | 0 |
Border style of track for custom range input. |
$form-range-track-border-radius |
string | $form-range-track-height |
Border radius of track for custom range input. |
$form-range-track-box-shadow |
string | map-get($shadows, "i1") |
Box shadow of track for custom range input. |
$form-range-thumb-width |
string | 1.125em |
Width of thumb for custom range input. |
$form-range-thumb-height |
string | $form-range-thumb-width |
Height of thumb for custom range input. |
$form-range-thumb-bg |
string | $primary |
Background color of thumb for custom range input. |
$form-range-thumb-border |
string | 0 |
Border style of thumb for custom range input. |
$form-range-thumb-border-radius |
string | 50% |
Border radius of thumb for custom range input. |
$form-range-thumb-box-shadow |
string | map-get($shadows, "d1") |
Box shadow of thumb for custom range input. |
$form-range-thumb-focus-box-shadow |
string | $input-focus-box-shadow |
Box shadow of thumb for custom range input in focus state. |
$form-range-thumb-focus-box-shadow-width |
string | .1875rem |
Width of box shadow of thumb for custom range input in focus state. |
$form-range-thumb-active-bg |
string | palette($primary, 600) |
Background color of thumb for custom range input in active state. |
$form-range-thumb-disabled-bg |
string | $uibase-300 |
Background color of thumb for custom range input in disabled state. |
$form-range-height |
string | calc(#{$form-range-thumb-height} + (#{$form-range-thumb-focus-box-shadow-width} * 2)) |
Height of custom range input. |
$form-range-min-width |
string | 8rem |
Minimum width of custom range input. Note: Browser default seems to be 129px/~8rem for IE/Chrome/Safari |
$form-feedback-margin-top |
string | $form-text-margin-top |
Vertical spacing between input and feeback text. |
$form-feedback-font-size |
string | $form-text-font-size |
Font size for feedback text. |
$form-feedback-valid-color |
string | map-get($base-colors, "success") |
Base color for valid feedback state. |
$form-feedback-invalid-color |
string | map-get($base-colors, "danger") |
Base color for invalid feedback state. |
$form-feedback-icon-offset |
string | .25em |
Additional horizontal spacing of visual feedback indicator icon. |
$form-feedback-icon-width |
string | 1em |
Width of visual feedback indicator icon. |
$form-feedback-icon-height |
string | 1em |
Height of visual feedback indicator icon. |
$form-feedback-icon-valid-color |
string | $form-feedback-valid-color |
Icon color for valid feedback state. |
$form-feedback-icon-valid-image |
string | encode-svg(url("data:image/svg+xml,")) |
Icon for valid feedback state. |
$form-feedback-icon-invalid-color |
string | $form-feedback-invalid-color |
Icon color for invalid feedback state. |
$form-feedback-icon-invalid-image |
string | encode-svg(url("data:image/svg+xml,")) |
Icon for invalid feedback state. |
$form-feedback-select-icon-position |
string | right calc(#{$form-feedback-icon-offset} + #{$form-select-indicator-width} + #{$form-select-indicator-offset}) center |
Position of feedback icon for custom select inputs. |
$input-transition |
string | background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out |
Transition effect for inputs. |
$switch-transition |
string | all .15s ease-in-out |
Transition effect for custom switch inputs. |
Mixins
Here are the mixins related to forms that we use to help generate our CSS. You can also uses these mixins to generate your own custom components or utilities.
form-validation-state()
Build form validation rules.
@include form-validation-state($state, $color, $icon);
Argument | Type | Default | Description |
---|---|---|---|
$state |
string | '' |
The value appended to generate the classes for the given validation state. |
$color |
string | none | The color to mix and use throughout the validation state. |
$icon |
string | none | The icon to use throughout the validation state. |
form-control-focus()
Add the focus state to a form control or input.
@include form-control-focus();
form-range-track()
Add the common, cross-browser rules for track of a range input.
@include form-range-track();
form-range-thumb()
Add the common, cross-browser rules for thumb of a range input.
@include form-range-thumb();