diff --git a/checkbox/lib/_checkbox.scss b/checkbox/lib/_checkbox.scss index 9aa18bb477..1484a4026d 100644 --- a/checkbox/lib/_checkbox.scss +++ b/checkbox/lib/_checkbox.scss @@ -86,7 +86,7 @@ $_checkmark-bottom-left: 7px, -14px; @include focus-ring.theme( ( - 'offset': -2px, + 'outward-offset': -2px, ) ); } diff --git a/chips/lib/_trailing-icon.scss b/chips/lib/_trailing-icon.scss index 7cf8cacf0a..b4d3864edd 100644 --- a/chips/lib/_trailing-icon.scss +++ b/chips/lib/_trailing-icon.scss @@ -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, ) ); } diff --git a/fab/lib/forced-colors-styles.scss b/fab/lib/forced-colors-styles.scss index c1f371d3e1..4f9d895cf7 100644 --- a/fab/lib/forced-colors-styles.scss +++ b/fab/lib/forced-colors-styles.scss @@ -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; diff --git a/focus/demo/demo.ts b/focus/demo/demo.ts index 3f31dbcd03..cc0cf68d34 100644 --- a/focus/demo/demo.ts +++ b/focus/demo/demo.ts @@ -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>('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)); diff --git a/focus/demo/stories.ts b/focus/demo/stories.ts index 37c7bd4989..0d7be0f1d8 100644 --- a/focus/demo/stories.ts +++ b/focus/demo/stories.ts @@ -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 = { @@ -22,7 +24,7 @@ const standard: MaterialStoryInit = { 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 = { 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` `; } @@ -59,7 +69,6 @@ const multiAction: MaterialStoryInit = { 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 = { } #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`
- +
diff --git a/focus/lib/_focus-ring.scss b/focus/lib/_focus-ring.scss index 3067729870..d6d5b81143 100644 --- a/focus/lib/_focus-ring.scss +++ b/focus/lib/_focus-ring.scss @@ -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; + } + to { + outline-width: var(--_active-width); } - 25% { - box-shadow: inset 0 0 0 calc(var(--_active-width) / 2) currentColor; - outline-width: calc(var(--_active-width) / 2); + } + + @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); } } diff --git a/focus/lib/focus-ring.ts b/focus/lib/focus-ring.ts index 7b57348c63..3b713318cb 100644 --- a/focus/lib/focus-ring.ts +++ b/focus/lib/focus-ring.ts @@ -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 diff --git a/list/lib/listitem/_list-item.scss b/list/lib/listitem/_list-item.scss index f5b6980812..c404d6817b 100644 --- a/list/lib/listitem/_list-item.scss +++ b/list/lib/listitem/_list-item.scss @@ -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( diff --git a/list/lib/listitem/list-item.ts b/list/lib/listitem/list-item.ts index aff2d9d1d2..88fb44e3c8 100644 --- a/list/lib/listitem/list-item.ts +++ b/list/lib/listitem/list-item.ts @@ -159,7 +159,7 @@ export class ListItemEl extends LitElement implements ListItem { * Handles rendering of the focus ring. */ protected renderFocusRing() { - return html``; + return html``; } /** diff --git a/navigationtab/lib/_navigation-tab.scss b/navigationtab/lib/_navigation-tab.scss index 481926633d..59e444f162 100644 --- a/navigationtab/lib/_navigation-tab.scss +++ b/navigationtab/lib/_navigation-tab.scss @@ -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, ) ); diff --git a/navigationtab/lib/navigation-tab.ts b/navigationtab/lib/navigation-tab.ts index c902a385cd..b08ffc71f2 100644 --- a/navigationtab/lib/navigation-tab.ts +++ b/navigationtab/lib/navigation-tab.ts @@ -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)}> - + ${ripple(this.getRipple)} + > + ${when(this.showRipple, this.renderRipple)}