]> git.ipfire.org Git - thirdparty/bootstrap.git/commitdiff
Focus ring helper and utilities (#33125)
authorMark Otto <markd.otto@gmail.com>
Thu, 29 Dec 2022 22:19:22 +0000 (14:19 -0800)
committerGitHub <noreply@github.com>
Thu, 29 Dec 2022 22:19:22 +0000 (14:19 -0800)
* Add global variables for box-shadow focus rings

* Update instances of -btn-focus-box-shadow to use -ring-box-shadow, unless it's for buttons or inputs

* fix variable name

* Add CSS variables for global focus styling, document it

* Move to CSS vars section

* Update scss/_nav.scss

Co-authored-by: Gaël Poupard <ffoodd@users.noreply.github.com>
* Helper and utils

* Fix bundlewatch

* Change 'Focus ring' in sidebar so that the page can be visible

* Minor typo fix

* fix merge

* Revamp some more, improve docs

Co-authored-by: Gaël Poupard <ffoodd@users.noreply.github.com>
Co-authored-by: Julien Déramond <juderamond@gmail.com>
Co-authored-by: Patrick H. Lauke <redux@splintered.co.uk>
.bundlewatch.config.json
scss/_helpers.scss
scss/_nav.scss
scss/_root.scss
scss/_utilities.scss
scss/_variables.scss
scss/helpers/_focus-ring.scss [new file with mode: 0644]
site/assets/scss/_component-examples.scss
site/content/docs/5.3/customize/css-variables.md
site/content/docs/5.3/helpers/focus-ring.md [new file with mode: 0644]
site/data/sidebar.yml

index 23a003679e5a6a4dca6225e17b9d3393fd587fc3..94396f15c7fc91cc0f55e5bd1e008e729c9cf1c4 100644 (file)
     },
     {
       "path": "./dist/css/bootstrap-utilities.css",
-      "maxSize": "10.5 kB"
+      "maxSize": "10.75 kB"
     },
     {
       "path": "./dist/css/bootstrap-utilities.min.css",
-      "maxSize": "9.75 kB"
+      "maxSize": "10.0 kB"
     },
     {
       "path": "./dist/css/bootstrap.css",
@@ -30,7 +30,7 @@
     },
     {
       "path": "./dist/css/bootstrap.min.css",
-      "maxSize": "29.25 kB"
+      "maxSize": "29.5 kB"
     },
     {
       "path": "./dist/js/bootstrap.bundle.js",
index 644b693fbc9195622b1e56129c8b5e03d19b0e4c..6126781cde7ecc5cd4d8196cf6418fe712b1c373 100644 (file)
@@ -1,6 +1,7 @@
 @import "helpers/clearfix";
 @import "helpers/color-bg";
 @import "helpers/colored-links";
+@import "helpers/focus-ring";
 @import "helpers/ratio";
 @import "helpers/position";
 @import "helpers/stacks";
index 1b3cf082f0bbe10797462142cd9ab84b4d3e3204..3a27ee79256a87c9232bc2d1e0f0753c0cbf66f9 100644 (file)
     text-decoration: if($link-hover-decoration == underline, none, null);
   }
 
