WIP: New form controls (#41740)

* New form controls

* Split Sass, update docs

* More migration docs

* basic migration, update changelog

* Bring back btn-check for now, but move to button stylesheet

* note

* Fix link

* lint
This commit is contained in:
Mark Otto 2025-09-24 11:54:21 -07:00 committed by GitHub
parent f0c163b917
commit f9c8e96f70
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 907 additions and 870 deletions

View File

@ -26,11 +26,11 @@
}, },
{ {
"path": "./dist/css/bootstrap.css", "path": "./dist/css/bootstrap.css",
"maxSize": "35.75 kB" "maxSize": "36.0 kB"
}, },
{ {
"path": "./dist/css/bootstrap.min.css", "path": "./dist/css/bootstrap.min.css",
"maxSize": "31.5 kB" "maxSize": "32.0 kB"
}, },
{ {
"path": "./dist/js/bootstrap.bundle.js", "path": "./dist/js/bootstrap.bundle.js",

View File

@ -21,6 +21,7 @@
"callout", "callout",
"callouts", "callouts",
"camelCase", "camelCase",
"checkgroup",
"clearfix", "clearfix",
"Codesniffer", "Codesniffer",
"combinator", "combinator",

View File

@ -91,6 +91,11 @@
--#{$prefix}root-font-size: #{$font-size-root}; --#{$prefix}root-font-size: #{$font-size-root};
} }
--#{$prefix}body-font-family: #{meta.inspect($font-family-base)}; --#{$prefix}body-font-family: #{meta.inspect($font-family-base)};
--#{$prefix}font-size-base: #{$font-size-base}; // 14px
--#{$prefix}font-size-sm: calc(#{$font-size-base} * .9285);
--#{$prefix}font-size-lg: calc(#{$font-size-base} * 1.285);
@include rfs($font-size-base, --#{$prefix}body-font-size); @include rfs($font-size-base, --#{$prefix}body-font-size);
--#{$prefix}body-font-weight: #{$font-weight-base}; --#{$prefix}body-font-weight: #{$font-weight-base};
--#{$prefix}body-line-height: #{$line-height-base}; --#{$prefix}body-line-height: #{$line-height-base};
@ -237,7 +242,7 @@
--#{$prefix}link-color-rgb: #{to-rgb($link-color-dark)}; --#{$prefix}link-color-rgb: #{to-rgb($link-color-dark)};
--#{$prefix}link-hover-color-rgb: #{to-rgb($link-hover-color-dark)}; --#{$prefix}link-hover-color-rgb: #{to-rgb($link-hover-color-dark)};
--#{$prefix}code-color: #{$code-color-dark}; // --#{$prefix}code-color: #{$code-color-dark}; // removed in v6
--#{$prefix}highlight-color: #{$mark-color-dark}; --#{$prefix}highlight-color: #{$mark-color-dark};
--#{$prefix}highlight-bg: #{$mark-bg-dark}; --#{$prefix}highlight-bg: #{$mark-bg-dark};

View File

@ -10,7 +10,7 @@
// consistent naming. Ex: $nav-link-disabled-color and $modal-content-box-shadow-xs. // consistent naming. Ex: $nav-link-disabled-color and $modal-content-box-shadow-xs.
// scss-docs-start theme-color-variables // scss-docs-start theme-color-variables
$primary: $purple-500 !default; $primary: $blue-500 !default;
$secondary: $gray-600 !default; $secondary: $gray-600 !default;
$success: $green-500 !default; $success: $green-500 !default;
$info: $cyan-500 !default; $info: $cyan-500 !default;
@ -167,6 +167,7 @@ $body-emphasis-color: $black !default;
$link-color: $primary !default; $link-color: $primary !default;
$link-decoration: underline !default; $link-decoration: underline !default;
$link-underline-offset: .2em !default;
$link-shade-percentage: 20% !default; $link-shade-percentage: 20% !default;
$link-hover-color: shift-color($link-color, $link-shade-percentage) !default; $link-hover-color: shift-color($link-color, $link-shade-percentage) !default;
$link-hover-decoration: null !default; $link-hover-decoration: null !default;
@ -204,7 +205,7 @@ $border-widths: (
5: 5px 5: 5px
) !default; ) !default;
$border-style: solid !default; $border-style: solid !default;
$border-color: $gray-300 !default; $border-color: color.mix($gray-300, $gray-400) !default;
$border-color-translucent: rgba($black, .175) !default; $border-color-translucent: rgba($black, .175) !default;
// scss-docs-end border-variables // scss-docs-end border-variables
@ -266,8 +267,8 @@ $font-family-code: var(--#{$prefix}font-monospace) !default;
// $font-size-root affects the value of `rem`, which is used for as well font sizes, paddings, and margins // $font-size-root affects the value of `rem`, which is used for as well font sizes, paddings, and margins
// $font-size-base affects the font size of the body text // $font-size-base affects the font size of the body text
$font-size-root: null !default; $font-size-root: 16px !default;
$font-size-base: 1rem !default; // Assumes the browser default, typically `16px` $font-size-base: 14px !default; // Assumes the browser default, typically `16px`
$font-size-sm: $font-size-base * .875 !default; $font-size-sm: $font-size-base * .875 !default;
$font-size-lg: $font-size-base * 1.25 !default; $font-size-lg: $font-size-base * 1.25 !default;
@ -546,8 +547,8 @@ $offcanvas-backdrop-opacity: $modal-backdrop-opacity !default;
// Code // Code
$code-font-size: $small-font-size !default; $code-font-size: 95% !default;
$code-color: $pink !default; $code-color: var(--#{$prefix}secondary-text) !default;
$kbd-padding-y: .1875rem !default; $kbd-padding-y: .1875rem !default;
$kbd-padding-x: .375rem !default; $kbd-padding-x: .375rem !default;
@ -578,7 +579,7 @@ $border-color-translucent-dark: rgba($white, .15) !default;
$headings-color-dark: inherit !default; $headings-color-dark: inherit !default;
$link-color-dark: tint-color($primary, 40%) !default; $link-color-dark: tint-color($primary, 40%) !default;
$link-hover-color-dark: shift-color($link-color-dark, -$link-shade-percentage) !default; $link-hover-color-dark: shift-color($link-color-dark, -$link-shade-percentage) !default;
$code-color-dark: tint-color($code-color, 40%) !default; // $code-color-dark: tint-color($code-color, 40%) !default;
$mark-color-dark: $body-color-dark !default; $mark-color-dark: $body-color-dark !default;
$mark-bg-dark: $yellow-800 !default; $mark-bg-dark: $yellow-800 !default;
@ -587,12 +588,6 @@ $mark-bg-dark: $yellow-800 !default;
// Forms // Forms
// //
$form-select-indicator-color-dark: $body-color-dark !default;
$form-select-indicator-dark: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='none' stroke='#{$form-select-indicator-color-dark}' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/></svg>") !default;
$form-switch-color-dark: rgba($white, .25) !default;
$form-switch-bg-image-dark: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'><circle r='3' fill='#{$form-switch-color-dark}'/></svg>") !default;
// scss-docs-start form-validation-colors-dark // scss-docs-start form-validation-colors-dark
$form-valid-color-dark: $green-300 !default; $form-valid-color-dark: $green-300 !default;
$form-valid-border-color-dark: $green-300 !default; $form-valid-border-color-dark: $green-300 !default;

View File

@ -279,4 +279,19 @@
.btn-sm { .btn-sm {
@include button-size($btn-padding-y-sm, $btn-padding-x-sm, $btn-font-size-sm, $btn-border-radius-sm); @include button-size($btn-padding-y-sm, $btn-padding-x-sm, $btn-font-size-sm, $btn-border-radius-sm);
} }
.btn-check {
position: absolute;
clip: rect(0, 0, 0, 0);
pointer-events: none;
&[disabled],
&:disabled {
+ .btn {
pointer-events: none;
filter: none;
opacity: .65;
}
}
}
} }

View File

