From e52a8a3dc4db5de44ad27d4a146fcc8bf56440cf Mon Sep 17 00:00:00 2001 From: Mark Otto Date: Mon, 29 Nov 2021 21:14:17 -0800 Subject: [PATCH 1/3] Convert border utilities to CSS variables - Updates the utilities mixin to check for specific CSS variable names via `css-variable` - Bonus fix: we now prevent local variables for `0` value utilities (e.g., `.border-top-0` no longer sets `--bs-border-opacity: 1` - Adds new `.border-opacity-*` classes - Adds new root variables: `--bs-border-color`, `--bs-border-style`, `--bs-border-width` - Documents the new variable changes --- scss/_maps.scss | 10 +++++ scss/_root.scss | 7 ++++ scss/_utilities.scss | 31 +++++++++++---- scss/mixins/_utilities.scss | 11 ++++-- site/content/docs/5.1/utilities/api.md | 14 ++++--- site/content/docs/5.1/utilities/borders.md | 44 ++++++++++++++++++++-- 6 files changed, 97 insertions(+), 20 deletions(-) diff --git a/scss/_maps.scss b/scss/_maps.scss index c8b9fa7e538a..2770a67615c7 100644 --- a/scss/_maps.scss +++ b/scss/_maps.scss @@ -39,6 +39,16 @@ $utilities-bg: map-merge( $utilities-bg-colors: map-loop($utilities-bg, rgba-css-var, "$key", "bg") !default; // scss-docs-end utilities-bg-colors +// scss-docs-start utilities-border-colors +$utilities-border: map-merge( + $utilities-colors, + ( + "white": to-rgb($white) + ) +) !default; +$utilities-border-colors: map-loop($utilities-border, rgba-css-var, "$key", "border") !default; +// scss-docs-end utilities-border-colors + $negative-spacers: if($enable-negative-margins, negativify-map($spacers), null) !default; $gutters: $spacers !default; diff --git a/scss/_root.scss b/scss/_root.scss index 2927c343ff23..ab0584e68a35 100644 --- a/scss/_root.scss +++ b/scss/_root.scss @@ -50,5 +50,12 @@ } --#{$variable-prefix}body-bg: #{$body-bg}; // scss-docs-end root-body-variables + + // scss-docs-start root-border-var + --#{$variable-prefix}border-width: #{$border-width}; + --#{$variable-prefix}border-style: solid; + --#{$variable-prefix}border-color: #{$border-color}; + --#{$variable-prefix}border-radius: #{$border-radius}; + // scss-docs-end root-border-var // stylelint-enable custom-property-empty-line-before } diff --git a/scss/_utilities.scss b/scss/_utilities.scss index 4d65bb88b522..7fc732acf665 100644 --- a/scss/_utilities.scss +++ b/scss/_utilities.scss @@ -98,15 +98,29 @@ $utilities: map-merge( // scss-docs-start utils-borders "border": ( property: border, + local-vars: ( + "border-opacity": 1 + ), values: ( - null: $border-width solid $border-color, + null: var(--#{$variable-prefix}border-width) var(--#{$variable-prefix}border-style) var(--#{$variable-prefix}border-color), 0: 0, ) ), + "border-opacity": ( + css-var: true, + class: border-opacity, + values: ( + 10: .1, + 25: .25, + 50: .5, + 75: .75, + 100: 1 + ) + ), "border-top": ( property: border-top, values: ( - null: $border-width solid $border-color, + null: var(--#{$variable-prefix}border-width) var(--#{$variable-prefix}border-style) var(--#{$variable-prefix}border-color), 0: 0, ) ), @@ -114,14 +128,14 @@ $utilities: map-merge( property: border-right, class: border-end, values: ( - null: $border-width solid $border-color, + null: var(--#{$variable-prefix}border-width) var(--#{$variable-prefix}border-style) var(--#{$variable-prefix}border-color), 0: 0, ) ), "border-bottom": ( property: border-bottom, values: ( - null: $border-width solid $border-color, + null: var(--#{$variable-prefix}border-width) var(--#{$variable-prefix}border-style) var(--#{$variable-prefix}border-color), 0: 0, ) ), @@ -129,17 +143,18 @@ $utilities: map-merge( property: border-left, class: border-start, values: ( - null: $border-width solid $border-color, + null: var(--#{$variable-prefix}border-width) var(--#{$variable-prefix}border-style) var(--#{$variable-prefix}border-color), 0: 0, ) ), "border-color": ( - property: border-color, + css-var: true, + css-variable-name: border-color, class: border, - values: map-merge($theme-colors, ("white": $white)) + values: $utilities-border-colors ), "border-width": ( - property: border-width, + css-var: true, class: border, values: $border-widths ), diff --git a/scss/mixins/_utilities.scss b/scss/mixins/_utilities.scss index e871b4233671..b2c6683d7de9 100644 --- a/scss/mixins/_utilities.scss +++ b/scss/mixins/_utilities.scss @@ -20,6 +20,9 @@ $property-class: if(map-has-key($utility, class), map-get($utility, class), nth($properties, 1)); $property-class: if($property-class == null, "", $property-class); + // Use custom CSS variable name if present, otherwise default to `class` + $css-variable-name: if(map-has-key($utility, css-variable-name), map-get($utility, css-variable-name), map-get($utility, class)); + // State params to generate pseudo-classes $state: if(map-has-key($utility, state), map-get($utility, state), ()); @@ -52,20 +55,20 @@ @if $is-css-var { .#{$property-class + $infix + $property-class-modifier} { - --#{$variable-prefix}#{$property-class}: #{$value}; + --#{$variable-prefix}#{$css-variable-name}: #{$value}; } @each $pseudo in $state { .#{$property-class + $infix + $property-class-modifier}-#{$pseudo}:#{$pseudo} { - --#{$variable-prefix}#{$property-class}: #{$value}; + --#{$variable-prefix}#{$css-variable-name}: #{$value}; } } } @else { .#{$property-class + $infix + $property-class-modifier} { @each $property in $properties { @if $is-local-vars { - @each $local-var, $value in $is-local-vars { - --#{$variable-prefix}#{$local-var}: #{$value}; + @each $local-var, $variable in $is-local-vars { + --#{$variable-prefix}#{$local-var}: #{$variable}; } } #{$property}: $value if($enable-important-utilities, !important, null); diff --git a/site/content/docs/5.1/utilities/api.md b/site/content/docs/5.1/utilities/api.md index f961a5da9b6a..16270ad5112c 100644 --- a/site/content/docs/5.1/utilities/api.md +++ b/site/content/docs/5.1/utilities/api.md @@ -18,6 +18,7 @@ The `$utilities` map contains all our utilities and is later merged with your cu | [`values`](#values) | **Required** | – | List of values, or a map if you don't want the class name to be the same as the value. If `null` is used as map key, `class` is not prepended to the class name. | | [`class`](#class) | Optional | null | Name of the generated class. If not provided and `property` is an array of strings, `class` will default to the first element of the `property` array. If not provided and `property` is a string, the `values` keys are used for the `class` names. | | [`css-var`](#css-variable-utilities) | Optional | `false` | Boolean to generate CSS variables instead of CSS rules. | +| [`css-variable-name`](#css-variable-utilities) | Optional | null | Custom un-prefixed name for the CSS variable inside the ruleset. | | [`local-vars`](#local-css-variables) | Optional | null | Map of local CSS variables to generate in addition to the CSS rules. | | [`state`](#states) | Optional | null | List of pseudo-class variants (e.g., `:hover` or `:focus`) to generate. | | [`responsive`](#responsive) | Optional | `false` | Boolean indicating if responsive classes should be generated. | @@ -158,12 +159,15 @@ Output: ### CSS variable utilities -Set the `css-var` boolean option to `true` and the API will generate local CSS variables for the given selector instead of the usual `property: value` rules. Consider our `.text-opacity-*` utilities: +Set the `css-var` boolean option to `true` and the API will generate local CSS variables for the given selector instead of the usual `property: value` rules. Add an optional `css-variable-name` to set a different CSS variable name than the class name. + +Consider our `.text-opacity-*` utilities. If we add the `css-variable-name` option, we'll get a custom output. ```scss $utilities: ( "text-opacity": ( css-var: true, + css-variable-name: text-alpha, class: text-opacity, values: ( 25: .25, @@ -178,10 +182,10 @@ $utilities: ( Output: ```css -.text-opacity-25 { --bs-text-opacity: .25; } -.text-opacity-50 { --bs-text-opacity: .5; } -.text-opacity-75 { --bs-text-opacity: .75; } -.text-opacity-100 { --bs-text-opacity: 1; } +.text-opacity-25 { --bs-text-alpha: .25; } +.text-opacity-50 { --bs-text-alpha: .5; } +.text-opacity-75 { --bs-text-alpha: .75; } +.text-opacity-100 { --bs-text-alpha: 1; } ``` ### Local CSS variables diff --git a/site/content/docs/5.1/utilities/borders.md b/site/content/docs/5.1/utilities/borders.md index 6ba1174c9d51..e76c461a3ac2 100644 --- a/site/content/docs/5.1/utilities/borders.md +++ b/site/content/docs/5.1/utilities/borders.md @@ -30,7 +30,7 @@ Use border utilities to add or remove an element's borders. Choose from all bord {{< /example >}} -## Border color +## Color Change the border color using utilities built on our theme colors. @@ -43,7 +43,45 @@ Change the border color using utilities built on our theme colors. {{< /example >}} -## Border-width +## Opacity + +Added in v5.2.0 + +Bootstrap `border-{color}` utilities are generated with Sass using CSS variables. This allows for real-time color changes without compilation and dynamic alpha transparency changes. + +### How it works + +Consider our default `.border-success` utility. + +```css +.border-success { + --bs-border-opacity: 1; + border-color: rgba(var(--bs-success-rgb), var(--bs-border-opacity)) !important; +} +``` + +We use an RGB version of our `--bs-success` (with the value of `25, 135, 84`) CSS variable and attached a second CSS variable, `--bs-border-opacity`, for the alpha transparency (with a default value `1` thanks to a local CSS variable). That means anytime you use `.border-success` now, your computed `color` value is `rgba(25, 135, 84, 1)`. The local CSS variable inside each `.border-*` class avoids inheritance issues so nested instances of the utilities don't automatically have a modified alpha transparency. + +### Example + +To change that opacity, override `--bs-border-opacity` via custom styles or inline styles. + +{{< example >}} +
This is default success border
+
This is 50% opacity success border
+{{< /example >}} + +Or, choose from any of the `.border-opacity` utilities: + +{{< example >}} +
This is default success border
+
This is 75% opacity success border
+
This is 50% opacity success border
+
This is 25% opacity success border
+
This is 10% opacity success border
+{{< /example >}} + +## Width {{< example class="bd-example-border-utils" >}} @@ -53,7 +91,7 @@ Change the border color using utilities built on our theme colors. {{< /example >}} -## Border-radius +## Radius Add classes to an element to easily round its corners. From 9950fa553350d874a2a914a6da1c5ec69160203c Mon Sep 17 00:00:00 2001 From: Mark Otto Date: Mon, 29 Nov 2021 22:07:28 -0800 Subject: [PATCH 2/3] bump bundlewatch --- .bundlewatch.config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.bundlewatch.config.json b/.bundlewatch.config.json index 382e162ae0ce..70652c6939be 100644 --- a/.bundlewatch.config.json +++ b/.bundlewatch.config.json @@ -22,7 +22,7 @@ }, { "path": "./dist/css/bootstrap-utilities.min.css", - "maxSize": "7 kB" + "maxSize": "7.0 kB" }, { "path": "./dist/css/bootstrap.css", From 7b2d7f396509103683539a19d110b0401baa042e Mon Sep 17 00:00:00 2001 From: Mark Otto Date: Fri, 25 Feb 2022 14:55:47 -0800 Subject: [PATCH 3/3] Replace instances of Sass vars for CSS variable versions of border-color and border-width --- scss/_accordion.scss | 2 +- scss/_navbar.scss | 2 +- scss/_variables.scss | 41 ++++++++++++----------- site/assets/scss/_component-examples.scss | 4 +-- 4 files changed, 25 insertions(+), 24 deletions(-) diff --git a/scss/_accordion.scss b/scss/_accordion.scss index 33a22f441374..53f1c0ccfc33 100644 --- a/scss/_accordion.scss +++ b/scss/_accordion.scss @@ -20,7 +20,7 @@ &:not(.collapsed) { color: $accordion-button-active-color; background-color: $accordion-button-active-bg; - box-shadow: inset 0 ($accordion-border-width * -1) 0 $accordion-border-color; + box-shadow: inset 0 calc($accordion-border-width * -1) 0 $accordion-border-color; // stylelint-disable-line function-disallowed-list &::after { background-image: escape-svg($accordion-button-active-icon); diff --git a/scss/_navbar.scss b/scss/_navbar.scss index f015d332c970..e2a42285ef43 100644 --- a/scss/_navbar.scss +++ b/scss/_navbar.scss @@ -156,7 +156,7 @@ line-height: 1; color: var(--#{$variable-prefix}navbar-color); background-color: transparent; // remove default button style - border: $border-width solid var(--#{$variable-prefix}navbar-toggler-border-color); // remove default button style + border: var(--#{$variable-prefix}border-width) solid var(--#{$variable-prefix}navbar-toggler-border-color); // remove default button style @include border-radius(var(--#{$variable-prefix}navbar-toggler-border-radius)); @include transition(var(--#{$variable-prefix}navbar-toggler-transition)); diff --git a/scss/_variables.scss b/scss/_variables.scss index 56a81be7fd42..ce41aa945b51 100644 --- a/scss/_variables.scss +++ b/scss/_variables.scss @@ -620,13 +620,14 @@ $blockquote-footer-font-size: $small-font-size !default; $hr-margin-y: $spacer !default; $hr-color: inherit !default; + // fusv-disable -$hr-bg-color: null !default; // Deprecated in v5.2.0 -$hr-height: null !default; // Deprecated in v5.2.0 +$hr-bg-color: null !default; // Deprecated in v5.2.0 +$hr-height: null !default; // Deprecated in v5.2.0 // fusv-enable -$hr-border-color: null !default; // Allows for inherited colors -$hr-border-width: $border-width !default; +$hr-border-color: null !default; // Allows for inherited colors +$hr-border-width: var(--#{$variable-prefix}border-width) !default; $hr-opacity: .25 !default; $legend-margin-bottom: .5rem !default; @@ -1058,8 +1059,8 @@ $nav-link-transition: color .15s ease-in-out, background-color .15 $nav-link-disabled-color: $gray-600 !default; $nav-tabs-border-color: $gray-300 !default; -$nav-tabs-border-width: $border-width !default; $nav-tabs-border-radius: $border-radius !default; +$nav-tabs-border-width: var(--#{$variable-prefix}border-width) !default; $nav-tabs-link-hover-border-color: $gray-200 $gray-200 $nav-tabs-border-color !default; $nav-tabs-link-active-color: $gray-700 !default; $nav-tabs-link-active-bg: $body-bg !default; @@ -1130,7 +1131,7 @@ $dropdown-color: $body-color !default; $dropdown-bg: $white !default; $dropdown-border-color: rgba($black, .15) !default; $dropdown-border-radius: $border-radius !default; -$dropdown-border-width: $border-width !default; +$dropdown-border-width: var(--#{$variable-prefix}border-width) !default; $dropdown-inner-border-radius: subtract($dropdown-border-radius, $dropdown-border-width) !default; $dropdown-divider-bg: $dropdown-border-color !default; $dropdown-divider-margin-y: $spacer * .5 !default; @@ -1180,8 +1181,8 @@ $pagination-padding-x-lg: 1.5rem !default; $pagination-color: $link-color !default; $pagination-bg: $white !default; -$pagination-border-width: $border-width !default; $pagination-border-radius: $border-radius !default; +$pagination-border-width: var(--#{$variable-prefix}border-width) !default; $pagination-margin-start: -$pagination-border-width !default; $pagination-border-color: $gray-300 !default; @@ -1222,9 +1223,9 @@ $placeholder-opacity-min: .2 !default; $card-spacer-y: $spacer !default; $card-spacer-x: $spacer !default; $card-title-spacer-y: $spacer * .5 !default; -$card-border-width: $border-width !default; -$card-border-color: rgba($black, .125) !default; $card-border-radius: $border-radius !default; +$card-border-width: var(--#{$variable-prefix}border-width) !default; +$card-border-color: var(--#{$variable-prefix}border-color) !default; $card-box-shadow: null !default; $card-inner-border-radius: subtract($card-border-radius, $card-border-width) !default; $card-cap-padding-y: $card-spacer-y * .5 !default; @@ -1245,9 +1246,9 @@ $accordion-padding-y: 1rem !default; $accordion-padding-x: 1.25rem !default; $accordion-color: $body-color !default; $accordion-bg: $body-bg !default; -$accordion-border-width: $border-width !default; -$accordion-border-color: rgba($black, .125) !default; $accordion-border-radius: $border-radius !default; +$accordion-border-width: var(--#{$variable-prefix}border-width) !default; +$accordion-border-color: var(--#{$variable-prefix}border-color) !default; $accordion-inner-border-radius: subtract($accordion-border-radius, $accordion-border-width) !default; $accordion-body-padding-y: $accordion-padding-y !default; @@ -1309,7 +1310,7 @@ $form-feedback-tooltip-border-radius: $tooltip-border-radius !default; $popover-font-size: $font-size-sm !default; $popover-bg: $white !default; $popover-max-width: 276px !default; -$popover-border-width: $border-width !default; +$popover-border-width: var(--#{$variable-prefix}border-width) !default; $popover-border-color: rgba($black, .2) !default; $popover-border-radius: $border-radius-lg !default; $popover-inner-border-radius: subtract($popover-border-radius, $popover-border-width) !default; @@ -1341,9 +1342,9 @@ $toast-padding-y: .5rem !default; $toast-font-size: .875rem !default; $toast-color: null !default; $toast-background-color: rgba($white, .85) !default; -$toast-border-width: 1px !default; -$toast-border-color: rgba($black, .1) !default; $toast-border-radius: $border-radius !default; +$toast-border-width: var(--#{$variable-prefix}border-width) !default; +$toast-border-color: var(--#{$variable-prefix}border-color) !default; $toast-box-shadow: $box-shadow !default; $toast-spacing: $container-padding-x !default; @@ -1379,16 +1380,16 @@ $modal-title-line-height: $line-height-base !default; $modal-content-color: null !default; $modal-content-bg: $white !default; -$modal-content-border-color: rgba($black, .2) !default; -$modal-content-border-width: $border-width !default; $modal-content-border-radius: $border-radius-lg !default; +$modal-content-border-color: var(--#{$variable-prefix}border-color) !default; +$modal-content-border-width: var(--#{$variable-prefix}border-width) !default; $modal-content-inner-border-radius: subtract($modal-content-border-radius, $modal-content-border-width) !default; $modal-content-box-shadow-xs: $box-shadow-sm !default; $modal-content-box-shadow-sm-up: $box-shadow !default; $modal-backdrop-bg: $black !default; $modal-backdrop-opacity: .5 !default; -$modal-header-border-color: $border-color !default; +$modal-header-border-color: var(--#{$variable-prefix}border-color) !default; $modal-footer-border-color: $modal-header-border-color !default; $modal-header-border-width: $modal-content-border-width !default; $modal-footer-border-width: $modal-header-border-width !default; @@ -1448,8 +1449,8 @@ $progress-bar-transition: width .6s ease !default; $list-group-color: $gray-900 !default; $list-group-bg: $white !default; $list-group-border-color: rgba($black, .125) !default; -$list-group-border-width: $border-width !default; $list-group-border-radius: $border-radius !default; +$list-group-border-width: var(--#{$variable-prefix}border-width) !default; $list-group-item-padding-y: $spacer * .5 !default; $list-group-item-padding-x: $spacer !default; @@ -1477,9 +1478,9 @@ $list-group-action-active-bg: $gray-200 !default; // scss-docs-start thumbnail-variables $thumbnail-padding: .25rem !default; $thumbnail-bg: $body-bg !default; -$thumbnail-border-width: $border-width !default; -$thumbnail-border-color: $gray-300 !default; $thumbnail-border-radius: $border-radius !default; +$thumbnail-border-width: var(--#{$variable-prefix}border-width) !default; +$thumbnail-border-color: var(--#{$variable-prefix}border-color) !default; $thumbnail-box-shadow: $box-shadow-sm !default; // scss-docs-end thumbnail-variables diff --git a/site/assets/scss/_component-examples.scss b/site/assets/scss/_component-examples.scss index 4bc44ad2f008..1d860b2c2c33 100644 --- a/site/assets/scss/_component-examples.scss +++ b/site/assets/scss/_component-examples.scss @@ -194,7 +194,7 @@ width: 10rem; color: $gray-600; background-color: $gray-100; - border: $border-width solid $border-color; + border: var(--#{$variable-prefix}border-width) solid var(--#{$variable-prefix}border-color); > div { display: flex; @@ -265,7 +265,7 @@ .bd-example-border-utils-0 { [class^="border"] { - border: 1px solid $border-color; + border: var(--#{$variable-prefix}border-width) solid var(--#{$variable-prefix}border-color); } }