+  &:focus {
+    outline: 0;
+    box-shadow: $nav-link-focus-box-shadow;
+  }
+
   // Disabled state lightens text
   &.disabled {
     color: var(--#{$prefix}nav-link-disabled-color);
index 45de310906e8421d97c2e8e35c14090a4effb8cd..323a5217bfe6165a13cda47009167aa60ac0bc94 100644 (file)
   @each $name, $value in $grid-breakpoints {
     --#{$prefix}breakpoint-#{$name}: #{$value};
   }
+
+  // Focus styles
+  // scss-docs-start root-focus-variables
+  --#{$prefix}focus-ring-width: #{$focus-ring-width};
+  --#{$prefix}focus-ring-opacity: #{$focus-ring-opacity};
+  --#{$prefix}focus-ring-color: #{$focus-ring-color};
+  // By default, there is no `--bs-focus-ring-x`, `--bs-focus-ring-y`, or `--bs-focus-ring-blur`, but we provide CSS variables with fallbacks to initial `0` values
+  --#{$prefix}focus-ring-box-shadow: var(--#{$prefix}focus-ring-x, 0) var(--#{$prefix}focus-ring-y, 0) var(--#{$prefix}focus-ring-blur, 0) var(--#{$prefix}focus-ring-width) var(--#{$prefix}focus-ring-color);
+  // scss-docs-end root-focus-variables
 }
 
 @if $enable-dark-mode {
index 4c2c39dd7610a55c200323f7d50ad8d175fe241d..5134688e90bfd647a904fa7dfc9498ca07819695 100644 (file)
@@ -84,6 +84,14 @@ $utilities: map-merge(
       )
     ),
     // scss-docs-end utils-shadow
+    // scss-docs-start utils-focus-ring
+    "focus-ring": (
+      css-var: true,
+      css-variable-name: focus-ring-color,
+      class: focus-ring,
+      values: map-loop($theme-colors-rgb, rgba-css-var, "$key", "focus-ring")
+    ),
+    // scss-docs-end utils-focus-ring
     // scss-docs-start utils-position
     "position": (
       property: position,
index 24a112a12a8380d72807ed4219d11f43630984f8..c2201401578de9b53748546c1761e14e9cc2a20f 100644 (file)
@@ -549,6 +549,14 @@ $box-shadow-inset:            inset 0 1px 2px rgba(var(--#{$prefix}body-color-rg
 $component-active-color:      $white !default;
 $component-active-bg:         $primary !default;
 
+// scss-docs-start focus-ring-variables
+$focus-ring-width:      .25rem !default;
+$focus-ring-opacity:    .25 !default;
+$focus-ring-color:      rgba($primary, $focus-ring-opacity) !default;
+$focus-ring-blur:       0 !default;
+$focus-ring-box-shadow: 0 0 $focus-ring-blur $focus-ring-width $focus-ring-color !default;
+// scss-docs-end focus-ring-variables
+
 // scss-docs-start caret-variables
 $caret-width:                 .3em !default;
 $caret-vertical-align:        $caret-width * .85 !default;
@@ -761,11 +769,11 @@ $input-btn-font-family:       null !default;
 $input-btn-font-size:         $font-size-base !default;
 $input-btn-line-height:       $line-height-base !default;
 
-$input-btn-focus-width:         .25rem !default;
-$input-btn-focus-color-opacity: .25 !default;
-$input-btn-focus-color:         rgba($component-active-bg, $input-btn-focus-color-opacity) !default;
-$input-btn-focus-blur:          0 !default;
-$input-btn-focus-box-shadow:    0 0 $input-btn-focus-blur $input-btn-focus-width $input-btn-focus-color !default;
+$input-btn-focus-width:         $focus-ring-width !default;
+$input-btn-focus-color-opacity: $focus-ring-opacity !default;
+$input-btn-focus-color:         $focus-ring-color !default;
+$input-btn-focus-blur:          $focus-ring-blur !default;
+$input-btn-focus-box-shadow:    $focus-ring-box-shadow !default;
 
 $input-btn-padding-y-sm:      .25rem !default;
 $input-btn-padding-x-sm:      .5rem !default;
@@ -918,7 +926,7 @@ $form-check-input-border:                 var(--#{$prefix}border-width) solid va
 $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:       $input-btn-focus-box-shadow !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;
@@ -1124,6 +1132,8 @@ $nav-link-color:                    var(--#{$prefix}link-color) !default;
 $nav-link-hover-color:              var(--#{$prefix}link-hover-color) !default;
 $nav-link-transition:               color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out !default;
 $nav-link-disabled-color:           var(--#{$prefix}secondary-color) !default;
+$nav-link-disabled-color:           $gray-600 !default;
+$nav-link-focus-box-shadow:         $focus-ring-box-shadow !default;
 
 $nav-tabs-border-color:             var(--#{$prefix}border-color) !default;
 $nav-tabs-border-width:             var(--#{$prefix}border-width) !default;
@@ -1265,7 +1275,7 @@ $pagination-border-color:           var(--#{$prefix}border-color) !default;
 
 $pagination-focus-color:            var(--#{$prefix}link-hover-color) !default;
 $pagination-focus-bg:               var(--#{$prefix}secondary-bg) !default;
-$pagination-focus-box-shadow:       $input-btn-focus-box-shadow !default;
+$pagination-focus-box-shadow:       $focus-ring-box-shadow !default;
 $pagination-focus-outline:          0 !default;
 
 $pagination-hover-color:            var(--#{$prefix}link-hover-color) !default;
@@ -1665,8 +1675,9 @@ $btn-close-height:           $btn-close-width !default;
 $btn-close-padding-x:        .25em !default;
 $btn-close-padding-y:        $btn-close-padding-x !default;
 $btn-close-color:            $black !default;
-$btn-close-bg:               url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#{$btn-close-color}'><path d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/></svg>") !default;
 $btn-close-focus-shadow:     $input-btn-focus-box-shadow !default;
+$btn-close-bg:               url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#{$btn-close-color}'><path d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/></svg>") !default;
+$btn-close-focus-shadow:     $focus-ring-box-shadow !default;
 $btn-close-opacity:          .5 !default;
 $btn-close-hover-opacity:    .75 !default;
 $btn-close-focus-opacity:    1 !default;
diff --git a/scss/helpers/_focus-ring.scss b/scss/helpers/_focus-ring.scss
new file mode 100644 (file)
index 0000000..26508a8
--- /dev/null
@@ -0,0 +1,5 @@
+.focus-ring:focus {
+  outline: 0;
+  // By default, there is no `--bs-focus-ring-x`, `--bs-focus-ring-y`, or `--bs-focus-ring-blur`, but we provide CSS variables with fallbacks to initial `0` values
+  box-shadow: var(--#{$prefix}focus-ring-x, 0) var(--#{$prefix}focus-ring-y, 0) var(--#{$prefix}focus-ring-blur, 0) var(--#{$prefix}focus-ring-width) var(--#{$prefix}focus-ring-color);
+}
index 11800a638f09aa273c787f772402f0c03b324441..99bc460c1212106168e03ae8dd5f15236a9369f9 100644 (file)
     margin-right: 0;
   }
 }
+
+.focused {
+  outline: 0;
+  box-shadow: var(--#{$variable-prefix}focus-ring-offset), var(--#{$variable-prefix}focus-ring-x, 0) var(--#{$variable-prefix}focus-ring-y, 0) var(--#{$variable-prefix}focus-ring-blur) var(--#{$variable-prefix}focus-ring-width) var(--#{$variable-prefix}focus-ring-color);
+}
index 838a1abf7aeddf72cf1abf901725bfb022c66ebd..ffb40c0c4a4a014c6dc33fb888199b068a7c467a 100644 (file)
@@ -74,6 +74,20 @@ a {
 }
 ```
 
+## Focus variables
+
+{{< added-in "5.3.0" >}}
+
+Bootstrap provides custom `:focus` styles using a combination of Sass and CSS variables that can be optionally added to specific components and elements. We do not yet globally override all `:focus` styles.
+
+In our Sass, we set default values that can be customized before compiling.
+
+{{< scss-docs name="focus-ring-variables" file="scss/_variables.scss" >}}
+
+Those variables are then reassigned to `:root` level CSS variables that can be customized in real-time, including with options for `x` and `y` offsets (which default to their fallback value of `0`).
+
+{{< scss-docs name="root-focus-variables" file="scss/_root.scss" >}}
+
 ## Grid breakpoints
 
 While we include our grid breakpoints as CSS variables (except for `xs`), be aware that **CSS variables do not work in media queries**. This is by design in the CSS spec for variables, but may change in coming years with support for `env()` variables. Check out [this Stack Overflow answer](https://stackoverflow.com/a/47212942) for some helpful links. In the meantime, you can use these variables in other CSS situations, as well as in your JavaScript.
diff --git a/site/content/docs/5.3/helpers/focus-ring.md b/site/content/docs/5.3/helpers/focus-ring.md
new file mode 100644 (file)
index 0000000..5819bf1
--- /dev/null
@@ -0,0 +1,60 @@
+---
+layout: docs
+title: Focus ring
+description: Utility classes that allows you to add and modify custom focus ring styles to elements and components.
+group: helpers
+toc: true
+added: "5.3"
+---
+
+The `.focus-ring` helper removes the default `outline` on `:focus`, replacing it with a `box-shadow` that can be more broadly customized. The new shadow is made up of a series of CSS variables, inherited from the `:root` level, that can be modified for any element or component.
+
+## Example
+
+Click into the example below and press <kbd>Tab</kbd> to see the focus ring in action.
+
+{{< example >}}
+<a href="#" class="d-inline-flex focus-ring py-1 px-2 text-decoration-none border rounded-2">
+  Custom focus ring
+</a>
+{{< /example >}}
+
+## Customize
+
+Modify the styling of a focus ring with our CSS variables, Sass variables, utilities, or custom styles.
+
+### CSS variables
+
+Modify the `--bs-focus-ring-*` CSS variables as needed to change the default appearance.
+
+{{< example >}}
+<a href="#" class="d-inline-flex focus-ring py-1 px-2 text-decoration-none border rounded-2" style="--bs-focus-ring-color: rgba(var(--bs-success-rgb), .25)">
+  Green focus ring
+</a>
+{{< /example >}}
+
+`.focus-ring` sets styles via global CSS variables that can be overridden on any parent element, as shown above. These variables are generated from their Sass variable counterparts.
+
+{{< scss-docs name="root-focus-variables" file="scss/_root.scss" >}}
+
+### Sass
+
+Customize the focus ring Sass variables to modify all usage of the focus ring styles across your Bootstrap-powered project.
+
+{{< scss-docs name="focus-ring-variables" file="scss/_variables.scss" >}}
+
+### Utilities
+
+In addition to `.focus-ring`, we have several `.focus-ring-*` utilities to modify the helper class defaults. Modify the color with any of our [theme colors]({{< docsref "/customize/color#theme-colors" >}}). Note that the light and dark variants may not be visible on all background colors given current color mode support.
+
+{{< example >}}
+{{< focus-ring.inline >}}
+{{- range (index $.Site.Data "theme-colors") }}
+<p><a href="#" class="d-inline-flex focus-ring focus-ring-{{ .name }} py-1 px-2 text-decoration-none border rounded-2">{{ title .name }} focus</a></p>
+{{- end -}}
+{{< /focus-ring.inline >}}
+{{< /example >}}
+
+Focus ring utilities are declared in our utilities API in `scss/_utilities.scss`. [Learn how to use the utilities API.]({{< docsref "/utilities/api#using-the-api" >}})
+
+{{< scss-docs name="utils-focus-ring" file="scss/_utilities.scss" >}}
index 4199fa535e0a5ecf1e86b22da68d9c8082701349..b1d567e9906e06647453d49768f3a86cfef41d36 100644 (file)
     - title: Clearfix
     - title: Color & background
     - title: Colored links
+    - title: Focus ring
     - title: Position
     - title: Ratio
     - title: Stacks