Skip to content

Commit 150d5af

Browse files
authored
fix(material-experimental/mdc-radio): add accessible touch targets (#22994)
Sets up accessible touch targets on the MDC-based radio button. Also hides the touch targets on the two lowest densities. This is something I forgot to do in #22892. Fixes #22991.
1 parent f0ddd54 commit 150d5af

File tree

7 files changed

+42
-15
lines changed

7 files changed

+42
-15
lines changed

src/material-experimental/mdc-checkbox/_checkbox-theme.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,12 @@
125125
$query: mdc-helpers.$mat-base-styles-query
126126
);
127127
}
128+
129+
@if ($density-scale == -2 or $density-scale == 'minimum') {
130+
.mat-mdc-checkbox-touch-target {
131+
display: none;
132+
}
133+
}
128134
}
129135

130136
@mixin theme($theme-or-color-config) {

src/material-experimental/mdc-radio/_radio-theme.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@
5858
.mat-mdc-radio-button .mdc-radio {
5959
@include mdc-radio-theme.density($density-scale, $query: mdc-helpers.$mat-base-styles-query);
6060
}
61+
62+
@if ($density-scale == -2 or $density-scale == 'minimum') {
63+
.mat-mdc-radio-touch-target {
64+
display: none;
65+
}
66+
}
6167
}
6268

6369
@mixin theme($theme-or-color-config) {

src/material-experimental/mdc-radio/radio.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
<div class="mdc-form-field" #formField
22
[class.mdc-form-field--align-end]="labelPosition == 'before'">
33
<div class="mdc-radio" [ngClass]="_classes">
4+
<!-- Render this element first so the input is on top. -->
5+
<div class="mat-mdc-radio-touch-target" (click)="_onInputInteraction($event)"></div>
46
<input #input class="mdc-radio__native-control" type="radio"
57
[id]="inputId"
68
[checked]="checked"
@@ -12,7 +14,7 @@
1214
[attr.aria-label]="ariaLabel"
1315
[attr.aria-labelledby]="ariaLabelledby"
1416
[attr.aria-describedby]="ariaDescribedby"
15-
(change)="_onInputChange($event)">
17+
(change)="_onInputInteraction($event)">
1618
<div class="mdc-radio__background">
1719
<div class="mdc-radio__outer-circle"></div>
1820
<div class="mdc-radio__inner-circle"></div>

src/material-experimental/mdc-radio/radio.scss

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
@use '@material/radio/radio' as mdc-radio;
22
@use '@material/radio/radio-theme' as mdc-radio-theme;
33
@use '@material/form-field' as mdc-form-field;
4+
@use '@material/touch-target' as mdc-touch-target;
45
@use '../mdc-helpers/mdc-helpers';
56
@use '../../cdk/a11y';
67
@use '../../material/core/style/layout-common';
@@ -25,6 +26,19 @@
2526
@include mdc-radio.without-ripple($query: animation);
2627
}
2728

29+
// Element used to provide a larger tap target for users on touch devices.
30+
.mat-mdc-radio-touch-target {
31+
@include mdc-touch-target.touch-target(
32+
$set-width: true,
33+
$query: mdc-helpers.$mat-base-styles-query);
34+
35+
[dir='rtl'] & {
36+
left: 0;
37+
right: 50%;
38+
transform: translate(50%, -50%);
39+
}
40+
}
41+
2842
// Note that this creates a square box around the circle, however it's consistent with
2943
// how IE/Edge treat native radio buttons in high contrast mode. We can't turn the border
3044
// into a dotted one, because it's too thick which causes the circles to look off.

src/material/radio/radio.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
[attr.aria-label]="ariaLabel"
1717
[attr.aria-labelledby]="ariaLabelledby"
1818
[attr.aria-describedby]="ariaDescribedby"
19-
(change)="_onInputChange($event)"
19+
(change)="_onInputInteraction($event)"
2020
(click)="_onInputClick($event)">
2121

2222
<!-- The ripple comes after the input so that we can target it with a CSS

src/material/radio/radio.ts

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -585,24 +585,23 @@ export abstract class _MatRadioButtonBase extends _MatRadioButtonMixinBase imple
585585
event.stopPropagation();
586586
}
587587

588-
/**
589-
* Triggered when the radio button received a click or the input recognized any change.
590-
* Clicking on a label element, will trigger a change event on the associated input.
591-
*/
592-
_onInputChange(event: Event) {
588+
/** Triggered when the radio button receives an interaction from the user. */
589+
_onInputInteraction(event: Event) {
593590
// We always have to stop propagation on the change event.
594591
// Otherwise the change event, from the input element, will bubble up and
595592
// emit its event object to the `change` output.
596593
event.stopPropagation();
597594

598-
const groupValueChanged = this.radioGroup && this.value !== this.radioGroup.value;
599-
this.checked = true;
600-
this._emitChangeEvent();
595+
if (!this.checked && !this.disabled) {
596+
const groupValueChanged = this.radioGroup && this.value !== this.radioGroup.value;
597+
this.checked = true;
598+
this._emitChangeEvent();
601599

602-
if (this.radioGroup) {
603-
this.radioGroup._controlValueAccessorChangeFn(this.value);
604-
if (groupValueChanged) {
605-
this.radioGroup._emitChangeEvent();
600+
if (this.radioGroup) {
601+
this.radioGroup._controlValueAccessorChangeFn(this.value);
602+
if (groupValueChanged) {
603+
this.radioGroup._emitChangeEvent();
604+
}
606605
}
607606
}
608607
}

tools/public_api_guard/material/radio.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ export declare abstract class _MatRadioButtonBase extends _MatRadioButtonMixinBa
2525
constructor(radioGroup: _MatRadioGroupBase<_MatRadioButtonBase>, elementRef: ElementRef, _changeDetector: ChangeDetectorRef, _focusMonitor: FocusMonitor, _radioDispatcher: UniqueSelectionDispatcher, animationMode?: string, _providerOverride?: MatRadioDefaultOptions | undefined, tabIndex?: string);
2626
_isRippleDisabled(): boolean;
2727
_markForCheck(): void;
28-
_onInputChange(event: Event): void;
2928
_onInputClick(event: Event): void;
29+
_onInputInteraction(event: Event): void;
3030
protected _setDisabled(value: boolean): void;
3131
focus(options?: FocusOptions, origin?: FocusOrigin): void;
3232
ngAfterViewInit(): void;

0 commit comments

Comments
 (0)