0
0
mirror of https://github.com/twbs/bootstrap.git synced 2024-12-01 13:24:25 +01:00

Focus ring helper and utilities (#33125)

* 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>
This commit is contained in:
Mark Otto 2022-12-29 14:19:22 -08:00 committed by GitHub
parent 1a043b55bc
commit 9e17b2b34c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 130 additions and 11 deletions

View File

@ -18,11 +18,11 @@
}, },
{ {
"path": "./dist/css/bootstrap-utilities.css", "path": "./dist/css/bootstrap-utilities.css",
"maxSize": "10.5 kB" "maxSize": "10.75 kB"
}, },
{ {
"path": "./dist/css/bootstrap-utilities.min.css", "path": "./dist/css/bootstrap-utilities.min.css",
"maxSize": "9.75 kB" "maxSize": "10.0 kB"
}, },
{ {
"path": "./dist/css/bootstrap.css", "path": "./dist/css/bootstrap.css",
@ -30,7 +30,7 @@
}, },
{ {
"path": "./dist/css/bootstrap.min.css", "path": "./dist/css/bootstrap.min.css",
"maxSize": "29.25 kB" "maxSize": "29.5 kB"
}, },
{ {
"path": "./dist/js/bootstrap.bundle.js", "path": "./dist/js/bootstrap.bundle.js",

View File

@ -1,6 +1,7 @@
@import "helpers/clearfix"; @import "helpers/clearfix";
@import "helpers/color-bg"; @import "helpers/color-bg";
@import "helpers/colored-links"; @import "helpers/colored-links";
@import "helpers/focus-ring";
@import "helpers/ratio"; @import "helpers/ratio";
@import "helpers/position"; @import "helpers/position";
@import "helpers/stacks"; @import "helpers/stacks";

View File

@ -38,6 +38,11 @@
text-decoration: if($link-hover-decoration == underline, none, null); text-decoration: if($link-hover-decoration == underline, none, null);
} }
&:focus {
outline: 0;
box-shadow: $nav-link-focus-box-shadow;
}
// Disabled state lightens text // Disabled state lightens text
&.disabled { &.disabled {
color: var(--#{$prefix}nav-link-disabled-color); color: var(--#{$prefix}nav-link-disabled-color);

View File

@ -127,6 +127,15 @@
@each $name, $value in $grid-breakpoints { @each $name, $value in $grid-breakpoints {
--#{$prefix}breakpoint-#{$name}: #{$value}; --#{$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 { @if $enable-dark-mode {

View File

@ -84,6 +84,14 @@ $utilities: map-merge(
) )
), ),
// scss-docs-end utils-shadow // 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 // scss-docs-start utils-position
"position": ( "position": (
property: position, property: position,

View 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-color: $white !default;
$component-active-bg: $primary !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 // scss-docs-start caret-variables
$caret-width: .3em !default; $caret-width: .3em !default;
$caret-vertical-align: $caret-width * .85 !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-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: .25rem !default; $input-btn-focus-width: $focus-ring-width !default;
$input-btn-focus-color-opacity: .25 !default; $input-btn-focus-color-opacity: $focus-ring-opacity !default;
$input-btn-focus-color: rgba($component-active-bg, $input-btn-focus-color-opacity) !default; $input-btn-focus-color: $focus-ring-color !default;
$input-btn-focus-blur: 0 !default; $input-btn-focus-blur: $focus-ring-blur !default;
$input-btn-focus-box-shadow: 0 0 $input-btn-focus-blur $input-btn-focus-width $input-btn-focus-color !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;
@ -918,7 +926,7 @@ $form-check-input-border: var(--#{$prefix}border-width) solid va
$form-check-input-border-radius: .25em !default; $form-check-input-border-radius: .25em !default;
$form-check-radio-border-radius: 50% !default; $form-check-radio-border-radius: 50% !default;
$form-check-input-focus-border: $input-focus-border-color !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-color: $component-active-color !default;
$form-check-input-checked-bg-color: $component-active-bg !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-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-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: 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-color: var(--#{$prefix}border-color) !default;
$nav-tabs-border-width: var(--#{$prefix}border-width) !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-color: var(--#{$prefix}link-hover-color) !default;
$pagination-focus-bg: var(--#{$prefix}secondary-bg) !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-focus-outline: 0 !default;
$pagination-hover-color: var(--#{$prefix}link-hover-color) !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-x: .25em !default;
$btn-close-padding-y: $btn-close-padding-x !default; $btn-close-padding-y: $btn-close-padding-x !default;
$btn-close-color: $black !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-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-opacity: .5 !default;
$btn-close-hover-opacity: .75 !default; $btn-close-hover-opacity: .75 !default;
$btn-close-focus-opacity: 1 !default; $btn-close-focus-opacity: 1 !default;

View File

@ -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);
}

View File

@ -397,3 +397,8 @@
margin-right: 0; 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);
}

View 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 ## 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. 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.

View File

@ -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" >}}

View File

@ -104,6 +104,7 @@
- title: Clearfix - title: Clearfix
- title: Color & background - title: Color & background
- title: Colored links - title: Colored links
- title: Focus ring
- title: Position - title: Position
- title: Ratio - title: Ratio
- title: Stacks - title: Stacks