mirror of
https://github.com/material-components/material-web.git
synced 2024-09-11 13:46:10 +03:00
fix(focus)!: corrected outward/inward animations
BREAKING CHANGE: inward focus rings must be specified with `inward` rather than a negative offset. PiperOrigin-RevId: 534552625
This commit is contained in:
parent
146aa17509
commit
26d69c271e
@ -86,7 +86,7 @@ $_checkmark-bottom-left: 7px, -14px;
|
||||
|
||||
@include focus-ring.theme(
|
||||
(
|
||||
'offset': -2px,
|
||||
'outward-offset': -2px,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -21,7 +21,7 @@
|
||||
'shape-end-start': var(--_container-shape-end-end),
|
||||
'shape-end-end': var(--_container-shape-end-end),
|
||||
'shape-start-end': var(--_container-shape-start-end),
|
||||
'offset': -2px,
|
||||
'outward-offset': -2px,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
// Adjust the focus ring padding to account for the 1px border in HCM.
|
||||
@include focus-ring.theme(
|
||||
(
|
||||
'offset': 3px,
|
||||
'outward-offset': 3px,
|
||||
)
|
||||
);
|
||||
border: 1px solid ButtonText;
|
||||
|
@ -8,21 +8,25 @@ import './index.js';
|
||||
import './material-collection.js';
|
||||
|
||||
import {KnobTypesToKnobs, MaterialCollection, materialInitsToStoryInits, setUpDemo} from './material-collection.js';
|
||||
import {cssCustomProperty, Knob, textInput} from './index.js';
|
||||
import {boolInput, cssCustomProperty, Knob, textInput} from './index.js';
|
||||
|
||||
import {stories, StoryKnobs} from './stories.js';
|
||||
|
||||
const collection =
|
||||
new MaterialCollection<KnobTypesToKnobs<StoryKnobs>>('Focus', [
|
||||
new Knob('inward', {ui: boolInput(), defaultValue: false}),
|
||||
new Knob(
|
||||
'--md-focus-ring-width',
|
||||
{ui: textInput(), wiring: cssCustomProperty, defaultValue: '3px'}),
|
||||
new Knob(
|
||||
'--md-focus-ring-offset',
|
||||
{ui: textInput(), wiring: cssCustomProperty, defaultValue: '2px'}),
|
||||
new Knob(
|
||||
'--md-focus-ring-active-width',
|
||||
{ui: textInput(), wiring: cssCustomProperty, defaultValue: '8px'}),
|
||||
new Knob(
|
||||
'--md-focus-ring-outward-offset',
|
||||
{ui: textInput(), wiring: cssCustomProperty, defaultValue: '2px'}),
|
||||
new Knob(
|
||||
'--md-focus-ring-inward-offset',
|
||||
{ui: textInput(), wiring: cssCustomProperty, defaultValue: '0px'}),
|
||||
]);
|
||||
|
||||
collection.addStories(...materialInitsToStoryInits(stories));
|
||||
|
@ -11,9 +11,11 @@ import {css, html} from 'lit';
|
||||
|
||||
/** Knob types for focus ring stories. */
|
||||
export interface StoryKnobs {
|
||||
inward: boolean;
|
||||
'--md-focus-ring-width': string;
|
||||
'--md-focus-ring-offset': string;
|
||||
'--md-focus-ring-active-width': string;
|
||||
'--md-focus-ring-outward-offset': string;
|
||||
'--md-focus-ring-inward-offset': string;
|
||||
}
|
||||
|
||||
const standard: MaterialStoryInit<StoryKnobs> = {
|
||||
@ -22,7 +24,7 @@ const standard: MaterialStoryInit<StoryKnobs> = {
|
||||
button {
|
||||
appearance: none;
|
||||
background: var(--md-sys-color-surface);
|
||||
border: 1px solid var(--md-sys-color-outline);
|
||||
border: none;
|
||||
border-radius: 16px;
|
||||
--md-focus-ring-shape: 16px;
|
||||
height: 64px;
|
||||
@ -33,20 +35,28 @@ const standard: MaterialStoryInit<StoryKnobs> = {
|
||||
width: 64px;
|
||||
}
|
||||
|
||||
button::before {
|
||||
border: 1px solid var(--md-sys-color-outline);
|
||||
border-radius: inherit;
|
||||
content: '';
|
||||
inset: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
button:focus {
|
||||
background: var(--md-sys-color-surface-variant);
|
||||
}
|
||||
`,
|
||||
render() {
|
||||
render({inward}) {
|
||||
return html`
|
||||
<button aria-label="A button with a focus ring">
|
||||
<md-focus-ring></md-focus-ring>
|
||||
<md-focus-ring ?inward=${inward}></md-focus-ring>
|
||||
</button>
|
||||
<button aria-label="A button with a focus ring">
|
||||
<md-focus-ring></md-focus-ring>
|
||||
<md-focus-ring ?inward=${inward}></md-focus-ring>
|
||||
</button>
|
||||
<button aria-label="A button with a focus ring">
|
||||
<md-focus-ring></md-focus-ring>
|
||||
<md-focus-ring ?inward=${inward}></md-focus-ring>
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
@ -59,7 +69,6 @@ const multiAction: MaterialStoryInit<StoryKnobs> = {
|
||||
align-items: center;
|
||||
appearance: none;
|
||||
background: var(--md-sys-color-surface);
|
||||
border: 1px solid var(--md-sys-color-outline);
|
||||
border-radius: 16px;
|
||||
--md-focus-ring-shape: 16px;
|
||||
display: flex;
|
||||
@ -94,17 +103,25 @@ const multiAction: MaterialStoryInit<StoryKnobs> = {
|
||||
}
|
||||
|
||||
#secondary {
|
||||
border: 1px solid var(--md-sys-color-outline);
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
border-radius: 32px;
|
||||
--md-focus-ring-shape: 32px;
|
||||
}
|
||||
|
||||
[role="list"]::before,
|
||||
#secondary::before {
|
||||
border: 1px solid var(--md-sys-color-outline);
|
||||
border-radius: inherit;
|
||||
content: '';
|
||||
inset: 0;
|
||||
position: absolute;
|
||||
}
|
||||
`,
|
||||
render() {
|
||||
render({inward}) {
|
||||
return html`
|
||||
<div role="list">
|
||||
<md-focus-ring for="primary"></md-focus-ring>
|
||||
<md-focus-ring for="primary" ?inward=${inward}></md-focus-ring>
|
||||
|
||||
<div role="listitem">
|
||||
<button id="primary" aria-label="The primary action for a multi-action component">
|
||||
@ -115,7 +132,7 @@ const multiAction: MaterialStoryInit<StoryKnobs> = {
|
||||
<div role="listitem">
|
||||
<button id="secondary" aria-label="The secondary action for a multi-action component">
|
||||
X
|
||||
<md-focus-ring></md-focus-ring>
|
||||
<md-focus-ring ?inward=${inward}></md-focus-ring>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -44,37 +44,80 @@ $_md-sys-motion: tokens.md-sys-motion-values();
|
||||
--_shape-end-end: var(--md-focus-ring-shape-end-end, var(--_shape));
|
||||
--_shape-end-start: var(--md-focus-ring-shape-end-start, var(--_shape));
|
||||
|
||||
animation-duration: var(--_duration);
|
||||
animation-delay: 0s, calc(var(--_duration) * 0.25);
|
||||
animation-duration: calc(var(--_duration) * 0.25),
|
||||
calc(var(--_duration) * 0.75);
|
||||
animation-timing-function: map.get($_md-sys-motion, 'easing-emphasized');
|
||||
border-end-end-radius: calc(var(--_offset) + var(--_shape-end-end));
|
||||
border-end-start-radius: calc(var(--_offset) + var(--_shape-end-start));
|
||||
border-start-end-radius: calc(var(--_offset) + var(--_shape-start-end));
|
||||
border-start-start-radius: calc(var(--_offset) + var(--_shape-start-start));
|
||||
box-shadow: inset 0 0 0 0 currentColor;
|
||||
box-sizing: border-box;
|
||||
color: var(--_color);
|
||||
display: none;
|
||||
inset: calc(-1 * (var(--_offset) + 1px));
|
||||
// NOTE: this 1px offset hides a transparent ring between the outline and
|
||||
// offset when border-radius is large
|
||||
outline-offset: -1px;
|
||||
outline: var(--_width) solid currentColor;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
:host([visible]) {
|
||||
display: flex;
|
||||
animation-name: focus-ring;
|
||||
}
|
||||
|
||||
@keyframes focus-ring {
|
||||
:host(:not([inward])) {
|
||||
animation-name: outward-grow, outward-shrink;
|
||||
border-end-end-radius: calc(var(--_shape-end-end) + var(--_outward-offset));
|
||||
border-end-start-radius: calc(
|
||||
var(--_shape-end-start) + var(--_outward-offset)
|
||||
);
|
||||
border-start-end-radius: calc(
|
||||
var(--_shape-start-end) + var(--_outward-offset)
|
||||
);
|
||||
border-start-start-radius: calc(
|
||||
var(--_shape-start-start) + var(--_outward-offset)
|
||||
);
|
||||
inset: calc(-1 * (var(--_outward-offset)));
|
||||
outline: var(--_width) solid currentColor;
|
||||
}
|
||||
|
||||
:host([inward]) {
|
||||
animation-name: inward-grow, inward-shrink;
|
||||
border-end-end-radius: calc(var(--_shape-end-end) - var(--_inward-offset));
|
||||
border-end-start-radius: calc(
|
||||
var(--_shape-end-start) - var(--_inward-offset)
|
||||
);
|
||||
border-start-end-radius: calc(
|
||||
var(--_shape-start-end) - var(--_inward-offset)
|
||||
);
|
||||
border-start-start-radius: calc(
|
||||
var(--_shape-start-start) - var(--_inward-offset)
|
||||
);
|
||||
border: var(--_width) solid currentColor;
|
||||
inset: var(--_inward-offset);
|
||||
}
|
||||
|
||||
@keyframes outward-grow {
|
||||
from {
|
||||
outline-width: 0px;
|
||||
outline-width: 0;
|
||||
}
|
||||
25% {
|
||||
box-shadow: inset 0 0 0 calc(var(--_active-width) / 2) currentColor;
|
||||
outline-width: calc(var(--_active-width) / 2);
|
||||
to {
|
||||
outline-width: var(--_active-width);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes outward-shrink {
|
||||
from {
|
||||
outline-width: var(--_active-width);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes inward-grow {
|
||||
from {
|
||||
border-width: 0;
|
||||
}
|
||||
to {
|
||||
border-width: var(--_active-width);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes inward-shrink {
|
||||
from {
|
||||
border-width: var(--_active-width);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ export class FocusRing extends LitElement {
|
||||
* Makes the focus ring visible.
|
||||
*/
|
||||
@property({type: Boolean, reflect: true}) visible = false;
|
||||
@property({type: Boolean, reflect: true}) inward = false;
|
||||
|
||||
/**
|
||||
* Reflects the value of the `for` attribute, which is the ID of the focus
|
||||
|
@ -47,8 +47,7 @@
|
||||
|
||||
@include focus-ring.theme(
|
||||
(
|
||||
'offset': -3px,
|
||||
'shape': map.get(tokens.md-sys-shape-values(), 'corner-extra-small'),
|
||||
'shape': 8px,
|
||||
)
|
||||
);
|
||||
@include ripple.theme(
|
||||
|
@ -159,7 +159,7 @@ export class ListItemEl extends LitElement implements ListItem {
|
||||
* Handles rendering of the focus ring.
|
||||
*/
|
||||
protected renderFocusRing() {
|
||||
return html`<md-focus-ring class="focus-ring" for="item"></md-focus-ring>`;
|
||||
return html`<md-focus-ring class="focus-ring" for="item" inward></md-focus-ring>`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -43,8 +43,8 @@ $_custom-property-prefix: 'navigation-bar';
|
||||
|
||||
@include focus-ring.theme(
|
||||
(
|
||||
'offset': -2px,
|
||||
'shape': map.get(tokens.md-sys-shape-values(), 'corner-small'),
|
||||
'inward-offset': -1px,
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -52,8 +52,9 @@ export class NavigationTab extends LitElement implements NavigationTabState {
|
||||
aria-label=${ariaLabel || nothing}
|
||||
tabindex="${this.active ? 0 : -1}"
|
||||
@click="${this.handleClick}"
|
||||
${ripple(this.getRipple)}>
|
||||
<md-focus-ring></md-focus-ring>
|
||||
${ripple(this.getRipple)}
|
||||
>
|
||||
<md-focus-ring inward></md-focus-ring>
|
||||
${when(this.showRipple, this.renderRipple)}
|
||||
<span aria-hidden="true" class="md3-navigation-tab__icon-content"
|
||||
><span class="md3-navigation-tab__active-indicator"
|
||||
|
@ -45,7 +45,7 @@ $_md-sys-motion: tokens.md-sys-motion-values();
|
||||
|
||||
@include focus-ring.theme(
|
||||
(
|
||||
'offset': -2px,
|
||||
'outward-offset': -2px,
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -450,7 +450,7 @@ $_md-sys-shape: tokens.md-sys-shape-values();
|
||||
md-focus-ring {
|
||||
@include focus-ring.theme(
|
||||
(
|
||||
'offset': -2px,
|
||||
'outward-offset': -2px,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -54,14 +54,15 @@
|
||||
|
||||
@include focus-ring.theme(
|
||||
(
|
||||
// desired border-radius is 8px and it's internally calc'd as sum
|
||||
// of shape + offset (-7 + 15)
|
||||
shape: 15px,
|
||||
offset: -7px
|
||||
'shape': 8px,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
:host([selected]) md-focus-ring {
|
||||
margin-bottom: calc(var(--_active-indicator-height) + 1px);
|
||||
}
|
||||
|
||||
.button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
@ -115,7 +115,7 @@ export class Tab extends LitElement {
|
||||
aria-label=${this.ariaLabel || nothing}
|
||||
${ripple(this.getRipple)}
|
||||
>
|
||||
<md-focus-ring></md-focus-ring>
|
||||
<md-focus-ring inward></md-focus-ring>
|
||||
<md-elevation></md-elevation>
|
||||
${when(this.showRipple, this.renderRipple)}
|
||||
<span class="touch"></span>
|
||||
|
@ -19,7 +19,8 @@ $supported-tokens: (
|
||||
'active-width',
|
||||
'color',
|
||||
'duration',
|
||||
'offset',
|
||||
'inward-offset',
|
||||
'outward-offset',
|
||||
'shape',
|
||||
'width',
|
||||
// go/keep-sorted end
|
||||
@ -36,7 +37,8 @@ $_default: (
|
||||
'active-width': if($exclude-hardcoded-values, null, 8px),
|
||||
'color': map.get($deps, 'md-sys-color', 'secondary'),
|
||||
'duration': map.get($deps, 'md-sys-motion', 'duration-long4'),
|
||||
'offset': if($exclude-hardcoded-values, null, 2px),
|
||||
'inward-offset': if($exclude-hardcoded-values, null, 0px),
|
||||
'outward-offset': if($exclude-hardcoded-values, null, 2px),
|
||||
'shape': map.get($deps, 'md-sys-shape', 'corner-full'),
|
||||
'width': if($exclude-hardcoded-values, null, 3px),
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user