Skip to content

feat(material/core): use strong focus indicators in high contrast mode #25039

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions src/dev-app/theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,9 @@ $candy-app-theme: mat.define-light-theme((

.demo-strong-focus {
// Include base styles for strong focus indicators.
@include mat.strong-focus-indicators();
@include experimental.mdc-strong-focus-indicators();

// Include the default theme for focus indicators.
@include mat.strong-focus-indicators-theme($candy-app-theme);
@include experimental.mdc-strong-focus-indicators-theme($candy-app-theme);
$indicators-config: (border-color: mat.get-color-from-palette($candy-app-primary));
@include mat.strong-focus-indicators($indicators-config);
@include experimental.mdc-strong-focus-indicators($indicators-config);
}

// Include the alternative theme styles inside of a block with a CSS class. You can make this
Expand All @@ -60,8 +57,9 @@ $candy-app-theme: mat.define-light-theme((

// Include the dark theme colors for focus indicators.
&.demo-strong-focus {
@include mat.strong-focus-indicators-color($dark-colors);
@include experimental.mdc-strong-focus-indicators-color($dark-colors);
$indicators-config: (border-color: mat.get-color-from-palette($dark-primary));
@include mat.strong-focus-indicators($indicators-config);
@include experimental.mdc-strong-focus-indicators($indicators-config);
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/material-experimental/mdc-button/_button-base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@
.mat-mdc-focus-indicator {
@include mat.private-fill();
}

&:focus .mat-mdc-focus-indicator::before {
content: '';
}
}

// MDC's disabled buttons define a default cursor with pointer-events none. However, they select
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,3 @@
outline: solid 1px;
}
}

@include cdk.high-contrast(active, off) {
.mat-mdc-button-base:focus {
outline: solid 3px;
}
}
19 changes: 19 additions & 0 deletions src/material-experimental/mdc-button/button.scss
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,22 @@
right: $offset;
border-width: $offset;
}

// For the button element, default inset/offset values are necessary to ensure that
// the focus indicator is sufficiently contrastive and renders appropriately.
.mat-mdc-unelevated-button,
.mat-mdc-raised-button {
.mat-mdc-focus-indicator::before {
$default-border-width: mat.$focus-indicators-private-default-border-width;
$border-width: var(--mat-mdc-focus-indicator-border-width, #{$default-border-width});
$offset: calc(#{$border-width} + 2px);
margin: calc(#{$offset} * -1);
}
}

.mat-mdc-outlined-button .mat-mdc-focus-indicator::before {
$default-border-width: mat.$focus-indicators-private-default-border-width;
$border-width: var(--mat-mdc-focus-indicator-border-width, #{$default-border-width});
$offset: calc(#{$border-width} + 3px);
margin: calc(#{$offset} * -1);
}
7 changes: 7 additions & 0 deletions src/material-experimental/mdc-button/fab.scss
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@
.mat-icon, .material-icons {
@include mdc-fab.icon_();
}

.mat-mdc-focus-indicator::before {
$default-border-width: mat.$focus-indicators-private-default-border-width;
$border-width: var(--mat-mdc-focus-indicator-border-width, #{$default-border-width});
$offset: calc(#{$border-width} + 2px);
margin: calc(#{$offset} * -1);
}
}

.mat-mdc-extended-fab {
Expand Down
18 changes: 12 additions & 6 deletions src/material-experimental/mdc-checkbox/checkbox.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
@use '@angular/cdk';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: could potentially update the BUILD files to keep the build graph minimal but at the same time we don't enforce strict deps here and also don't control it nicely, so not feeling strongly..

@use '@angular/material' as mat;
@use '@material/checkbox/checkbox' as mdc-checkbox;
@use '@material/checkbox/checkbox-theme' as mdc-checkbox-theme;
Expand Down Expand Up @@ -49,11 +48,6 @@
.mdc-checkbox__native-control:not([disabled]):focus ~ .mdc-checkbox__ripple {
opacity: map.get(mdc-ripple.$dark-ink-opacities, hover) +
map.get(mdc-ripple.$dark-ink-opacities, focus);

@include cdk.high-contrast(active, off) {
outline: solid 3px;
opacity: 1;
}
}
}

Expand Down Expand Up @@ -125,3 +119,15 @@
$set-width: true,
$query: mdc-helpers.$mat-base-styles-query);
}

// Checkbox components have to set `border-radius: 50%` in order to support density scaling
// which will clip a square focus indicator so we have to turn it into a circle.
.mat-mdc-checkbox-ripple::before {
border-radius: 50%;
}

// For checkboxes render the focus indicator when we know
// the hidden input is focused (slightly different for each control).
.mdc-checkbox__native-control:focus ~ .mat-mdc-focus-indicator::before {
content: '';
}
45 changes: 31 additions & 14 deletions src/material-experimental/mdc-chips/chip.scss
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,6 @@
@include cdk.high-contrast(active, off) {
outline: solid 1px;

&.cdk-focused {
// Use 2px here since the dotted outline is a little thinner.
outline: dotted 2px;
}

.mdc-evolution-chip__checkmark-path {
// SVG colors won't be changed in high contrast mode and since the checkmark is white
// by default, it'll blend in with the background in black-on-white mode. Override the
Expand Down Expand Up @@ -180,25 +175,42 @@
left: 0;
pointer-events: none;
}
}

.mat-mdc-chip-remove {
.mat-icon {
width: inherit;
height: inherit;
font-size: inherit;
box-sizing: content-box;
// For the chip element, default inset/offset values are necessary to ensure that
// the focus indicator is sufficiently contrastive and renders appropriately.
.mat-mdc-focus-indicator::before {
$default-border-width: mat.$focus-indicators-private-default-border-width;
$border-width: var(--mat-mdc-focus-indicator-border-width, #{$default-border-width});
$offset: calc(#{$border-width} + 2px);
margin: calc(#{$offset} * -1);
}
}

// Fades out the trailing icon slightly so that it can become darker when focused.
// The MDC theming has variables for this, but the focus/hover states don't seem to work.
.mat-mdc-chip-remove {
// Fades out the trailing icon slightly so that it can become darker when focused.
// The MDC theming has variables for this, but the focus/hover states don't seem to work.
opacity: 0.54;

&:focus {
opacity: 1;
}

&::before {
$default-border-width: mat.$focus-indicators-private-default-border-width;
$offset: var(--mat-mdc-focus-indicator-border-width, #{$default-border-width});
margin: calc(#{$offset} * -1);

// MDC sets a padding a on the chip button which stretches out the focus indicator.
left: 8px;
right: 8px;
}

.mat-icon {
width: inherit;
height: inherit;
font-size: inherit;
box-sizing: content-box;
}
}

.mat-chip-edit-input {
Expand All @@ -215,3 +227,8 @@
outline-width: 3px;
}
}

// In the chips the individual actions have focus so we target a different element.
.mat-mdc-chip-action:focus .mat-mdc-focus-indicator::before {
content: '';
}
24 changes: 3 additions & 21 deletions src/material-experimental/mdc-core/option/option.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
@use '@angular/cdk';
@use '@angular/material' as mat;
@use '@material/list/evolution-mixins' as mdc-list-mixins;
@use '@material/list/evolution-variables' as mdc-list-variables;
Expand Down Expand Up @@ -91,24 +90,7 @@
}
}

.mat-mdc-option-active {
@include cdk.high-contrast(active, off) {
// A pseudo element is used here, because the active indication gets moved between options
// and we don't want the border from below to shift the layout around as it's added and removed.
&::before {
content: '';
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
pointer-events: none;

// We use a border here, rather than an outline, because the outline will be cut off
// by the `overflow: hidden` on the panel wrapping the options, whereas a border
// will push the element inwards. This could be done using `outline-offset: -1px`,
// however the property isn't supported on IE11.
border: solid 1px currentColor;
}
}
// For options, render the focus indicator when the class .mat-mdc-option-active is present.
.mat-mdc-option-active::before {
content: '';
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,28 @@
@use 'sass:map';
@use 'sass:meta';

@mixin _border-color($color) {
.mat-mdc-focus-indicator::before {
border-color: $color;
}
}

// stylelint-disable-next-line material/theme-mixin-api
@mixin color($config-or-theme-or-color) {
@if meta.type-of($config-or-theme-or-color) == 'color' {
@include _border-color($config-or-theme-or-color);
@include mat.focus-indicators-private-private-customize-focus-indicators((
border-color: $config-or-theme-or-color
), 'mat-mdc');
}
@else {
$config: mat.get-color-config($config-or-theme-or-color);
$border-color: mat.get-color-from-palette(map.get($config, primary));
@include _border-color($border-color);
@include mat.focus-indicators-private-private-customize-focus-indicators((
border-color: $border-color
), 'mat-mdc');
}
}

// stylelint-disable-next-line material/theme-mixin-api
@mixin theme($theme-or-color-config-or-color) {
@if meta.type-of($theme-or-color-config-or-color) == 'color' {
@include _border-color($theme-or-color-config-or-color);
@include mat.focus-indicators-private-private-customize-focus-indicators((
border-color: $theme-or-color-config-or-color
), 'mat-mdc');
}
@else {
$theme: mat.private-legacy-get-theme($theme-or-color-config-or-color);
Expand Down
97 changes: 4 additions & 93 deletions src/material-experimental/mdc-helpers/_focus-indicators.scss
Original file line number Diff line number Diff line change
@@ -1,104 +1,15 @@
@use '@angular/material' as mat;
@use 'sass:map';
@use '@angular/material' as mat;

/// Mixin that turns on strong focus indicators.
///
/// @example
/// .my-app {
/// @include mat-mdc-strong-focus-indicators($config);
/// }
@mixin strong-focus-indicators($config: ()) {
// Default focus indicator config.
$default-config: (
border-style: solid,
border-width: 3px,
border-radius: 4px,
border-color: black,
display: block,
);

// Merge default config with user config.
$config: map.merge($default-config, $config);
$border-style: map.get($config, border-style);
$border-width: map.get($config, border-width);
$border-radius: map.get($config, border-radius);

// Base styles for focus indicators.
.mat-mdc-focus-indicator::before {
@include mat.private-fill();
box-sizing: border-box;
pointer-events: none;
border: $border-width $border-style transparent;
border-radius: $border-radius;

.cdk-high-contrast-active & {
display: none;
}
}

// By default, all focus indicators are flush with the bounding box of their
// host element. For particular elements (listed below), default inset/offset
// values are necessary to ensure that the focus indicator is sufficiently
// contrastive and renders appropriately.

.mat-mdc-unelevated-button .mat-mdc-focus-indicator::before,
.mat-mdc-raised-button .mat-mdc-focus-indicator::before,
.mdc-fab .mat-mdc-focus-indicator::before,
.mat-mdc-chip-action-label .mat-mdc-focus-indicator::before {
margin: -($border-width + 2px);
}

.mat-mdc-outlined-button .mat-mdc-focus-indicator::before {
margin: -($border-width + 3px);
}

.mat-mdc-focus-indicator.mat-mdc-chip-remove::before {
margin: -$border-width;
}

// MDC sets a padding a on the button which stretches out the focus indicator.
.mat-mdc-focus-indicator.mat-mdc-chip-remove::before {
left: 8px;
right: 8px;
}

.mat-mdc-focus-indicator.mat-mdc-tab::before,
.mat-mdc-focus-indicator.mat-mdc-tab-link::before {
margin: 5px;
}

// These components have to set `border-radius: 50%` in order to support density scaling
// which will clip a square focus indicator so we have to turn it into a circle.
.mat-mdc-checkbox-ripple.mat-mdc-focus-indicator::before,
.mat-radio-ripple.mat-mdc-focus-indicator::before,
.mat-mdc-slider .mat-mdc-focus-indicator::before,
.mat-mdc-slide-toggle .mat-mdc-focus-indicator::before {
border-radius: 50%;
}

// Render the focus indicator on focus. Defining a pseudo element's
// content will cause it to render.

// For checkboxes, radios and slide toggles, render the focus indicator when we know
// the hidden input is focused (slightly different for each control).
.mdc-checkbox__native-control:focus ~ .mat-mdc-focus-indicator::before,
.mat-mdc-slide-toggle-focused .mat-mdc-focus-indicator::before,
.mat-mdc-radio-button.cdk-focused .mat-mdc-focus-indicator::before,

// In the chips the individual actions have focus so we target a different element.
.mat-mdc-chip-action:focus .mat-mdc-focus-indicator::before,

// For buttons and list items, render the focus indicator when the parent
// button or list item is focused.
.mat-mdc-button-base:focus .mat-mdc-focus-indicator::before,
.mat-mdc-list-item:focus > .mat-mdc-focus-indicator::before,

// For options, render the focus indicator when the class .mat-mdc-option-active is present.
.mat-mdc-focus-indicator.mat-mdc-option-active::before,

// In the MDC slider the focus indicator is inside the thumb.
.mdc-slider__thumb--focused .mat-mdc-focus-indicator::before,

// For all other components, render the focus indicator on focus.
.mat-mdc-focus-indicator:focus::before {
content: '';
}
@include mat.focus-indicators-private-private-customize-focus-indicators($config, 'mat-mdc');
}
14 changes: 11 additions & 3 deletions src/material-experimental/mdc-list/list.scss
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,17 @@
// The MDC-based list items already use the `::before` pseudo element for the standard
// focus/selected/hover state. Hence, we need to have a separate list-item spanning
// element that can be used for strong focus indicators.
.mat-mdc-list-item > .mat-mdc-focus-indicator {
@include mat.private-fill();
pointer-events: none;
.mat-mdc-list-item {
& > .mat-mdc-focus-indicator {
@include mat.private-fill();
pointer-events: none;
}

// For list items, render the focus indicator when the parent
// listem item is focused.
&:focus > .mat-mdc-focus-indicator::before {
content: '';
}
}

.mat-mdc-list-item.mdc-list-item--with-three-lines {
Expand Down
Loading