aboutsummaryrefslogtreecommitdiff
path: root/scss/forms
diff options
context:
space:
mode:
Diffstat (limited to 'scss/forms')
-rw-r--r--scss/forms/_form-check.scss116
-rw-r--r--scss/forms/_form-control.scss115
-rw-r--r--scss/forms/_form-file.scss72
-rw-r--r--scss/forms/_form-range.scss142
-rw-r--r--scss/forms/_form-select.scss76
-rw-r--r--scss/forms/_input-group.scss188
-rw-r--r--scss/forms/_labels.scss27
-rw-r--r--scss/forms/_layout.scss101
-rw-r--r--scss/forms/_validation.scss10
9 files changed, 847 insertions, 0 deletions
diff --git a/scss/forms/_form-check.scss b/scss/forms/_form-check.scss
new file mode 100644
index 000000000..b309d2e70
--- /dev/null
+++ b/scss/forms/_form-check.scss
@@ -0,0 +1,116 @@
+//
+// Check/radio
+//
+
+.form-check {
+ display: block;
+ min-height: $form-check-min-height;
+ padding-left: $form-check-padding-left;
+ margin-bottom: $form-check-margin-bottom;
+}
+
+.form-check-input {
+ float: left;
+ width: $form-check-input-width;
+ height: $form-check-input-width;
+ // Todo: Change static value to base line-height?
+ margin-top: calc((1.5em - #{$form-check-input-width}) / 2); // line-height minus check height
+ margin-left: $form-check-padding-left * -1;
+ background-color: $form-check-input-bg;
+ border: $form-check-input-border;
+ appearance: none;
+
+ &[type="checkbox"] {
+ @include border-radius($form-check-input-border-radius);
+ }
+
+ &[type="radio"] {
+ @include border-radius($form-check-radio-border-radius);
+ }
+
+ &:active {
+ filter: $form-check-input-active-filter;
+ }
+
+ &:focus {
+ border-color: $form-check-input-focus-border;
+ outline: 0;
+ box-shadow: $form-check-input-focus-box-shadow;
+ }
+
+ &:checked {
+ background-color: $form-check-input-checked-bg-color;
+ background-repeat: $form-check-input-checked-bg-repeat;
+ background-position: $form-check-input-checked-bg-position;
+ background-size: $form-check-input-checked-bg-size;
+ border-color: $form-check-input-checked-border-color;
+
+ &[type="checkbox"] {
+ background-image: $form-check-input-checked-bg-image;
+ }
+
+ &[type="radio"] {
+ background-image: $form-check-radio-checked-bg-image;
+ }
+ }
+
+ &[type="checkbox"]:indeterminate {
+ background-color: $form-check-input-indeterminate-bg-color;
+ background-image: $form-check-input-indeterminate-bg-image;
+ background-repeat: $form-check-input-indeterminate-bg-repeat;
+ background-position: $form-check-input-indeterminate-bg-position;
+ background-size: $form-check-input-indeterminate-bg-size;
+ border-color: $form-check-input-indeterminate-border-color;
+ }
+
+ // Use disabled attribute instead of :disabled pseudo-class
+ // Workaround for: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/11295231
+ &[disabled] {
+ pointer-events: none;
+ filter: none;
+ opacity: .5;
+
+ ~ .form-check-label {
+ opacity: .5;
+ }
+ }
+}
+
+.form-check-label {
+ margin-bottom: 0;
+}
+
+//
+// Switch
+//
+
+.form-switch {
+ padding-left: $form-switch-padding-left;
+
+ .form-check-input {
+ width: $form-switch-width;
+ margin-left: $form-switch-padding-left * -1;
+ background-image: $form-switch-bg-image;
+ background-repeat: no-repeat;
+ background-position: left center;
+ background-size: calc(#{$form-switch-height} - 2px); // Get a 1px separation
+ @include border-radius($form-switch-border-radius);
+ // Todo: Figure out how to tackle these, with or without mixin?
+ // transition: $form-switch-transition;
+ // transition-property: $form-switch-transition-property;
+
+ &:focus {
+ background-image: $form-switch-focus-bg-image;
+ }
+
+ &:checked {
+ background-image: $form-switch-checked-bg-image;
+ background-position: $form-switch-checked-bg-position;
+ }
+ }
+}
+
+.form-check-inline {
+ display: inline-block;
+ margin-right: $form-check-inline-margin-right;
+}
diff --git a/scss/forms/_form-control.scss b/scss/forms/_form-control.scss
new file mode 100644
index 000000000..ce2b42662
--- /dev/null
+++ b/scss/forms/_form-control.scss
@@ -0,0 +1,115 @@
+// stylelint-disable selector-no-qualifying-type
+
+//
+// Textual form controls
+//
+
+.form-control {
+ display: block;
+ width: 100%;
+ height: $input-height;
+ padding: $input-padding-y $input-padding-x;
+ font-family: $input-font-family;
+ @include font-size($input-font-size);
+ font-weight: $input-font-weight;
+ line-height: $input-line-height;
+ color: $input-color;
+ background-color: $input-bg;
+ background-clip: padding-box;
+ border: $input-border-width solid $input-border-color;
+
+ // Note: This has no effect on <select>s in some browsers, due to the limited stylability of `<select>`s in CSS.
+ @include border-radius($input-border-radius, 0);
+
+ @include box-shadow($input-box-shadow);
+ @include transition($input-transition);
+
+ // Unstyle the caret on `<select>`s in IE10+.
+ &::-ms-expand {
+ background-color: transparent;
+ border: 0;
+ }
+
+ // Customize the `:focus` state to imitate native WebKit styles.
+ &:focus {
+ color: $input-focus-color;
+ background-color: $input-focus-bg;
+ border-color: $input-focus-border-color;
+ outline: 0;
+ // Avoid using mixin so we can pass custom focus shadow properly
+ @if $enable-shadows {
+ box-shadow: $input-box-shadow, $input-focus-box-shadow;
+ } @else {
+ box-shadow: $input-focus-box-shadow;
+ }
+ }
+
+ // Placeholder
+ &::placeholder {
+ color: $input-placeholder-color;
+ // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526.
+ opacity: 1;
+ }
+
+ // Disabled and read-only inputs
+ //
+ // HTML5 says that controls under a fieldset > legend:first-child won't be
+ // disabled if the fieldset is disabled. Due to implementation difficulty, we
+ // don't honor that edge case; we style them as disabled anyway.
+ &:disabled,
+ &[readonly] {
+ background-color: $input-disabled-bg;
+ // iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655.
+ opacity: 1;
+ }
+}
+
+// Readonly controls as plain text
+//
+// Apply class to a readonly input to make it appear like regular plain
+// text (without any border, background color, focus indicator)
+
+.form-control-plaintext {
+ display: block;
+ width: 100%;
+ padding: $input-padding-y 0;
+ margin-bottom: 0; // match inputs if this class comes on inputs with default margins
+ line-height: $input-line-height;
+ color: $input-plaintext-color;
+ background-color: transparent;
+ border: solid transparent;
+ border-width: $input-border-width 0;
+
+ &.form-control-sm,
+ &.form-control-lg {
+ padding-right: 0;
+ padding-left: 0;
+ }
+}
+
+// Form control sizing
+//
+// Build on `.form-control` with modifier classes to decrease or increase the
+// height and font-size of form controls.
+//
+// Repeated in `_input_group.scss` to avoid Sass extend issues.
+
+.form-control-sm {
+ height: $input-height-sm;
+ padding: $input-padding-y-sm $input-padding-x-sm;
+ @include font-size($input-font-size-sm);
+ line-height: $input-line-height-sm;
+ @include border-radius($input-border-radius-sm);
+}
+
+.form-control-lg {
+ height: $input-height-lg;
+ padding: $input-padding-y-lg $input-padding-x-lg;
+ @include font-size($input-font-size-lg);
+ line-height: $input-line-height-lg;
+ @include border-radius($input-border-radius-lg);
+}
+
+textarea.form-control {
+ height: auto;
+}
diff --git a/scss/forms/_form-file.scss b/scss/forms/_form-file.scss
new file mode 100644
index 000000000..bbc38a9f8
--- /dev/null
+++ b/scss/forms/_form-file.scss
@@ -0,0 +1,72 @@
+.form-file {
+ position: relative;
+ display: inline-block;
+ width: 100%;
+ height: $form-file-height;
+ margin-bottom: 0;
+}
+
+.form-file-input {
+ position: relative;
+ z-index: 2;
+ width: 100%;
+ height: $form-file-height;
+ margin: 0;
+ opacity: 0;
+
+ &:focus ~ .form-file-label {
+ border-color: $form-file-focus-border-color;
+ box-shadow: $form-file-focus-box-shadow;
+ }
+
+ // Use disabled attribute instead of :disabled pseudo-class
+ // Workaround for: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/11295231
+ &[disabled] ~ .form-file-label {
+ background-color: $form-file-disabled-bg;
+ }
+}
+
+.form-file-label {
+ position: absolute;
+ top: 0;
+ right: 0;
+ left: 0;
+ z-index: 1;
+ display: flex;
+ height: $form-file-height;
+ border-color: $form-file-border-color;
+ @include border-radius($form-file-border-radius);
+ @include box-shadow($form-file-box-shadow);
+}
+
+.form-file-text {
+ display: block;
+ flex-grow: 1;
+ padding: $form-file-padding-y $form-file-padding-x;
+ overflow: hidden;
+ font-family: $form-file-font-family;
+ font-weight: $form-file-font-weight;
+ line-height: $form-file-line-height;
+ color: $form-file-color;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ background-color: $form-file-bg;
+ border-color: inherit;
+ border-style: solid;
+ border-width: $form-file-border-width;
+ @include border-left-radius(inherit);
+}
+
+.form-file-button {
+ display: block;
+ flex-shrink: 0;
+ padding: $form-file-padding-y $form-file-padding-x;
+ margin-left: -$form-file-border-width;
+ line-height: $form-file-line-height;
+ color: $form-file-button-color;
+ @include gradient-bg($form-file-button-bg);
+ border-color: inherit;
+ border-style: solid;
+ border-width: $form-file-border-width;
+ @include border-right-radius(inherit);
+}
diff --git a/scss/forms/_form-range.scss b/scss/forms/_form-range.scss
new file mode 100644
index 000000000..b54d4d6aa
--- /dev/null
+++ b/scss/forms/_form-range.scss
@@ -0,0 +1,142 @@
+// Range
+//
+// Style range inputs the same across browsers. Vendor-specific rules for pseudo
+// elements cannot be mixed. As such, there are no shared styles for focus or
+// active states on prefixed selectors.
+
+.form-range {
+ width: 100%;
+ height: calc(#{$form-range-thumb-height} + #{$form-range-thumb-focus-box-shadow-width * 2});
+ padding: 0; // Need to reset padding
+ background-color: transparent;
+ appearance: none;
+
+ &:focus {
+ outline: none;
+
+ // Pseudo-elements must be split across multiple rulesets to have an effect.
+ // No box-shadow() mixin for focus accessibility.
+ &::-webkit-slider-thumb { box-shadow: $form-range-thumb-focus-box-shadow; }
+ &::-moz-range-thumb { box-shadow: $form-range-thumb-focus-box-shadow; }
+ &::-ms-thumb { box-shadow: $form-range-thumb-focus-box-shadow; }
+ }
+
+ &::-moz-focus-outer {
+ border: 0;
+ }
+
+ &::-webkit-slider-thumb {
+ width: $form-range-thumb-width;
+ height: $form-range-thumb-height;
+ margin-top: ($form-range-track-height - $form-range-thumb-height) / 2; // Webkit specific
+ @include gradient-bg($form-range-thumb-bg);
+ border: $form-range-thumb-border;
+ @include border-radius($form-range-thumb-border-radius);
+ @include box-shadow($form-range-thumb-box-shadow);
+ @include transition($custom-forms-transition);
+ appearance: none;
+
+ &:active {
+ @include gradient-bg($form-range-thumb-active-bg);
+ }
+ }
+
+ &::-webkit-slider-runnable-track {
+ width: $form-range-track-width;
+ height: $form-range-track-height;
+ color: transparent; // Why?
+ cursor: $form-range-track-cursor;
+ background-color: $form-range-track-bg;
+ border-color: transparent;
+ @include border-radius($form-range-track-border-radius);
+ @include box-shadow($form-range-track-box-shadow);
+ }
+
+ &::-moz-range-thumb {
+ width: $form-range-thumb-width;
+ height: $form-range-thumb-height;
+ @include gradient-bg($form-range-thumb-bg);
+ border: $form-range-thumb-border;
+ @include border-radius($form-range-thumb-border-radius);
+ @include box-shadow($form-range-thumb-box-shadow);
+ @include transition($custom-forms-transition);
+ appearance: none;
+
+ &:active {
+ @include gradient-bg($form-range-thumb-active-bg);
+ }
+ }
+
+ &::-moz-range-track {
+ width: $form-range-track-width;
+ height: $form-range-track-height;
+ color: transparent;
+ cursor: $form-range-track-cursor;
+ background-color: $form-range-track-bg;
+ border-color: transparent; // Firefox specific?
+ @include border-radius($form-range-track-border-radius);
+ @include box-shadow($form-range-track-box-shadow);
+ }
+
+ &::-ms-thumb {
+ width: $form-range-thumb-width;
+ height: $form-range-thumb-height;
+ margin-top: 0; // Edge specific
+ margin-right: $form-range-thumb-focus-box-shadow-width; // Workaround that overflowed box-shadow is hidden.
+ margin-left: $form-range-thumb-focus-box-shadow-width; // Workaround that overflowed box-shadow is hidden.
+ @include gradient-bg($form-range-thumb-bg);
+ border: $form-range-thumb-border;
+ @include border-radius($form-range-thumb-border-radius);
+ @include box-shadow($form-range-thumb-box-shadow);
+ @include transition($custom-forms-transition);
+ appearance: none;
+
+ &:active {
+ @include gradient-bg($form-range-thumb-active-bg);
+ }
+ }
+
+ &::-ms-track {
+ width: $form-range-track-width;
+ height: $form-range-track-height;
+ color: transparent;
+ cursor: $form-range-track-cursor;
+ background-color: transparent;
+ border-color: transparent;
+ border-width: $form-range-thumb-height / 2;
+ @include box-shadow($form-range-track-box-shadow);
+ }
+
+ &::-ms-fill-lower {
+ background-color: $form-range-track-bg;
+ @include border-radius($form-range-track-border-radius);
+ }
+
+ &::-ms-fill-upper {
+ margin-right: 15px; // arbitrary?
+ background-color: $form-range-track-bg;
+ @include border-radius($form-range-track-border-radius);
+ }
+
+ &:disabled {
+ &::-webkit-slider-thumb {
+ background-color: $form-range-thumb-disabled-bg;
+ }
+
+ &::-webkit-slider-runnable-track {
+ cursor: default;
+ }
+
+ &::-moz-range-thumb {
+ background-color: $form-range-thumb-disabled-bg;
+ }
+
+ &::-moz-range-track {
+ cursor: default;
+ }
+
+ &::-ms-thumb {
+ background-color: $form-range-thumb-disabled-bg;
+ }
+ }
+}
diff --git a/scss/forms/_form-select.scss b/scss/forms/_form-select.scss
new file mode 100644
index 000000000..e0b69c04f
--- /dev/null
+++ b/scss/forms/_form-select.scss
@@ -0,0 +1,76 @@
+// Select
+//
+// Replaces the browser default select with a custom one, mostly pulled from
+// https://primer.github.io/.
+
+.form-select {
+ display: inline-block;
+ width: 100%;
+ height: $form-select-height;
+ padding: $form-select-padding-y ($form-select-padding-x + $form-select-indicator-padding) $form-select-padding-y $form-select-padding-x;
+ font-family: $form-select-font-family;
+ @include font-size($form-select-font-size);
+ font-weight: $form-select-font-weight;
+ line-height: $form-select-line-height;
+ color: $form-select-color;
+ vertical-align: middle;
+ background: $form-select-background;
+ background-color: $form-select-bg;
+ border: $form-select-border-width solid $form-select-border-color;
+ @include border-radius($form-select-border-radius, 0);
+ @include box-shadow($form-select-box-shadow);
+ appearance: none;
+
+ &:focus {
+ border-color: $form-select-focus-border-color;
+ outline: 0;
+ @if $enable-shadows {
+ box-shadow: $form-select-box-shadow, $form-select-focus-box-shadow;
+ } @else {
+ box-shadow: $form-select-focus-box-shadow;
+ }
+
+ &::-ms-value {
+ // For visual consistency with other platforms/browsers,
+ // suppress the default white text on blue background highlight given to
+ // the selected option text when the (still closed) <select> receives focus
+ // in IE and (under certain conditions) Edge.
+ // See https://github.com/twbs/bootstrap/issues/19398.
+ color: $input-color;
+ background-color: $input-bg;
+ }
+ }
+
+ &[multiple],
+ &[size]:not([size="1"]) {
+ height: auto;
+ padding-right: $form-select-padding-x;
+ background-image: none;
+ }
+
+ &:disabled {
+ color: $form-select-disabled-color;
+ background-color: $form-select-disabled-bg;
+ }
+
+ // Hides the default caret in IE11
+ &::-ms-expand {
+ display: none;
+ }
+}
+
+.form-select-sm {
+ height: $form-select-height-sm;
+ padding-top: $form-select-padding-y-sm;
+ padding-bottom: $form-select-padding-y-sm;
+ padding-left: $form-select-padding-x-sm;
+ @include font-size($form-select-font-size-sm);
+}
+
+.form-select-lg {
+ height: $form-select-height-lg;
+ padding-top: $form-select-padding-y-lg;
+ padding-bottom: $form-select-padding-y-lg;
+ padding-left: $form-select-padding-x-lg;
+ @include font-size($form-select-font-size-lg);
+}
diff --git a/scss/forms/_input-group.scss b/scss/forms/_input-group.scss
new file mode 100644
index 000000000..099accecb
--- /dev/null
+++ b/scss/forms/_input-group.scss
@@ -0,0 +1,188 @@
+// stylelint-disable selector-no-qualifying-type
+
+//
+// Base styles
+//
+
+.input-group {
+ position: relative;
+ display: flex;
+ flex-wrap: wrap; // For form validation feedback
+ align-items: stretch;
+ width: 100%;
+
+ > .form-control,
+ > .form-select,
+ > .form-file {
+ position: relative; // For focus state's z-index
+ flex: 1 1 0%;
+ margin-bottom: 0;
+
+ + .form-control,
+ + .form-select,
+ + .form-file {
+ margin-left: -$input-border-width;
+ }
+ }
+
+ // Bring the "active" form control to the top of surrounding elements
+ > .form-control:focus,
+ > .form-select:focus,
+ > .form-file .form-file-input:focus ~ .form-file-label {
+ z-index: 3;
+ }
+
+ // Bring the custom file input above the label
+ > .form-file .form-file-input:focus {
+ z-index: 4;
+ }
+
+ > .form-control,
+ > .form-select {
+ &:not(:last-child) { @include border-right-radius(0); }
+ &:not(:first-child) { @include border-left-radius(0); }
+ }
+
+ // Custom file inputs have more complex markup, thus requiring different
+ // border-radius overrides.
+ > .form-file {
+ display: flex;
+ align-items: center;
+
+ &:not(:last-child) .form-file-label { @include border-right-radius(0); }
+ &:not(:first-child) .form-file-label { @include border-left-radius(0); }
+ }
+}
+
+
+// Prepend and append
+//
+// While it requires one extra layer of HTML for each, dedicated prepend and
+// append elements allow us to 1) be less clever, 2) simplify our selectors, and
+// 3) support HTML5 form validation.
+
+.input-group-prepend,
+.input-group-append {
+ display: flex;
+
+ // Ensure buttons are always above inputs for more visually pleasing borders.
+ // This isn't needed for `.input-group-text` since it shares the same border-color
+ // as our inputs.
+ .btn {
+ position: relative;
+ z-index: 2;
+
+ &:focus {
+ z-index: 3;
+ }
+ }
+
+ .btn + .btn,
+ .btn + .input-group-text,
+ .input-group-text + .input-group-text,
+ .input-group-text + .btn {
+ margin-left: -$input-border-width;
+ }
+}
+
+.input-group-prepend { margin-right: -$input-border-width; }
+.input-group-append { margin-left: -$input-border-width; }
+
+
+// Textual addons
+//
+// Serves as a catch-all element for any text or radio/checkbox input you wish
+// to prepend or append to an input.
+
+.input-group-text {
+ display: flex;
+ align-items: center;
+ padding: $input-padding-y $input-padding-x;
+ margin-bottom: 0; // Allow use of <label> elements by overriding our default margin-bottom
+ @include font-size($input-font-size); // Match inputs
+ font-weight: $font-weight-normal;
+ line-height: $input-line-height;
+ color: $input-group-addon-color;
+ text-align: center;
+ white-space: nowrap;
+ background-color: $input-group-addon-bg;
+ border: $input-border-width solid $input-group-addon-border-color;
+ @include border-radius($input-border-radius);
+
+ // Nuke default margins from checkboxes and radios to vertically center within.
+ input[type="radio"],
+ input[type="checkbox"] {
+ margin-top: 0;
+ }
+}
+
+
+// Sizing
+//
+// Remix the default form control sizing classes into new ones for easier
+// manipulation.
+
+.input-group-lg > .form-control:not(textarea),
+.input-group-lg > .form-select {
+ height: $input-height-lg;
+}
+
+.input-group-lg > .form-control,
+.input-group-lg > .form-select,
+.input-group-lg > .input-group-prepend > .input-group-text,
+.input-group-lg > .input-group-append > .input-group-text,
+.input-group-lg > .input-group-prepend > .btn,
+.input-group-lg > .input-group-append > .btn {
+ padding: $input-padding-y-lg $input-padding-x-lg;
+ @include font-size($input-font-size-lg);
+ line-height: $input-line-height-lg;
+ @include border-radius($input-border-radius-lg);
+}
+
+.input-group-sm > .form-control:not(textarea),
+.input-group-sm > .form-select {
+ height: $input-height-sm;
+}
+
+.input-group-sm > .form-control,
+.input-group-sm > .form-select,
+.input-group-sm > .input-group-prepend > .input-group-text,
+.input-group-sm > .input-group-append > .input-group-text,
+.input-group-sm > .input-group-prepend > .btn,
+.input-group-sm > .input-group-append > .btn {
+ padding: $input-padding-y-sm $input-padding-x-sm;
+ @include font-size($input-font-size-sm);
+ line-height: $input-line-height-sm;
+ @include border-radius($input-border-radius-sm);
+}
+
+.input-group-lg > .form-select,
+.input-group-sm > .form-select {
+ padding-right: $form-select-padding-x + $form-select-indicator-padding;
+}
+
+
+// Prepend and append rounded corners
+//
+// These rulesets must come after the sizing ones to properly override sm and lg
+// border-radius values when extending. They're more specific than we'd like
+// with the `.input-group >` part, but without it, we cannot override the sizing.
+
+
+.input-group > .input-group-prepend > .btn,
+.input-group > .input-group-prepend > .input-group-text,
+.input-group > .input-group-append:not(:last-child) > .btn,
+.input-group > .input-group-append:not(:last-child) > .input-group-text,
+.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle),
+.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) {
+ @include border-right-radius(0);
+}
+
+.input-group > .input-group-append > .btn,
+.input-group > .input-group-append > .input-group-text,
+.input-group > .input-group-prepend:not(:first-child) > .btn,
+.input-group > .input-group-prepend:not(:first-child) > .input-group-text,
+.input-group > .input-group-prepend:first-child > .btn:not(:first-child),
+.input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) {
+ @include border-left-radius(0);
+}
diff --git a/scss/forms/_labels.scss b/scss/forms/_labels.scss
new file mode 100644
index 000000000..1a4ea8c5c
--- /dev/null
+++ b/scss/forms/_labels.scss
@@ -0,0 +1,27 @@
+//
+// Labels
+//
+
+// For use with horizontal and inline forms, when you need the label (or legend)
+// text to align with the form controls.
+.col-form-label {
+ padding-top: calc(#{$input-padding-y} + #{$input-border-width});
+ padding-bottom: calc(#{$input-padding-y} + #{$input-border-width});
+ margin-bottom: 0; // Override the `<label>/<legend>` default
+ @include font-size(inherit); // Override the `<legend>` default
+ line-height: $input-line-height;
+}
+
+.col-form-label-lg {
+ padding-top: calc(#{$input-padding-y-lg} + #{$input-border-width});
+ padding-bottom: calc(#{$input-padding-y-lg} + #{$input-border-width});
+ @include font-size($input-font-size-lg);
+ line-height: $input-line-height-lg;
+}
+
+.col-form-label-sm {
+ padding-top: calc(#{$input-padding-y-sm} + #{$input-border-width});
+ padding-bottom: calc(#{$input-padding-y-sm} + #{$input-border-width});
+ @include font-size($input-font-size-sm);
+ line-height: $input-line-height-sm;
+}
diff --git a/scss/forms/_layout.scss b/scss/forms/_layout.scss
new file mode 100644
index 000000000..f65b5493f
--- /dev/null
+++ b/scss/forms/_layout.scss
@@ -0,0 +1,101 @@
+// Form grid
+//
+// Special replacement for our grid system's `.row` for tighter form layouts.
+
+@if $enable-grid-classes {
+ .form-row {
+ display: flex;
+ flex-wrap: wrap;
+ margin-right: -$form-grid-gutter-width / 2;
+ margin-left: -$form-grid-gutter-width / 2;
+
+ > .col,
+ > [class*="col-"] {
+ padding-right: $form-grid-gutter-width / 2;
+ padding-left: $form-grid-gutter-width / 2;
+ }
+ }
+}
+
+// Inline forms
+//
+// Make forms appear inline(-block) by adding the `.form-inline` class. Inline
+// forms begin stacked on extra small (mobile) devices and then go inline when
+// viewports reach <768px.
+//
+// Requires wrapping inputs and labels with `.form-group` for proper display of
+// default HTML form controls and our custom form controls (e.g., input groups).
+
+.form-inline {
+ display: flex;
+ flex-flow: row wrap;
+ align-items: center; // Prevent shorter elements from growing to same height as others (e.g., small buttons growing to normal sized button height)
+
+ // Because we use flex, the initial sizing of checkboxes is collapsed and
+ // doesn't occupy the full-width (which is what we want for xs grid tier),
+ // so we force that here.
+ .form-check {
+ width: 100%;
+ }
+
+ // Kick in the inline
+ @include media-breakpoint-up(sm) {
+ label {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-bottom: 0;
+ }
+
+ // Inline-block all the things for "inline"
+ .form-group {
+ display: flex;
+ flex: 0 0 auto;
+ flex-flow: row wrap;
+ align-items: center;
+ margin-bottom: 0;
+ }
+
+ // Allow folks to *not* use `.form-group`
+ .form-control {
+ display: inline-block;
+ width: auto; // Prevent labels from stacking above inputs in `.form-group`
+ vertical-align: middle;
+ }
+
+ // Make static controls behave like regular ones
+ .form-control-plaintext {
+ display: inline-block;
+ }
+
+ .input-group,
+ .form-select {
+ width: auto;
+ }
+
+ // Remove default margin on radios/checkboxes that were used for stacking, and
+ // then undo the floating of radios and checkboxes to match.
+ .form-check {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: auto;
+ padding-left: 0;
+ }
+ .form-check-input {
+ position: relative;
+ flex-shrink: 0;
+ margin-top: 0;
+ margin-right: $form-check-input-margin-x;
+ margin-left: 0;
+ }
+
+ .custom-control {
+ align-items: center;
+ justify-content: center;
+ }
+ .custom-control-label {
+ margin-bottom: 0;
+ }
+ }
+}
diff --git a/scss/forms/_validation.scss b/scss/forms/_validation.scss
new file mode 100644
index 000000000..d15e20899
--- /dev/null
+++ b/scss/forms/_validation.scss
@@ -0,0 +1,10 @@
+// Form validation
+//
+// Provide feedback to users when form field values are valid or invalid. Works
+// primarily for client-side validation via scoped `:invalid` and `:valid`
+// pseudo-classes but also includes `.is-invalid` and `.is-valid` classes for
+// server-side validation.
+
+@each $state, $data in $form-validation-states {
+ @include form-validation-state($state, map-get($data, color), map-get($data, icon));
+}