@ -33,7 +33,8 @@
:root { :root {
@if $font-size-root != null { @if $font-size-root != null {
@include font-size(var(--#{$prefix}root-font-size)); font-size: var(--#{$prefix}root-font-size);
// @include font-size(var(--#{$prefix}root-font-size));
} }
@if $enable-smooth-scroll { @if $enable-smooth-scroll {
@ -256,12 +257,13 @@
// Links // Links
a { a {
color: rgba(var(--#{$prefix}link-color-rgb), var(--#{$prefix}link-opacity, 1)); color: var(--#{$prefix}link-color);
text-decoration: $link-decoration; text-decoration: var(--#{$prefix}link-decoration);
text-underline-offset: $link-underline-offset;
&:hover { &:hover {
--#{$prefix}link-color-rgb: var(--#{$prefix}link-hover-color-rgb); color: var(--#{$prefix}link-hover-color);
text-decoration: $link-hover-decoration; text-decoration: var(--#{$prefix}link-hover-decoration);
} }
} }

102
scss/forms/_check.scss Normal file
View File

@ -0,0 +1,102 @@
@use "../config" as *;
@use "../colors" as *;
@use "../variables" as *;
@use "../functions" as *;
@use "../vendor/rfs" as *;
@use "../mixins/border-radius" as *;
@use "../mixins/box-shadow" as *;
@use "../mixins/color-mode" as *;
@use "../mixins/focus-ring" as *;
@use "../mixins/transition" as *;
@use "form-variables" as *;
// scss-docs-start check-variables
$check-border-color: var(--#{$prefix}border-color) !default;
$check-checked-bg: var(--#{$prefix}primary-base) !default;
$check-checked-border-color: $check-checked-bg !default;
$check-indeterminate-bg: var(--#{$prefix}primary-base) !default;
$check-indeterminate-border-color: $check-indeterminate-bg !default;
$check-disabled-bg: var(--#{$prefix}secondary-bg) !default;
$check-disabled-border-color: $check-disabled-bg !default;
$check-disabled-opacity: .65 !default;
// scss-docs-end check-variables
@layer forms {
b-checkgroup {
display: flex;
gap: var(--#{$prefix}gap, .5rem);
align-items: var(--#{$prefix}align-items, start);
.description {
color: var(--#{$prefix}secondary-text);
}
}
.check {
// scss-docs-start check-css-variables
--#{$prefix}check-bg: transparent;
--#{$prefix}check-border-color: #{$check-border-color};
--#{$prefix}check-checked-bg: #{$check-checked-bg};
--#{$prefix}check-checked-border-color: #{$check-checked-border-color};
--#{$prefix}check-indeterminate-bg: #{$check-indeterminate-bg};
--#{$prefix}check-indeterminate-border-color: #{$check-indeterminate-border-color};
--#{$prefix}check-disabled-bg: #{$check-disabled-bg};
--#{$prefix}check-disabled-border-color: #{$check-disabled-border-color};
--#{$prefix}check-disabled-opacity: #{$check-disabled-opacity};
// scss-docs-end check-css-variables
display: grid;
grid-template-columns: repeat(1, minmax(0, 1fr));
margin-block: .125rem;
:where(svg, input) {
flex-shrink: 0;
grid-row-start: 1;
grid-column-start: 1;
width: 1rem;
height: 1rem;
}
:where(input) {
appearance: none;
// later: maybe set a tertiary bg color?
background-color: var(--#{$prefix}check-bg);
border: 1px solid var(--#{$prefix}check-border-color);
// stylelint-disable-next-line property-disallowed-list
border-radius: .25em;
}
:where(input:checked, input:indeterminate) {
background-color: var(--#{$prefix}check-checked-bg);
border-color: var(--#{$prefix}check-checked-border-color);
}
&:has(input:checked) .checked,
&:has(input:indeterminate) .indeterminate {
display: block;
color: var(--#{$prefix}primary-contrast);
stroke: currentcolor;
}
&:has(input:disabled) {
--#{$prefix}check-bg: var(--#{$prefix}check-disabled-bg);
~ label {
color: var(--#{$prefix}secondary-text);
cursor: default;
}
}
&:has(input:disabled:checked) {
opacity: var(--#{$prefix}check-disabled-opacity);
}
:where(svg) {
pointer-events: none;
}
:where(svg path) {
display: none;
}
}
}

View File

@ -1,252 +0,0 @@
@use "../config" as *;
@use "../colors" as *;
@use "../variables" as *;
@use "../functions" as *;
@use "../vendor/rfs" as *;
@use "../mixins/border-radius" as *;
@use "../mixins/box-shadow" as *;
@use "../mixins/color-mode" as *;
@use "../mixins/focus-ring" as *;
@use "../mixins/transition" as *;
@use "form-variables" as *;
// scss-docs-start form-check-variables
$form-check-input-width: 1em !default;
$form-check-min-height: $font-size-base * $line-height-base !default;
$form-check-padding-start: $form-check-input-width + .5em !default;
$form-check-margin-bottom: .125rem !default;
$form-check-label-color: null !default;
$form-check-label-cursor: null !default;
$form-check-transition: null !default;
$form-check-input-active-filter: brightness(90%) !default;
$form-check-input-bg: $input-bg !default;
$form-check-input-border: var(--#{$prefix}border-width) solid var(--#{$prefix}border-color) !default;
$form-check-input-border-radius: .25em !default;
$form-check-radio-border-radius: 50% !default;
$form-check-input-focus-border: $input-focus-border-color !default;
$form-check-input-focus-box-shadow: $focus-ring-box-shadow !default;
$form-check-input-checked-color: $component-active-color !default;
$form-check-input-checked-bg-color: $component-active-bg !default;
$form-check-input-checked-border-color: $form-check-input-checked-bg-color !default;
$form-check-input-checked-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'><path fill='none' stroke='#{$form-check-input-checked-color}' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/></svg>") !default;
$form-check-radio-checked-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'><circle r='2' fill='#{$form-check-input-checked-color}'/></svg>") !default;
$form-check-input-indeterminate-color: $component-active-color !default;
$form-check-input-indeterminate-bg-color: $component-active-bg !default;
$form-check-input-indeterminate-border-color: $form-check-input-indeterminate-bg-color !default;
$form-check-input-indeterminate-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'><path fill='none' stroke='#{$form-check-input-indeterminate-color}' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/></svg>") !default;
$form-check-input-disabled-opacity: .5 !default;
$form-check-label-disabled-opacity: $form-check-input-disabled-opacity !default;
$form-check-btn-check-disabled-opacity: $btn-disabled-opacity !default;
$form-check-inline-margin-end: 1rem !default;
// scss-docs-end form-check-variables
// scss-docs-start form-switch-variables
$form-switch-color: rgba($black, .25) !default;
$form-switch-width: 1.5em !default;
$form-switch-padding-start: $form-switch-width + .5em !default;
$form-switch-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'><circle r='3' fill='#{$form-switch-color}'/></svg>") !default;
$form-switch-border-radius: $form-switch-width !default;
$form-switch-transition: background-position .15s ease-in-out !default;
$form-switch-focus-color: $input-focus-border-color !default;
$form-switch-focus-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'><circle r='3' fill='#{$form-switch-focus-color}'/></svg>") !default;
$form-switch-checked-color: $component-active-color !default;
$form-switch-checked-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'><circle r='3' fill='#{$form-switch-checked-color}'/></svg>") !default;
$form-switch-checked-bg-position: right center !default;
// scss-docs-end form-switch-variables
@layer forms {
.form-check {
display: block;
min-height: $form-check-min-height;
padding-left: $form-check-padding-start;
margin-bottom: $form-check-margin-bottom;
.form-check-input {
float: left;
margin-left: $form-check-padding-start * -1;
}
}
.form-check-reverse {
padding-right: $form-check-padding-start;
padding-left: 0;
text-align: right;
.form-check-input {
float: right;
margin-right: $form-check-padding-start * -1;
margin-left: 0;
}
}
.form-check-input {
--#{$prefix}form-check-bg: #{$form-check-input-bg};
flex-shrink: 0;
width: $form-check-input-width;
height: $form-check-input-width;
margin-top: ($line-height-base - $form-check-input-width) * .5; // line-height minus check height
vertical-align: top;
appearance: none;
background-color: var(--#{$prefix}form-check-bg);
background-image: var(--#{$prefix}form-check-bg-image);
background-repeat: no-repeat;
background-position: center;
background-size: contain;
border: $form-check-input-border;
print-color-adjust: exact; // Keep themed appearance for print
@include transition($form-check-transition);
&[type="checkbox"] {
@include border-radius($form-check-input-border-radius);
}
&[type="radio"] {
// stylelint-disable-next-line property-disallowed-list
border-radius: $form-check-radio-border-radius;
}
&:active {
filter: $form-check-input-active-filter;
}
&:focus-visible {
border-color: $form-check-input-focus-border;
@include focus-ring(true);
--#{$prefix}focus-ring-offset: 1px;
// box-shadow: $form-check-input-focus-box-shadow;
}
&:checked {
background-color: $form-check-input-checked-bg-color;
border-color: $form-check-input-checked-border-color;
&[type="checkbox"] {
@if $enable-gradients {
--#{$prefix}form-check-bg-image: #{escape-svg($form-check-input-checked-bg-image)}, var(--#{$prefix}gradient);
} @else {
--#{$prefix}form-check-bg-image: #{escape-svg($form-check-input-checked-bg-image)};
}
}
&[type="radio"] {
@if $enable-gradients {
--#{$prefix}form-check-bg-image: #{escape-svg($form-check-radio-checked-bg-image)}, var(--#{$prefix}gradient);
} @else {
--#{$prefix}form-check-bg-image: #{escape-svg($form-check-radio-checked-bg-image)};
}
}
}
&[type="checkbox"]:indeterminate {
background-color: $form-check-input-indeterminate-bg-color;
border-color: $form-check-input-indeterminate-border-color;
@if $enable-gradients {
--#{$prefix}form-check-bg-image: #{escape-svg($form-check-input-indeterminate-bg-image)}, var(--#{$prefix}gradient);
} @else {
--#{$prefix}form-check-bg-image: #{escape-svg($form-check-input-indeterminate-bg-image)};
}
}
&:disabled {
pointer-events: none;
filter: none;
opacity: $form-check-input-disabled-opacity;
}
// Use disabled attribute in addition of :disabled pseudo-class
// See: https://github.com/twbs/bootstrap/issues/28247
&[disabled],
&:disabled {
~ .form-check-label {
cursor: default;
opacity: $form-check-label-disabled-opacity;
}
}
}
.form-check-label {
color: $form-check-label-color;
cursor: $form-check-label-cursor;
}
//
// Switch
//
.form-switch {
padding-left: $form-switch-padding-start;
.form-check-input {
--#{$prefix}form-switch-bg: #{escape-svg($form-switch-bg-image)};
width: $form-switch-width;
margin-left: $form-switch-padding-start * -1;
background-image: var(--#{$prefix}form-switch-bg);
background-position: left center;
@include border-radius($form-switch-border-radius, 0);
@include transition($form-switch-transition);
&:focus {
--#{$prefix}form-switch-bg: #{escape-svg($form-switch-focus-bg-image)};
}
&:checked {
background-position: $form-switch-checked-bg-position;
@if $enable-gradients {
--#{$prefix}form-switch-bg: #{escape-svg($form-switch-checked-bg-image)}, var(--#{$prefix}gradient);
} @else {
--#{$prefix}form-switch-bg: #{escape-svg($form-switch-checked-bg-image)};
}
}
}
&.form-check-reverse {
padding-right: $form-switch-padding-start;
padding-left: 0;
.form-check-input {
margin-right: $form-switch-padding-start * -1;
margin-left: 0;
}
}
}
.form-check-inline {
display: inline-block;
margin-right: $form-check-inline-margin-end;
}
.btn-check {
position: absolute;
clip: rect(0, 0, 0, 0);
pointer-events: none;
&[disabled],
&:disabled {
+ .btn {
pointer-events: none;
filter: none;
opacity: $form-check-btn-check-disabled-opacity;
}
}
}
@if $enable-dark-mode {
@include color-mode(dark) {
.form-switch .form-check-input:not(:checked):not(:focus) {
--#{$prefix}form-switch-bg: #{escape-svg($form-switch-bg-image-dark)};
}
}
}
}

View File

@ -1,36 +1,45 @@
@use "sass:math"; @use "sass:math";
@use "../config" as *; @use "../config" as *;
@use "../variables" as *; @use "../variables" as *;
@use "../functions" as *;
@use "../vendor/rfs" as *; @use "../vendor/rfs" as *;
@use "../mixins/border-radius" as *; @use "../mixins/border-radius" as *;
@use "../mixins/box-shadow" as *; @use "../mixins/box-shadow" as *;
@use "../mixins/color-mode" as *;
@use "../mixins/focus-ring" as *; @use "../mixins/focus-ring" as *;
@use "../mixins/gradients" as *; @use "../mixins/gradients" as *;
@use "../mixins/transition" as *; @use "../mixins/transition" as *;
@use "form-variables" as *; @use "form-variables" as *;
//
// General form controls (plus a few specific high-level interventions)
//
@layer forms { @layer forms {
.form-control { .form-control {
--#{$prefix}control-min-height: #{$control-min-height};
--#{$prefix}control-padding-y: #{$control-padding-y};
--#{$prefix}control-padding-x: #{$control-padding-x};
--#{$prefix}control-font-size: #{$control-font-size};
--#{$prefix}control-line-height: #{$control-line-height};
--#{$prefix}control-color: #{$control-color};
--#{$prefix}control-bg: #{$control-bg};
--#{$prefix}control-border-width: #{$control-border-width};
--#{$prefix}control-border-color: #{$control-border-color};
--#{$prefix}control-border-radius: #{$control-border-radius};
--#{$prefix}control-select-bg-color: #{$control-select-indicator-color};
--#{$prefix}control-select-bg: #{escape-svg($control-select-indicator)};
--#{$prefix}control-select-bg-position: #{$control-select-bg-position};
--#{$prefix}control-select-bg-size: #{$control-select-bg-size};
display: block; display: block;
width: 100%; width: 100%;
padding: $input-padding-y $input-padding-x; min-height: var(--#{$prefix}control-min-height);
font-family: $input-font-family; padding: var(--#{$prefix}control-padding-y) var(--#{$prefix}control-padding-x);
@include font-size($input-font-size); font-size: var(--#{$prefix}control-font-size);
font-weight: $input-font-weight; line-height: var(--#{$prefix}control-line-height);
line-height: $input-line-height; color: var(--#{$prefix}control-color);
color: $input-color; appearance: none;
appearance: none; // Fix appearance for date inputs in Safari background-color: var(--#{$prefix}control-bg);
background-color: $input-bg;
background-clip: padding-box; background-clip: padding-box;
border: $input-border-width solid $input-border-color; border: var(--#{$prefix}control-border-width) solid var(--#{$prefix}control-border-color);
@include border-radius(var(--#{$prefix}control-border-radius), 0);
// 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 box-shadow($input-box-shadow);
@include transition($input-transition); @include transition($input-transition);
@ -99,16 +108,17 @@
// File input buttons theming // File input buttons theming
&::file-selector-button { &::file-selector-button {
padding: $input-padding-y $input-padding-x; min-height: var(--#{$prefix}control-min-height);
margin: (-$input-padding-y) (-$input-padding-x); padding: var(--#{$prefix}control-padding-y) var(--#{$prefix}control-padding-x);
margin-inline-end: $input-padding-x; margin: calc(var(--#{$prefix}control-padding-y) * -1) calc(var(--#{$prefix}control-padding-x) * -1);
margin-inline-end: var(--#{$prefix}control-padding-x);
color: $form-file-button-color; color: $form-file-button-color;
@include gradient-bg($form-file-button-bg); @include gradient-bg($form-file-button-bg);
pointer-events: none; pointer-events: none;
border-color: inherit; border-color: inherit;
border-style: solid; border-style: solid;
border-width: 0; border-width: 0;
border-inline-end-width: $input-border-width; border-inline-end-width: var(--#{$prefix}control-border-width);
border-radius: 0; // stylelint-disable-line property-disallowed-list border-radius: 0; // stylelint-disable-line property-disallowed-list
@include transition($btn-transition); @include transition($btn-transition);
} }
@ -145,6 +155,28 @@
} }
} }
// stylelint-disable selector-no-qualifying-type
select.form-control {
padding-right: calc(var(--#{$prefix}control-padding-x) * 3);
background-image: var(--#{$prefix}control-select-bg);
background-repeat: no-repeat;
background-position: var(--#{$prefix}control-select-bg-position);
background-size: var(--#{$prefix}control-select-bg-size);
&[multiple],
&[size]:not([size="1"]) {
padding-right: var(--#{$prefix}control-padding-x);
background-image: none;
}
@if $enable-dark-mode {
@include color-mode(dark) {
--#{$prefix}control-select-indicator: #{escape-svg($control-select-indicator-dark)};
}
}
}
// stylelint-enable selector-no-qualifying-type
// Form control sizing // Form control sizing
// //
// Build on `.form-control` with modifier classes to decrease or increase the // Build on `.form-control` with modifier classes to decrease or increase the
@ -153,48 +185,40 @@
// Repeated in `_input_group.scss` to avoid Sass extend issues. // Repeated in `_input_group.scss` to avoid Sass extend issues.
.form-control-sm { .form-control-sm {
min-height: $input-height-sm; --#{$prefix}control-min-height: #{$control-min-height-sm};
padding: $input-padding-y-sm $input-padding-x-sm; --#{$prefix}control-padding-y: #{$control-padding-y-sm};
@include font-size($input-font-size-sm); --#{$prefix}control-padding-x: #{$control-padding-x-sm};
@include border-radius($input-border-radius-sm); --#{$prefix}control-font-size: #{$control-font-size-sm};
--#{$prefix}control-line-height: #{$control-line-height-sm};
&::file-selector-button { --#{$prefix}control-border-radius: #{$control-border-radius-sm};
padding: $input-padding-y-sm $input-padding-x-sm;
margin: (-$input-padding-y-sm) (-$input-padding-x-sm);
margin-inline-end: $input-padding-x-sm;
}
} }
.form-control-lg { .form-control-lg {
min-height: $input-height-lg; --#{$prefix}control-min-height: #{$control-min-height-lg};
padding: $input-padding-y-lg $input-padding-x-lg; --#{$prefix}control-padding-y: #{$control-padding-y-lg};
@include font-size($input-font-size-lg); --#{$prefix}control-padding-x: #{$control-padding-x-lg};
@include border-radius($input-border-radius-lg); --#{$prefix}control-font-size: #{$control-font-size-lg};
--#{$prefix}control-line-height: #{$control-line-height-lg};
&::file-selector-button { --#{$prefix}control-border-radius: #{$control-border-radius-lg};
padding: $input-padding-y-lg $input-padding-x-lg;
margin: (-$input-padding-y-lg) (-$input-padding-x-lg);
margin-inline-end: $input-padding-x-lg;
}
} }
// Make sure textareas don't shrink too much when resized // // Make sure textareas don't shrink too much when resized
// https://github.com/twbs/bootstrap/pull/29124 // // https://github.com/twbs/bootstrap/pull/29124
// stylelint-disable selector-no-qualifying-type // // stylelint-disable selector-no-qualifying-type
textarea { // textarea {
&.form-control { // &.form-control {
min-height: $input-height; // min-height: $input-height;
} // }
&.form-control-sm { // &.form-control-sm {
min-height: $input-height-sm; // min-height: $input-height-sm;
} // }
&.form-control-lg { // &.form-control-lg {
min-height: $input-height-lg; // min-height: $input-height-lg;
} // }
} // }
// stylelint-enable selector-no-qualifying-type // // stylelint-enable selector-no-qualifying-type
.form-control-color { .form-control-color {
width: $form-color-width; width: $form-color-width;

View File

@ -1,127 +0,0 @@
@use "../config" as *;
@use "../colors" as *;
@use "../variables" as *;
@use "../functions" as *;
@use "../vendor/rfs" as *;
@use "../mixins/border-radius" as *;
@use "../mixins/box-shadow" as *;
@use "../mixins/color-mode" as *;
@use "../mixins/focus-ring" as *;
@use "../mixins/transition" as *;
@use "form-variables" as *;
// scss-docs-start form-select-variables
$form-select-padding-y: $input-padding-y !default;
$form-select-padding-x: $input-padding-x !default;
$form-select-font-family: $input-font-family !default;
$form-select-font-size: $input-font-size !default;
$form-select-indicator-padding: $form-select-padding-x * 3 !default; // Extra padding for background-image
$form-select-font-weight: $input-font-weight !default;
$form-select-line-height: $input-line-height !default;
$form-select-color: $input-color !default;
$form-select-bg: $input-bg !default;
$form-select-disabled-color: null !default;
$form-select-disabled-bg: $input-disabled-bg !default;
$form-select-disabled-border-color: $input-disabled-border-color !default;
$form-select-bg-position: right $form-select-padding-x center !default;
$form-select-bg-size: 16px 12px !default; // In pixels because image dimensions
$form-select-indicator-color: $gray-800 !default;
$form-select-indicator: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='none' stroke='#{$form-select-indicator-color}' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/></svg>") !default;
$form-select-feedback-icon-padding-end: $form-select-padding-x * 2.5 + $form-select-indicator-padding !default;
$form-select-feedback-icon-position: center right $form-select-indicator-padding !default;
$form-select-feedback-icon-size: $input-height-inner-half $input-height-inner-half !default;
$form-select-border-width: $input-border-width !default;
$form-select-border-color: $input-border-color !default;
$form-select-border-radius: $input-border-radius !default;
$form-select-box-shadow: var(--#{$prefix}box-shadow-inset) !default;
$form-select-focus-border-color: $input-focus-border-color !default;
$form-select-focus-width: $input-focus-width !default;
// $form-select-focus-box-shadow: 0 0 0 $form-select-focus-width $input-btn-focus-color !default;
$form-select-padding-y-sm: $input-padding-y-sm !default;
$form-select-padding-x-sm: $input-padding-x-sm !default;
$form-select-font-size-sm: $input-font-size-sm !default;
$form-select-border-radius-sm: $input-border-radius-sm !default;
$form-select-padding-y-lg: $input-padding-y-lg !default;
$form-select-padding-x-lg: $input-padding-x-lg !default;
$form-select-font-size-lg: $input-font-size-lg !default;
$form-select-border-radius-lg: $input-border-radius-lg !default;
$form-select-transition: $input-transition !default;
// scss-docs-end form-select-variables
@layer forms {
.form-select {
--#{$prefix}form-select-bg-img: #{escape-svg($form-select-indicator)};
display: block;
width: 100%;
padding: $form-select-padding-y $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;
appearance: none;
background-color: $form-select-bg;
background-image: var(--#{$prefix}form-select-bg-img), var(--#{$prefix}form-select-bg-icon, none);
background-repeat: no-repeat;
background-position: $form-select-bg-position;
background-size: $form-select-bg-size;
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);
@include transition($form-select-transition);
&:focus-visible {
border-color: $form-select-focus-border-color;
@include focus-ring(true);
}
&[multiple],
&[size]:not([size="1"]) {
padding-right: $form-select-padding-x;
background-image: none;
}
&:disabled {
color: $form-select-disabled-color;
background-color: $form-select-disabled-bg;
border-color: $form-select-disabled-border-color;
}
// Remove outline from select box in FF
&:-moz-focusring {
color: transparent;
text-shadow: 0 0 0 $form-select-color;
}
}
.form-select-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);
@include border-radius($form-select-border-radius-sm);
}
.form-select-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);
@include border-radius($form-select-border-radius-lg);
}
@if $enable-dark-mode {
@include color-mode(dark) {
.form-select {
--#{$prefix}form-select-bg-img: #{escape-svg($form-select-indicator-dark)};
}
}
}
}

View File

@ -1,18 +1,53 @@
@use "../config" as *; @use "../config" as *;
@use "../colors" as *;
@use "../variables" as *; @use "../variables" as *;
$control-min-height: 2.5rem !default;
$control-min-height-sm: 2rem !default;
$control-min-height-lg: 3rem !default;
$control-padding-y: .375rem !default;
$control-padding-x: .75rem !default;
$control-font-size: $font-size-base !default;
$control-line-height: $line-height-base !default;
$control-color: var(--#{$prefix}body-color) !default;
$control-bg: var(--#{$prefix}body-bg) !default;
$control-border-width: var(--#{$prefix}border-width) !default;
$control-border-color: var(--#{$prefix}border-color) !default;
$control-border-radius: var(--#{$prefix}border-radius) !default;
$control-padding-y-sm: .25rem !default;
$control-padding-x-sm: .5rem !default;
$control-font-size-sm: $font-size-sm !default;
$control-line-height-sm: $line-height-sm !default;
$control-border-radius-sm: var(--#{$prefix}border-radius-sm) !default;
$control-padding-y-lg: .5rem !default;
$control-padding-x-lg: 1rem !default;
$control-font-size-lg: $font-size-lg !default;
$control-line-height-lg: $line-height-lg !default;
$control-border-radius-lg: var(--#{$prefix}border-radius-lg) !default;
$control-select-indicator-color: $gray-600 !default;
$control-select-indicator: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='none' stroke='#{$control-select-indicator-color}' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/></svg>") !default;
$control-select-bg-position: right $control-padding-x center !default;
$control-select-bg-size: 16px 12px !default;
$control-select-indicator-color-dark: $body-color-dark !default;
$control-select-indicator-dark: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='none' stroke='#{$control-select-indicator-color-dark}' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/></svg>") !default;
// scss-docs-start input-btn-variables // scss-docs-start input-btn-variables
$input-btn-padding-y: .375rem !default; $input-btn-padding-y: .375rem !default;
$input-btn-padding-x: .75rem !default; $input-btn-padding-x: .75rem !default;
$input-btn-font-family: null !default; // $input-btn-font-family: null !default;
$input-btn-font-size: $font-size-base !default; $input-btn-font-size: $font-size-base !default;
$input-btn-line-height: $line-height-base !default; $input-btn-line-height: $line-height-base !default;
$input-btn-focus-width: $focus-ring-width !default; // $input-btn-focus-width: $focus-ring-width !default;
$input-btn-focus-color-opacity: $focus-ring-opacity !default; // $input-btn-focus-color-opacity: $focus-ring-opacity !default;
$input-btn-focus-color: $focus-ring-color !default; // $input-btn-focus-color: $focus-ring-color !default;
$input-btn-focus-blur: $focus-ring-blur !default; // $input-btn-focus-blur: $focus-ring-blur !default;
$input-btn-focus-box-shadow: $focus-ring-box-shadow !default; // $input-btn-focus-box-shadow: $focus-ring-box-shadow !default;
$input-btn-padding-y-sm: .25rem !default; $input-btn-padding-y-sm: .25rem !default;
$input-btn-padding-x-sm: .5rem !default; $input-btn-padding-x-sm: .5rem !default;
@ -22,15 +57,15 @@ $input-btn-padding-y-lg: .5rem !default;
$input-btn-padding-x-lg: 1rem !default; $input-btn-padding-x-lg: 1rem !default;
$input-btn-font-size-lg: $font-size-lg !default; $input-btn-font-size-lg: $font-size-lg !default;
$input-btn-border-width: var(--#{$prefix}border-width) !default; // $input-btn-border-width: var(--#{$prefix}border-width) !default;
// scss-docs-end input-btn-variables // scss-docs-end input-btn-variables
// scss-docs-start form-input-variables // scss-docs-start form-input-variables
$input-padding-y: $input-btn-padding-y !default; $input-padding-y: $input-btn-padding-y !default;
$input-padding-x: $input-btn-padding-x !default; $input-padding-x: $input-btn-padding-x !default;
$input-font-family: $input-btn-font-family !default; // $input-font-family: $input-btn-font-family !default;
$input-font-size: $input-btn-font-size !default; $input-font-size: $input-btn-font-size !default;
$input-font-weight: $font-weight-base !default; // $input-font-weight: $font-weight-base !default;
$input-line-height: $input-btn-line-height !default; $input-line-height: $input-btn-line-height !default;
$input-padding-y-sm: $input-btn-padding-y-sm !default; $input-padding-y-sm: $input-btn-padding-y-sm !default;
@ -48,7 +83,7 @@ $input-disabled-border-color: null !default;
$input-color: var(--#{$prefix}body-color) !default; $input-color: var(--#{$prefix}body-color) !default;
$input-border-color: var(--#{$prefix}border-color) !default; $input-border-color: var(--#{$prefix}border-color) !default;
$input-border-width: $input-btn-border-width !default; $input-border-width: var(--#{$prefix}border-width) !default;
$input-box-shadow: var(--#{$prefix}box-shadow-inset) !default; $input-box-shadow: var(--#{$prefix}box-shadow-inset) !default;
$input-border-radius: var(--#{$prefix}border-radius) !default; $input-border-radius: var(--#{$prefix}border-radius) !default;
@ -70,9 +105,12 @@ $input-height-inner: add($input-line-height * 1em, $input-pad
$input-height-inner-half: add($input-line-height * .5em, $input-padding-y) !default; $input-height-inner-half: add($input-line-height * .5em, $input-padding-y) !default;
$input-height-inner-quarter: add($input-line-height * .25em, $input-padding-y * .5) !default; $input-height-inner-quarter: add($input-line-height * .25em, $input-padding-y * .5) !default;
$input-height: add($input-line-height * 1em, add($input-padding-y * 2, $input-height-border, false)) !default; // $input-height: add($input-line-height * 1em, add($input-padding-y * 2, $input-height-border, false)) !default;
$input-height-sm: add($input-line-height * 1em, add($input-padding-y-sm * 2, $input-height-border, false)) !default; // $input-height-sm: add($input-line-height * 1em, add($input-padding-y-sm * 2, $input-height-border, false)) !default;
$input-height-lg: add($input-line-height * 1em, add($input-padding-y-lg * 2, $input-height-border, false)) !default; // $input-height-lg: add($input-line-height * 1em, add($input-padding-y-lg * 2, $input-height-border, false)) !default;
$input-height: 2.5rem !default;
$input-height-sm: 2rem !default;
$input-height-lg: 3rem !default;
$input-transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out !default; $input-transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;

View File

@ -10,7 +10,7 @@
// scss-docs-start input-group-variables // scss-docs-start input-group-variables
$input-group-addon-padding-y: $input-padding-y !default; $input-group-addon-padding-y: $input-padding-y !default;
$input-group-addon-padding-x: $input-padding-x !default; $input-group-addon-padding-x: $input-padding-x !default;
$input-group-addon-font-weight: $input-font-weight !default; // $input-group-addon-font-weight: $input-font-weight !default;
$input-group-addon-color: $input-color !default; $input-group-addon-color: $input-color !default;
$input-group-addon-bg: var(--#{$prefix}tertiary-bg) !default; $input-group-addon-bg: var(--#{$prefix}tertiary-bg) !default;
$input-group-addon-border-color: $input-border-color !default; $input-group-addon-border-color: $input-border-color !default;
@ -64,7 +64,7 @@ $input-group-addon-border-color: $input-border-color !default;
align-items: center; align-items: center;
padding: $input-group-addon-padding-y $input-group-addon-padding-x; padding: $input-group-addon-padding-y $input-group-addon-padding-x;
@include font-size($input-font-size); // Match inputs @include font-size($input-font-size); // Match inputs
font-weight: $input-group-addon-font-weight; // font-weight: $input-group-addon-font-weight;
line-height: $input-line-height; line-height: $input-line-height;
color: $input-group-addon-color; color: $input-group-addon-color;
text-align: center; text-align: center;

79
scss/forms/_radio.scss Normal file
View File

@ -0,0 +1,79 @@
@use "../config" as *;
@use "../colors" as *;
@use "../variables" as *;
@use "../functions" as *;
@use "../vendor/rfs" as *;
@use "../mixins/border-radius" as *;
@use "../mixins/box-shadow" as *;
@use "../mixins/color-mode" as *;
@use "../mixins/focus-ring" as *;
@use "../mixins/transition" as *;
@use "form-variables" as *;
// scss-docs-start radio-variables
$radio-border-color: var(--#{$prefix}border-color) !default;
$radio-checked-bg: var(--#{$prefix}primary-base) !default;
$radio-checked-border-color: $radio-checked-bg !default;
$radio-disabled-bg: var(--#{$prefix}secondary-bg) !default;
$radio-disabled-border-color: $radio-disabled-bg !default;
$radio-disabled-opacity: .65 !default;
// scss-docs-end radio-variables
@layer forms {
b-radiogroup {
display: flex;
gap: var(--#{$prefix}gap, .5rem);
align-items: var(--#{$prefix}align-items, start);
.description {
color: var(--#{$prefix}secondary-text);
}
}
.radio {
// scss-docs-start radio-css-variables
--#{$prefix}radio-bg: transparent;
--#{$prefix}radio-border-color: #{$radio-border-color};
--#{$prefix}radio-checked-bg: #{$radio-checked-bg};
--#{$prefix}radio-checked-border-color: #{$radio-checked-border-color};
--#{$prefix}radio-disabled-bg: #{$radio-disabled-bg};
--#{$prefix}radio-disabled-border-color: #{$radio-disabled-border-color};
--#{$prefix}radio-disabled-opacity: #{$radio-disabled-opacity};
// scss-docs-end radio-css-variables
position: relative;
flex-shrink: 0;
width: 1rem;
height: 1rem;
margin-block: .125rem;
appearance: none;
background-color: var(--#{$prefix}radio-bg);
border: 1px solid var(--#{$prefix}radio-border-color);
// stylelint-disable-next-line property-disallowed-list
border-radius: 50%;
&:checked {
color: var(--#{$prefix}primary-contrast);
background-color: var(--#{$prefix}radio-checked-bg);
border-color: var(--#{$prefix}radio-checked-border-color);
&::before {
position: absolute;
inset: .25rem;
content: "";
background-color: currentcolor;
// stylelint-disable-next-line property-disallowed-list
border-radius: 50%;
}
}
&:disabled {
--#{$prefix}radio-bg: var(--#{$prefix}radio-disabled-bg);
~ label {
color: var(--#{$prefix}secondary-text);
cursor: default;
}
}
}
}

89
scss/forms/_switch.scss Normal file
View File

@ -0,0 +1,89 @@
@use "../config" as *;
@use "../colors" as *;
@use "../variables" as *;
@use "../functions" as *;
@use "../vendor/rfs" as *;
@use "../mixins/border-radius" as *;
@use "../mixins/box-shadow" as *;
@use "../mixins/color-mode" as *;
@use "../mixins/focus-ring" as *;
@use "../mixins/transition" as *;
@use "form-variables" as *;
@layer forms {
.switch {
// scss-docs-start switch-css-variables
--#{$prefix}switch-height: 1.25rem;
--#{$prefix}switch-width: calc(var(--#{$prefix}switch-height) * 1.5);
--#{$prefix}switch-padding: .0625rem;
--#{$prefix}switch-bg: var(--#{$prefix}secondary-bg);
--#{$prefix}switch-border-width: var(--#{$prefix}border-width);
--#{$prefix}switch-border-color: var(--#{$prefix}border-color);
--#{$prefix}switch-indicator-bg: var(--#{$prefix}white);
--#{$prefix}switch-checked-bg: var(--#{$prefix}primary-base);
--#{$prefix}switch-checked-indicator-bg: var(--#{$prefix}white);
--#{$prefix}switch-disabled-bg: var(--#{$prefix}secondary-bg);
--#{$prefix}switch-disabled-indicator-bg: var(--#{$prefix}secondary-text);
// scss-docs-end switch-css-variables
position: relative;
display: flex;
flex-shrink: 0;
align-items: stretch;
justify-content: flex-start;
width: var(--#{$prefix}switch-width);
height: var(--#{$prefix}switch-height);
padding: var(--#{$prefix}switch-padding);
background-color: var(--#{$prefix}switch-bg);
border: var(--#{$prefix}switch-border-width) solid var(--#{$prefix}switch-border-color);
// stylelint-disable-next-line property-disallowed-list
border-radius: 10rem;
box-shadow: inset 0 1px 2px rgba($black, .05);
// stylelint-disable-next-line property-disallowed-list
transition: .15s ease-in-out;
transition-property: padding-inline-start, background-color;
&::before {
flex-shrink: 0;
width: calc(var(--#{$prefix}switch-height) - calc(var(--#{$prefix}switch-padding) * 2) - var(--#{$prefix}switch-border-width) * 2);
height: calc(var(--#{$prefix}switch-height) - calc(var(--#{$prefix}switch-padding) * 2) - var(--#{$prefix}switch-border-width) * 2);
// width: calc(var(--#{$prefix}switch-height) - calc(var(--#{$prefix}switch-padding) * 2));
// height: calc(var(--#{$prefix}switch-height) - calc(var(--#{$prefix}switch-padding) * 2));
content: "";
background-color: var(--#{$prefix}switch-indicator-bg);
// stylelint-disable-next-line property-disallowed-list
border-radius: 50%;
box-shadow: 0 1px 2px rgba($black, .1);
}
input {
position: absolute;
inset: 0;
appearance: none;
background-color: transparent;
}
&:has(input:checked) {
padding-inline-start: calc(var(--#{$prefix}switch-height) / 2 + var(--#{$prefix}switch-padding));
background-color: var(--#{$prefix}primary-base);
}
&:has(input:disabled) {
--#{$prefix}switch-bg: var(--#{$prefix}switch-disabled-bg);
--#{$prefix}switch-indicator-bg: var(--#{$prefix}switch-disabled-indicator-bg);
&::before { opacity: .4; }
~ label {
color: var(--#{$prefix}secondary-text);
cursor: default;
}
}
}
.switch-sm {
--#{$prefix}switch-height: 1em;
}
.switch-lg {
--#{$prefix}switch-height: 2em;
}
}

View File

@ -1,8 +1,9 @@
@forward "labels"; @forward "labels";
@forward "form-text"; @forward "form-text";
@forward "form-control"; @forward "form-control";
@forward "form-select"; @forward "check";
@forward "form-check"; @forward "radio";
@forward "switch";
@forward "form-range"; @forward "form-range";
@forward "floating-labels"; @forward "floating-labels";
@forward "input-group"; @forward "input-group";

View File

@ -1,13 +1,24 @@
// stylelint-disable selector-no-qualifying-type
@layer helpers { @layer helpers {
// scss-docs-start stacks // scss-docs-start stacks
.hstack { .hstack,
b-hstack {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
align-self: stretch; align-self: stretch;
} }
.hstack-start,
b-hstack[align="start"] {
display: flex;
flex-direction: row;
align-items: flex-start;
align-self: stretch;
}
.vstack { .vstack,
b-vstack {
display: flex; display: flex;
flex: 1 1 auto; flex: 1 1 auto;
flex-direction: column; flex-direction: column;

View File

@ -60,8 +60,9 @@
pages: pages:
- title: Overview - title: Overview
- title: Form control - title: Form control
- title: Select - title: Checkbox
- title: Checks & radios - title: Radio
- title: Switch
- title: Range - title: Range
- title: Input group - title: Input group
- title: Floating labels - title: Floating labels

View File

@ -44,7 +44,7 @@ These classes can also be added to groups of links, as an alternative to the [`.
## Checkbox and radio button groups ## Checkbox and radio button groups
Combine button-like checkbox and radio [toggle buttons]([[docsref:/forms/checks-radios]]) into a seamless looking button group. Combine button-like checkbox and radio toggle buttons into a seamless looking button group.
<Example code={`<div class="btn-group" role="group" aria-label="Basic checkbox toggle button group"> <Example code={`<div class="btn-group" role="group" aria-label="Basic checkbox toggle button group">
<input type="checkbox" class="btn-check" id="btncheck1" autocomplete="off"> <input type="checkbox" class="btn-check" id="btncheck1" autocomplete="off">

View File

@ -15,7 +15,7 @@ Bootstrap has a base `.btn` class that sets up basic styles such as padding and
The `.btn` class is intended to be used in conjunction with our button variants, or to serve as a basis for your own custom styles. The `.btn` class is intended to be used in conjunction with our button variants, or to serve as a basis for your own custom styles.
<Callout type="warning"> <Callout type="warning">
If you are using the `.btn` class on its own, remember to at least define some explicit `:focus` and/or `:focus-visible` styles. When using `.btn` without a modifier, be sure to add some explicit `:focus-visible` styles.
</Callout> </Callout>
## Variants ## Variants
@ -126,12 +126,82 @@ Additional utilities can be used to adjust the alignment of buttons when horizon
<button class="btn btn-primary" type="button">Button</button> <button class="btn btn-primary" type="button">Button</button>
</div>`} /> </div>`} />
## Toggle buttons
Create button-like checkboxes and radio buttons by using `.btn` styles rather than `.form-check-label` on the `<label>` elements. These toggle buttons can further be grouped in a [button group]([[docsref:/components/button-group]]) if needed.
### Checkbox toggle buttons
<Example code={`<input type="checkbox" class="btn-check" id="btn-check" autocomplete="off">
<label class="btn btn-primary" for="btn-check">Single toggle</label>
<input type="checkbox" class="btn-check" id="btn-check-2" checked autocomplete="off">
<label class="btn btn-primary" for="btn-check-2">Checked</label>
<input type="checkbox" class="btn-check" id="btn-check-3" autocomplete="off" disabled>
<label class="btn btn-primary" for="btn-check-3">Disabled</label>`} />
<Example code={`<input type="checkbox" class="btn-check" id="btn-check-4" autocomplete="off">
<label class="btn" for="btn-check-4">Single toggle</label>
<input type="checkbox" class="btn-check" id="btn-check-5" checked autocomplete="off">
<label class="btn" for="btn-check-5">Checked</label>
<input type="checkbox" class="btn-check" id="btn-check-6" autocomplete="off" disabled>
<label class="btn" for="btn-check-6">Disabled</label>`} />
<Callout>
Visually, these checkbox toggle buttons are identical to the [button plugin toggle buttons]([[docsref:/components/buttons#button-plugin]]). However, they are conveyed differently by assistive technologies: the checkbox toggles will be announced by screen readers as “checked“/“not checked“ (since, despite their appearance, they are fundamentally still checkboxes), whereas the button plugin toggle buttons will be announced as “button“/“button pressed“. The choice between these two approaches will depend on the type of toggle you are creating, and whether or not the toggle will make sense to users when announced as a checkbox or as an actual button.
</Callout>
### Radio toggle buttons
<Example code={`<input type="radio" class="btn-check" name="options" id="option1" autocomplete="off" checked>
<label class="btn btn-secondary" for="option1">Checked</label>
<input type="radio" class="btn-check" name="options" id="option2" autocomplete="off">
<label class="btn btn-secondary" for="option2">Radio</label>
<input type="radio" class="btn-check" name="options" id="option3" autocomplete="off" disabled>
<label class="btn btn-secondary" for="option3">Disabled</label>
<input type="radio" class="btn-check" name="options" id="option4" autocomplete="off">
<label class="btn btn-secondary" for="option4">Radio</label>`} />
<Example code={`<input type="radio" class="btn-check" name="options-base" id="option5" autocomplete="off" checked>
<label class="btn" for="option5">Checked</label>
<input type="radio" class="btn-check" name="options-base" id="option6" autocomplete="off">
<label class="btn" for="option6">Radio</label>
<input type="radio" class="btn-check" name="options-base" id="option7" autocomplete="off" disabled>
<label class="btn" for="option7">Disabled</label>
<input type="radio" class="btn-check" name="options-base" id="option8" autocomplete="off">
<label class="btn" for="option8">Radio</label>`} />
### Outlined styles
Different variants of `.btn`, such as the various outlined styles, are supported.
<Example code={`<input type="checkbox" class="btn-check" id="btn-check-outlined" autocomplete="off">
<label class="btn btn-outline-primary" for="btn-check-outlined">Single toggle</label><br>
<input type="checkbox" class="btn-check" id="btn-check-2-outlined" checked autocomplete="off">
<label class="btn btn-outline-secondary" for="btn-check-2-outlined">Checked</label><br>
<input type="radio" class="btn-check" name="options-outlined" id="success-outlined" autocomplete="off" checked>
<label class="btn btn-outline-success" for="success-outlined">Checked success radio</label>
<input type="radio" class="btn-check" name="options-outlined" id="danger-outlined" autocomplete="off">
<label class="btn btn-outline-danger" for="danger-outlined">Danger radio</label>`} />
## Button plugin ## Button plugin
The button plugin allows you to create simple on/off toggle buttons. The button plugin allows you to create simple on/off toggle buttons.
<Callout> <Callout>
Visually, these toggle buttons are identical to the [checkbox toggle buttons]([[docsref:/forms/checks-radios#checkbox-toggle-buttons]]). However, they are conveyed differently by assistive technologies: the checkbox toggles will be announced by screen readers as “checked”/“not checked” (since, despite their appearance, they are fundamentally still checkboxes), whereas these toggle buttons will be announced as “button”/“button pressed”. The choice between these two approaches will depend on the type of toggle you are creating, and whether or not the toggle will make sense to users when announced as a checkbox or as an actual button. Visually, these toggle buttons are identical to the [checkbox toggle buttons]([[docsref:/forms/checkbox]]). However, they are conveyed differently by assistive technologies: the checkbox toggles will be announced by screen readers as “checked”/“not checked” (since, despite their appearance, they are fundamentally still checkboxes), whereas these toggle buttons will be announced as “button”/“button pressed”. The choice between these two approaches will depend on the type of toggle you are creating, and whether or not the toggle will make sense to users when announced as a checkbox or as an actual button.
</Callout> </Callout>
### Toggle states ### Toggle states

View File

@ -42,10 +42,7 @@ Several Bootstrap components include embedded SVGs in our CSS to style component
- [Accordion]([[docsref:/components/accordion]]) - [Accordion]([[docsref:/components/accordion]])
- [Carousel controls]([[docsref:/components/carousel#with-controls]]) - [Carousel controls]([[docsref:/components/carousel#with-controls]])
- [Close button]([[docsref:/components/close-button]]) (used in alerts and modals) - [Close button]([[docsref:/components/close-button]]) (used in alerts and modals)
- [Form checkboxes and radio buttons]([[docsref:/forms/checks-radios]])
- [Form switches]([[docsref:/forms/checks-radios#switches]])
- [Form validation icons]([[docsref:/forms/validation#server-side]]) - [Form validation icons]([[docsref:/forms/validation#server-side]])
- [Navbar toggle buttons]([[docsref:/components/navbar#responsive-behaviors]]) - [Navbar toggle buttons]([[docsref:/components/navbar#responsive-behaviors]])
- [Select menus]([[docsref:/forms/select]])
Based on [community conversation](https://github.com/twbs/bootstrap/issues/25394), some options for addressing this in your own codebase include [replacing the URLs with locally hosted assets]([[docsref:/getting-started/webpack#extracting-svg-files]]), removing the images and using inline images (not possible in all components), and modifying your CSP. Our recommendation is to carefully review your own security policies and decide on the best path forward, if necessary. Based on [community conversation](https://github.com/twbs/bootstrap/issues/25394), some options for addressing this in your own codebase include [replacing the URLs with locally hosted assets]([[docsref:/getting-started/webpack#extracting-svg-files]]), removing the images and using inline images (not possible in all components), and modifying your CSP. Our recommendation is to carefully review your own security policies and decide on the best path forward, if necessary.

View File

@ -0,0 +1,117 @@
---
title: Checkbox
description: Highly customizable, native checkbox `<input>` elements for presenting toggleable options.
toc: true
---
**Trialing new components with the following rules:**
- New components are purely components, they don't assume layout of any kind.
- New options for form layout:
- Use new form groups to lay out your forms: `.control-group`, `.check-group`, `.radio-group`.
- Use helpers to lay out your forms—`.hstack` and `.vstack`—with utilities.
## Basic checkbox
All checkbox styling is contained to a wrapper class, `.check`. This does the following:
- Adds a stacking grid layout for the checkbox and custom SVG icon.
- Overrides the default `<input>` appearance with themed colors.
- Handles the toggling of separate paths in our custom SVG for the `:checked` and indeterminate states. Two `<path>`s are included in the SVG, one for each state, and the appropriate `<path>` is shown based on the `<input>`s state.
For folks looking to replace our provided icons, you'll need to add the `.checked` and `.indeterminate` classes to the `<path>`s and use them in a single `<svg>` element.
Checkbox layout and labels are handled with additional HTML and CSS.
<Example code={`<div class="check">
<input type="checkbox" id="check" checked />
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'>
<path class="checked" fill='none' stroke='currentcolor' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/>
<path class="indeterminate" fill='none' stroke='currentcolor' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/>
</svg>
</div>`} />
## Indeterminate
Checkboxes can utilize the indeterminate pseudo class when manually set via JavaScript. There is no available HTML attribute for specifying it.
<Example addStackblitzJs class="bd-example-indeterminate" code={`<div class="check">
<input type="checkbox" id="checkIndeterminate" />
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'>
<path class="checked" fill='none' stroke='currentcolor' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/>
<path class="indeterminate" fill='none' stroke='currentcolor' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/>
</svg>
</div>`} />
## Label
Wrap the `.check` in a `<b-checkgroup>` layout component and add your label. We use a custom element for layout here that sets some basic flexbox styling.
Consider margin utilities for additional spacing, and flex utilities for alignment.
<Example code={`<b-checkgroup>
<div class="check">
<input type="checkbox" id="checkLabel" />
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'>
<path class="checked" fill='none' stroke='currentcolor' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/>
<path class="indeterminate" fill='none' stroke='currentcolor' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/>
</svg>
</div>
<label for="checkLabel">Example new checkbox</label>
</b-checkgroup>`} />
## Description
With this layout approach, you can easily add a description or other content after the label. Here we use another custom element, `<b-vstack>`, to stack the label and description.
<Example code={`<b-checkgroup>
<div class="check">
<input type="checkbox" id="checkDescription" />
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'>
<path class="checked" fill='none' stroke='currentcolor' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/>
<path class="indeterminate" fill='none' stroke='currentcolor' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/>
</svg>
</div>
<b-vstack>
<label for="checkDescription">Example new checkbox</label>
<small class="description">Supporting description for the above label.</small>
</b-vstack>
</b-checkgroup>`} />
## Disabled
Add the `disabled` attribute and the associated `<label>`s are automatically styled to match with a lighter color to help indicate the inputs state.
<Example code={`<b-checkgroup>
<div class="check">
<input type="checkbox" id="checkDisabled" disabled />
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'>
<path class="checked" fill='none' stroke='currentcolor' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/>
<path class="indeterminate" fill='none' stroke='currentcolor' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/>
</svg>
</div>
<label for="checkDisabled">Example new checkbox</label>
</b-checkgroup>`} />
<Example code={`<b-checkgroup>
<div class="check">
<input type="checkbox" id="checkDisabledChecked" checked disabled />
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'>
<path class="checked" fill='none' stroke='currentcolor' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/>
<path class="indeterminate" fill='none' stroke='currentcolor' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/>
</svg>
</div>
<label for="checkDisabledChecked">Example new checkbox</label>
</b-checkgroup>`} />
## CSS
### Variables
CSS variables for the checkbox component are built on the Sass variables.
<ScssDocs name="check-css-variables" file="scss/forms/_check.scss" />
### Sass variables
<ScssDocs name="check-variables" file="scss/forms/_check.scss" />

View File

@ -1,311 +0,0 @@
---
title: Checks and radios
description: Create consistent cross-browser and cross-device checkboxes and radios with our completely rewritten checks component.
aliases: "/docs/[[config:docs_version]]/forms/checks/"
toc: true
---
## Approach
Browser default checkboxes and radios are replaced with the help of `.form-check`, a series of classes for both input types that improves the layout and behavior of their HTML elements, that provide greater customization and cross browser consistency. Checkboxes are for selecting one or several options in a list, while radios are for selecting one option from many.
Structurally, our `<input>`s and `<label>`s are sibling elements as opposed to an `<input>` within a `<label>`. This is slightly more verbose as you must specify `id` and `for` attributes to relate the `<input>` and `<label>`. We use the sibling selector (`~`) for all our `<input>` states, like `:checked` or `:disabled`. When combined with the `.form-check-label` class, we can easily style the text for each item based on the `<input>`s state.
Our checks use custom Bootstrap icons to indicate checked or indeterminate states.
## Checks
<Example code={`<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="checkDefault">
<label class="form-check-label" for="checkDefault">
Default checkbox
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="checkChecked" checked>
<label class="form-check-label" for="checkChecked">
Checked checkbox
</label>
</div>`} />
### Indeterminate
Checkboxes can utilize the `:indeterminate` pseudo class when manually set via JavaScript (there is no available HTML attribute for specifying it).
<Example addStackblitzJs class="bd-example-indeterminate" code={`<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="checkIndeterminate">
<label class="form-check-label" for="checkIndeterminate">
Indeterminate checkbox
</label>
</div>`} />
### Disabled
Add the `disabled` attribute and the associated `<label>`s are automatically styled to match with a lighter color to help indicate the inputs state.
<Example addStackblitzJs class="bd-example-indeterminate" code={`<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="checkIndeterminateDisabled" disabled>
<label class="form-check-label" for="checkIndeterminateDisabled">
Disabled indeterminate checkbox
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="checkDisabled" disabled>
<label class="form-check-label" for="checkDisabled">
Disabled checkbox
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="checkCheckedDisabled" checked disabled>
<label class="form-check-label" for="checkCheckedDisabled">
Disabled checked checkbox
</label>
</div>`} />
## Radios
<Example code={`<div class="form-check">
<input class="form-check-input" type="radio" name="radioDefault" id="radioDefault1">
<label class="form-check-label" for="radioDefault1">
Default radio
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="radioDefault" id="radioDefault2" checked>
<label class="form-check-label" for="radioDefault2">
Default checked radio
</label>
</div>`} />
### Disabled
Add the `disabled` attribute and the associated `<label>`s are automatically styled to match with a lighter color to help indicate the inputs state.
<Example code={`<div class="form-check">
<input class="form-check-input" type="radio" name="radioDisabled" id="radioDisabled" disabled>
<label class="form-check-label" for="radioDisabled">
Disabled radio
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="radioDisabled" id="radioCheckedDisabled" checked disabled>
<label class="form-check-label" for="radioCheckedDisabled">
Disabled checked radio
</label>
</div>`} />
## Switches
A switch has the markup of a custom checkbox but uses the `.form-switch` class to render a toggle switch. Consider using `role="switch"` to more accurately convey the nature of the control to assistive technologies that support this role. In older assistive technologies, it will simply be announced as a regular checkbox as a fallback. Switches also support the `disabled` attribute.
<Example code={`<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="switchCheckDefault">
<label class="form-check-label" for="switchCheckDefault">Default switch checkbox input</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="switchCheckChecked" checked>
<label class="form-check-label" for="switchCheckChecked">Checked switch checkbox input</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="switchCheckDisabled" disabled>
<label class="form-check-label" for="switchCheckDisabled">Disabled switch checkbox input</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="switchCheckCheckedDisabled" checked disabled>
<label class="form-check-label" for="switchCheckCheckedDisabled">Disabled checked switch checkbox input</label>
</div>`} />
### Native switches
Progressively enhance your switches for mobile Safari (iOS 17.4+) by adding a `switch` attribute to your input to enable haptic feedback when toggling switches, just like native iOS switches. There are no style changes attached to using this attribute in Bootstrap as all our switches use custom styles.
<Example code={`<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" value="" id="checkNativeSwitch" switch>
<label class="form-check-label" for="checkNativeSwitch">
Native switch haptics
</label>
</div>`} />
Be sure to read more about [the switch attribute on the WebKit blog](https://webkit.org/blog/15054/an-html-switch-control/). Safari 17.4+ on macOS and iOS both have native-style switches in HTML while other browsers simply fall back to the standard checkbox appearance. Applying the attribute to a non-Bootstrap checkbox in more recent versions of Safari will render a native switch.
## Default (stacked)
By default, any number of checkboxes and radios that are immediate sibling will be vertically stacked and appropriately spaced with `.form-check`.
<Example code={`<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="defaultCheck1">
<label class="form-check-label" for="defaultCheck1">
Default checkbox
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="defaultCheck2" disabled>
<label class="form-check-label" for="defaultCheck2">
Disabled checkbox
</label>
</div>`} />
<Example code={`<div class="form-check">
<input class="form-check-input" type="radio" name="exampleRadios" id="exampleRadios1" value="option1" checked>
<label class="form-check-label" for="exampleRadios1">
Default radio
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="exampleRadios" id="exampleRadios2" value="option2">
<label class="form-check-label" for="exampleRadios2">
Second default radio
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="exampleRadios" id="exampleRadios3" value="option3" disabled>
<label class="form-check-label" for="exampleRadios3">
Disabled radio
</label>
</div>`} />
## Inline
Group checkboxes or radios on the same horizontal row by adding `.form-check-inline` to any `.form-check`.
<Example code={`<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="inlineCheckbox1" value="option1">
<label class="form-check-label" for="inlineCheckbox1">1</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="inlineCheckbox2" value="option2">
<label class="form-check-label" for="inlineCheckbox2">2</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="inlineCheckbox3" value="option3" disabled>
<label class="form-check-label" for="inlineCheckbox3">3 (disabled)</label>
</div>`} />
<Example code={`<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1">
<label class="form-check-label" for="inlineRadio1">1</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio2" value="option2">
<label class="form-check-label" for="inlineRadio2">2</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio3" value="option3" disabled>
<label class="form-check-label" for="inlineRadio3">3 (disabled)</label>
</div>`} />
## Reverse
Put your checkboxes, radios, and switches on the opposite side with the `.form-check-reverse` modifier class.
<Example code={`<div class="form-check form-check-reverse">
<input class="form-check-input" type="checkbox" value="" id="reverseCheck1">
<label class="form-check-label" for="reverseCheck1">
Reverse checkbox
</label>
</div>
<div class="form-check form-check-reverse">
<input class="form-check-input" type="checkbox" value="" id="reverseCheck2" disabled>
<label class="form-check-label" for="reverseCheck2">
Disabled reverse checkbox
</label>
</div>
<div class="form-check form-switch form-check-reverse">
<input class="form-check-input" type="checkbox" id="switchCheckReverse">
<label class="form-check-label" for="switchCheckReverse">Reverse switch checkbox input</label>
</div>`} />
## Without labels
Omit the wrapping `.form-check` for checkboxes and radios that have no label text. Remember to still provide some form of accessible name for assistive technologies (for instance, using `aria-label`). See the [forms overview accessibility]([[docsref:/forms/overview/#accessibility]]) section for details.
<Example code={`<div>
<input class="form-check-input" type="checkbox" id="checkboxNoLabel" value="" aria-label="...">
</div>
<div>
<input class="form-check-input" type="radio" name="radioNoLabel" id="radioNoLabel1" value="" aria-label="...">
</div>`} />
## Toggle buttons
Create button-like checkboxes and radio buttons by using `.btn` styles rather than `.form-check-label` on the `<label>` elements. These toggle buttons can further be grouped in a [button group]([[docsref:/components/button-group]]) if needed.
### Checkbox toggle buttons
<Example code={`<input type="checkbox" class="btn-check" id="btn-check" autocomplete="off">
<label class="btn btn-primary" for="btn-check">Single toggle</label>
<input type="checkbox" class="btn-check" id="btn-check-2" checked autocomplete="off">
<label class="btn btn-primary" for="btn-check-2">Checked</label>
<input type="checkbox" class="btn-check" id="btn-check-3" autocomplete="off" disabled>
<label class="btn btn-primary" for="btn-check-3">Disabled</label>`} />
<Example code={`<input type="checkbox" class="btn-check" id="btn-check-4" autocomplete="off">
<label class="btn" for="btn-check-4">Single toggle</label>
<input type="checkbox" class="btn-check" id="btn-check-5" checked autocomplete="off">
<label class="btn" for="btn-check-5">Checked</label>
<input type="checkbox" class="btn-check" id="btn-check-6" autocomplete="off" disabled>
<label class="btn" for="btn-check-6">Disabled</label>`} />
<Callout>
Visually, these checkbox toggle buttons are identical to the [button plugin toggle buttons]([[docsref:/components/buttons#button-plugin]]). However, they are conveyed differently by assistive technologies: the checkbox toggles will be announced by screen readers as “checked“/“not checked“ (since, despite their appearance, they are fundamentally still checkboxes), whereas the button plugin toggle buttons will be announced as “button“/“button pressed“. The choice between these two approaches will depend on the type of toggle you are creating, and whether or not the toggle will make sense to users when announced as a checkbox or as an actual button.
</Callout>
### Radio toggle buttons
<Example code={`<input type="radio" class="btn-check" name="options" id="option1" autocomplete="off" checked>
<label class="btn btn-secondary" for="option1">Checked</label>
<input type="radio" class="btn-check" name="options" id="option2" autocomplete="off">
<label class="btn btn-secondary" for="option2">Radio</label>
<input type="radio" class="btn-check" name="options" id="option3" autocomplete="off" disabled>
<label class="btn btn-secondary" for="option3">Disabled</label>
<input type="radio" class="btn-check" name="options" id="option4" autocomplete="off">
<label class="btn btn-secondary" for="option4">Radio</label>`} />
<Example code={`<input type="radio" class="btn-check" name="options-base" id="option5" autocomplete="off" checked>
<label class="btn" for="option5">Checked</label>
<input type="radio" class="btn-check" name="options-base" id="option6" autocomplete="off">
<label class="btn" for="option6">Radio</label>
<input type="radio" class="btn-check" name="options-base" id="option7" autocomplete="off" disabled>
<label class="btn" for="option7">Disabled</label>
<input type="radio" class="btn-check" name="options-base" id="option8" autocomplete="off">
<label class="btn" for="option8">Radio</label>`} />
### Outlined styles
Different variants of `.btn`, such as the various outlined styles, are supported.
<Example code={`<input type="checkbox" class="btn-check" id="btn-check-outlined" autocomplete="off">
<label class="btn btn-outline-primary" for="btn-check-outlined">Single toggle</label><br>
<input type="checkbox" class="btn-check" id="btn-check-2-outlined" checked autocomplete="off">
<label class="btn btn-outline-secondary" for="btn-check-2-outlined">Checked</label><br>
<input type="radio" class="btn-check" name="options-outlined" id="success-outlined" autocomplete="off" checked>
<label class="btn btn-outline-success" for="success-outlined">Checked success radio</label>
<input type="radio" class="btn-check" name="options-outlined" id="danger-outlined" autocomplete="off">
<label class="btn btn-outline-danger" for="danger-outlined">Danger radio</label>`} />
## CSS
### Sass variables
Variables for checks:
<ScssDocs name="form-check-variables" file="scss/forms/_form-check.scss" />
Variables for switches:
<ScssDocs name="form-switch-variables" file="scss/forms/_form-check.scss" />

View File

@ -1,9 +1,13 @@
--- ---
title: Form controls title: Form controls
description: Give textual form controls like `<input>`s and `<textarea>`s an upgrade with custom styles, sizing, focus states, and more. description: Give textual `<input>`, `<textarea>`, and `<select>` elements an upgrade with custom styles, sizing, focus states, and more.
toc: true toc: true
--- ---
<Callout type="info">
**Heads up!** As of v6, `<select>` elements are now styled with the `.form-control` class. This reduces a ton of duplication and abstraction while making it easier to consistently customize the appearance of form controls.
</Callout>
## Example ## Example
Form controls are styled with a mix of Sass and CSS variables, allowing them to adapt to color modes and support any customization method. Form controls are styled with a mix of Sass and CSS variables, allowing them to adapt to color modes and support any customization method.
@ -13,17 +17,78 @@ Form controls are styled with a mix of Sass and CSS variables, allowing them to
<input type="email" class="form-control" id="exampleFormControlInput1" placeholder="name@example.com"> <input type="email" class="form-control" id="exampleFormControlInput1" placeholder="name@example.com">
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="exampleFormControlSelect1" class="form-label">Example select</label>
<select class="form-control" id="exampleFormControlSelect1">
<option>Open this select menu</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
</div>
<div>
<label for="exampleFormControlTextarea1" class="form-label">Example textarea</label> <label for="exampleFormControlTextarea1" class="form-label">Example textarea</label>
<textarea class="form-control" id="exampleFormControlTextarea1" rows="3"></textarea> <textarea class="form-control" id="exampleFormControlTextarea1" rows="3"></textarea>
</div>`} /> </div>`} />
## Sizing ## Sizing
Set heights using classes like `.form-control-lg` and `.form-control-sm`. Change the size of a form control by using classes like `.form-control-lg` and `.form-control-sm`. This adjusts `height`, `padding`, `font-size`, `line-height`, and `border-radius`.
<Example code={`<input class="form-control form-control-lg" type="text" placeholder=".form-control-lg" aria-label=".form-control-lg example"> <Example code={`<div class="mb-3">
<input class="form-control" type="text" placeholder="Default input" aria-label="default input example"> <label for="largeInput" class="form-label">Large input</label>
<input class="form-control form-control-sm" type="text" placeholder=".form-control-sm" aria-label=".form-control-sm example">`} /> <input class="form-control form-control-lg" type="text" id="largeInput" placeholder="Large input" aria-label="Large input example">
</div>
<div class="mb-3">
<label for="largeSelect" class="form-label">Large select</label>
<select class="form-control form-control-lg" id="largeSelect">
<option>Open this select menu</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
</div>
<div>
<label for="largeTextarea" class="form-label">Large textarea</label>
<textarea class="form-control form-control-lg" id="largeTextarea" rows="3" placeholder="Large textarea" aria-label="Large textarea example"></textarea>
</div>`} />
<Example code={`<div class="mb-3">
<label for="smallInput" class="form-label">Small input</label>
<input class="form-control form-control-sm" type="text" id="smallInput" placeholder="Small input" aria-label="Small input example">
</div>
<div class="mb-3">
<label for="smallSelect" class="form-label">Small select</label>
<select class="form-control form-control-sm" id="smallSelect">
<option>Open this select menu</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
</div>
<div>
<label for="smallTextarea" class="form-label">Small textarea</label>
<textarea class="form-control form-control-sm" id="smallTextarea" rows="3" placeholder="Small textarea" aria-label="Small textarea example"></textarea>
</div>`} />
## Select
The `multiple` attribute is supported on select elements:
<Example code={`<select class="form-select" multiple aria-label="Multiple select example">
<option selected>Open this select menu</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>`} />
As is the `size` attribute:
<Example code={`<select class="form-select" size="3" aria-label="Size 3 select example">
<option selected>Open this select menu</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>`} />
## Form text ## Form text
@ -62,7 +127,13 @@ Inline text can use any typical inline HTML element (be it a `<span>`, `<small>`
Add the `disabled` boolean attribute on an input to give it a grayed out appearance, remove pointer events, and prevent focusing. Add the `disabled` boolean attribute on an input to give it a grayed out appearance, remove pointer events, and prevent focusing.
<Example code={`<input class="form-control" type="text" placeholder="Disabled input" aria-label="Disabled input example" disabled> <Example code={`<input class="form-control" type="text" placeholder="Disabled input" aria-label="Disabled input example" disabled>
<input class="form-control" type="text" value="Disabled readonly input" aria-label="Disabled input example" disabled readonly>`} /> <input class="form-control" type="text" value="Disabled readonly input" aria-label="Disabled input example" disabled readonly>
<select class="form-select" aria-label="Disabled select example" disabled>
<option selected>Open this select menu</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>`} />
## Readonly ## Readonly

View File

@ -0,0 +1,56 @@
---
title: Radio
description: Highly customizable, native radio `<input>` elements for choosing one option from many.
toc: true
---
## Basic radio
Similar to checkboxes, radios are styled with the `.radio` class. However, there's no custom SVG as we use a Unicode character for the checked state.
<Example code={`<input type="radio" id="radio" class="radio" />`} />
## Label
Wrap the `.radio` in a `<b-radiogroup>` layout component and add your label. We use a custom element for layout here that sets some basic flexbox styling.
<Example code={`<b-radiogroup>
<input type="radio" id="radioLabel" class="radio" />
<label for="radioLabel">Example new radio</label>
</b-radiogroup>
`} />
## Description
With this layout approach, you can easily add a description or other content after the label. Here we use another custom element, `<b-vstack>`, to stack the label and description.
<Example code={`<b-radiogroup>
<input type="radio" id="radioLabelDescription" class="radio" />
<b-vstack>
<label for="radioLabelDescription">Example new radio</label>
<small class="description">Supporting description for the above label.</small>
</b-vstack>
</b-radiogroup>
`} />
## Disabled
Add the `disabled` attribute and the associated `<label>`s are automatically styled to match with a lighter color to help indicate the inputs state.
<Example code={`<b-radiogroup>
<input type="radio" id="radioDisabled" class="radio" disabled />
<label for="radioDisabled">Example new radio</label>
</b-radiogroup>
`} />
## CSS
### Variables
CSS variables for the radio component are built on the Sass variables.
<ScssDocs name="radio-css-variables" file="scss/forms/_radio.scss" />
### Sass variables
<ScssDocs name="radio-variables" file="scss/forms/_radio.scss" />

View File

@ -1,69 +0,0 @@
---
title: Select
description: Customize the native `<select>`s with custom CSS that changes the elements initial appearance.
toc: true
---
## Default
Custom `<select>` menus need only a custom class, `.form-select` to trigger the custom styles. Custom styles are limited to the `<select>`s initial appearance and cannot modify the `<option>`s due to browser limitations.
<Example code={`<select class="form-select" aria-label="Default select example">
<option selected>Open this select menu</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>`} />
## Sizing
You may also choose from small and large custom selects to match our similarly sized text inputs.
<Example code={`<select class="form-select form-select-lg mb-3" aria-label="Large select example">
<option selected>Open this select menu</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
<select class="form-select form-select-sm" aria-label="Small select example">
<option selected>Open this select menu</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>`} />
The `multiple` attribute is also supported:
<Example code={`<select class="form-select" multiple aria-label="Multiple select example">
<option selected>Open this select menu</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>`} />
As is the `size` attribute:
<Example code={`<select class="form-select" size="3" aria-label="Size 3 select example">
<option selected>Open this select menu</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>`} />
## Disabled
Add the `disabled` boolean attribute on a select to give it a grayed out appearance and remove pointer events.
<Example code={`<select class="form-select" aria-label="Disabled select example" disabled>
<option selected>Open this select menu</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>`} />
## CSS
### Sass variables
<ScssDocs name="form-select-variables" file="scss/forms/_form-select.scss" />

View File

@ -0,0 +1,68 @@
---
title: Switch
description: Custom toggle component built on top of native `<input>` checkbox elements.
toc: true
---
## Basic switch
Switches are built with checkboxes under the hood, so their HTML closely mirrors that of checkboxes, including optional layout components.
Switches work by layering an invisible checkbox over the custom switch indicator. This preserves interactivity while giving you a completely customizable layer underneath.
Switches also include the `switch` attribute for browsers that support it. This provides haptic feedback when toggling the switch.
<Example code={`<div class="switch">
<input type="checkbox" value="" id="switch" switch>
</div>`} />
## Label
Wrap the `.switch` in a `<b-checkgroup>` layout component and add your label. We use a custom element for layout here that sets some basic flexbox styling. Switches are checkboxes under the hood, so we reused the same custom element here.
Consider margin utilities for additional spacing, and flex utilities for alignment, especially when using small or large switches.
<Example class="d-flex flex-column gap-3" code={`<b-checkgroup>
<div class="switch switch-sm mt-1">
<input type="checkbox" value="" id="switchSmLabel" switch>
</div>
<label for="switchSmLabel">Small switch</label>
</b-checkgroup>
<b-checkgroup>
<div class="switch">
<input type="checkbox" value="" id="switchMdLabel" switch>
</div>
<label for="switchMdLabel">Default switch</label>
</b-checkgroup>`} />
## Disabled
Add the `disabled` attribute and the associated `<label>`s are automatically styled to match with a lighter color to help indicate the inputs state.
<Example class="d-flex flex-column gap-3" code={`<b-checkgroup>
<div class="switch">
<input type="checkbox" value="" id="switchDisabledLabel" switch disabled>
</div>
<label for="switchDisabledLabel">Disabled switch</label>
</b-checkgroup>`} />
## Sizes
Add a size modifier class to make your switch appear smaller or larger.
<Example class="d-flex flex-column gap-3" code={`<div class="switch switch-sm">
<input type="checkbox" value="" id="switchSizeSm" switch>
</div>
<div class="switch">
<input type="checkbox" value="" id="switchSizeMd" switch>
</div>
<div class="switch switch-lg">
<input type="checkbox" value="" id="switchSizeLg" switch>
</div>`} />
## CSS
### Variables
<ScssDocs name="switch-css-variables" file="scss/forms/_switch.scss" />

View File

@ -5,7 +5,46 @@ aliases: "/migration/"
toc: true toc: true
--- ---
## v6.0.0 ## v6.0.0 Migration
Bootstrap 6 is a major release with many breaking changes to modernize our codebase, adopt newer build tools, and improve customization. Keep reading for a guide on how to migrate from v5 to v6, and a full changelog of what's new.
1. Bump your Bootstrap dependency:
```json
{
"dependencies": {
"bootstrap": "^6.0.0"
}
}
```
2. If using all of Bootstrap's Sass files, include it in your Sass using `@use`:
```scss
@use "bootstrap/scss/bootstrap";
```
With this, you can then easily override Bootstrap's Sass variables and maps:
```scss
@use "bootstrap/scss/bootstrap" with (
$spacer: 1rem,
$enable-reduced-motion: true,
);
```
3. If using only certain parts of Bootstrap's Sass files, you can use `@use` to import them individually. Be aware that our Sass file structure has changed and you may need to adjust your imports accordingly.
```scss
@use "bootstrap/scss/forms";
```
4. Update HTML and CSS per the changelog and updates in the documentation.
5. Recompile your Sass to see the changes.
## v6.0.0 Changelog
### CSS ### CSS
@ -44,6 +83,21 @@ toc: true
- Relocated heading classes (like `.h1`) and some type classes (`.mark` and `.small`) to Reboot from `_type.scss`. This avoids a dependency in Sass modules and we like to avoid extending selectors in general. - Relocated heading classes (like `.h1`) and some type classes (`.mark` and `.small`) to Reboot from `_type.scss`. This avoids a dependency in Sass modules and we like to avoid extending selectors in general.
### Forms
- **Refactor checks, radios, and switches.**
- Split apart `_form-check.scss` into separate stylesheets: `_checkbox.scss`, `_radio.scss`, and `_switch.scss`.
- Also split apart the documentation pages for checks, radios, and switches.
- Added new CSS variables on each of these components. _Side note: we could've shared variables here, but chose not to for simplicity's sake._
- Removed several now unused Sass variables.
- Checkboxes now have a wrapping element and an SVG in the DOM for checked and indeterminate states. Radios and switches do not.
- Revamped layout for checks, radios, and switches with labels (and descriptions). We now have custom elements for layout that include basic flexbox styling.
- @mdo-do: Decide on fate of `.form-check-reverse` and `.btn-check`
- **Consolidate `.form-select` into `.form-control`.**
- Removed `.form-select`—use `.form-control` on `<select>` elements now. Too much abstraction and duplication at the same time.
- Adds new CSS variables on `.form-control` for easier customization without Sass compilation.
- `.form-control` now has a `min-height` at all times as opposed to just on `<textarea>` elements. This reduces some CSS for us.
### Helpers ### Helpers
- Ratio helpers have been moved to utilities. - Ratio helpers have been moved to utilities.