diff --git a/src/lib/button-toggle/button-toggle.html b/src/lib/button-toggle/button-toggle.html index face84340cfa..2f73348c495d 100644 --- a/src/lib/button-toggle/button-toggle.html +++ b/src/lib/button-toggle/button-toggle.html @@ -5,6 +5,8 @@ [checked]="checked" [disabled]="disabled" [name]="name" + (blur)="_onInputBlur()" + (focus)="_onInputFocus()" (change)="_onInputChange($event)" (click)="_onInputClick($event)"> @@ -12,3 +14,5 @@ + +
diff --git a/src/lib/button-toggle/button-toggle.scss b/src/lib/button-toggle/button-toggle.scss index 78a54939c765..7185f4a58ca1 100644 --- a/src/lib/button-toggle/button-toggle.scss +++ b/src/lib/button-toggle/button-toggle.scss @@ -1,4 +1,5 @@ @import '../core/style/elevation'; +@import '../core/a11y/a11y'; $mat-button-toggle-padding: 0 16px !default; $mat-button-toggle-line-height: 36px !default; @@ -34,6 +35,11 @@ $mat-button-toggle-border-radius: 2px !default; .mat-button-toggle { white-space: nowrap; font-family: $mat-font-family; + position: relative; +} + +.mat-button-toggle.mat-button-toggle-focus .mat-button-toggle-focus-overlay { + opacity: 1; } .mat-button-toggle-label-content { @@ -47,3 +53,27 @@ $mat-button-toggle-border-radius: 2px !default; .mat-button-toggle-label-content > * { vertical-align: middle; } + +// Overlay to be used as a tint. Note that the same effect can be achieved by using a pseudo +// element, however we use a proper DOM element in order to be able to disable the default +// touch action. This makes the buttons more responsive on touch devices. +.mat-button-toggle-focus-overlay { + // The button spec calls for focus on raised buttons (and FABs) to be indicated with a + // black, 12% opacity shade over the normal color (for both light and dark themes). + background-color: rgba(black, 0.12); + border-radius: inherit; + pointer-events: none; + opacity: 0; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + + @include cdk-high-contrast { + // Note that IE will render this in the same way, no + // matter whether the theme is light or dark. This helps + // with the readability of focused buttons. + background-color: rgba(white, 0.5); + } +} diff --git a/src/lib/button-toggle/button-toggle.ts b/src/lib/button-toggle/button-toggle.ts index a34cbb15134b..56e99f45d7f1 100644 --- a/src/lib/button-toggle/button-toggle.ts +++ b/src/lib/button-toggle/button-toggle.ts @@ -276,7 +276,6 @@ export class MdButtonToggleGroupMultiple { set vertical(value) { this._vertical = coerceBooleanProperty(value); } - } /** Single button inside of a toggle group. */ @@ -287,13 +286,21 @@ export class MdButtonToggleGroupMultiple { styleUrls: ['button-toggle.css'], encapsulation: ViewEncapsulation.None, host: { - '[class.mat-button-toggle]': 'true' + '[class.mat-button-toggle-focus]': '_hasFocus', + '[class.mat-button-toggle]': 'true', + '(mousedown)': '_setMousedown()', } }) export class MdButtonToggle implements OnInit { /** Whether or not this button toggle is checked. */ private _checked: boolean = false; + /** Whether the button has focus. Used for class binding. */ + _hasFocus: boolean = false; + + /** Whether a mousedown has occurred on this element in the last 100ms. */ + _isMouseDown: boolean = false; + /** Type of the button toggle. Either 'radio' or 'checkbox'. */ _type: ToggleType; @@ -461,10 +468,31 @@ export class MdButtonToggle implements OnInit { event.stopPropagation(); } + _onInputFocus() { + // Only show the focus / ripple indicator when the focus was not triggered by a mouse + // interaction on the component. + if (!this._isMouseDown) { + this._hasFocus = true; + } + } + + _onInputBlur() { + this._hasFocus = false; + } + /** Focuses the button. */ focus() { this._renderer.invokeElementMethod(this._inputElement.nativeElement, 'focus'); } + + _setMousedown() { + // We only *show* the focus style when focus has come to the button via the keyboard. + // The Material Design spec is silent on this topic, and without doing this, the + // button continues to look :active after clicking. + // @see http://marcysutton.com/button-focus-hell/ + this._isMouseDown = true; + setTimeout(() => { this._isMouseDown = false; }, 100); + } }