Skip to content

Commit 0858864

Browse files
committed
fix(material/radio): rendering artifacts at some zoom levels
Currently the circle inside the radio button is rendered out as an element with a solid border. Presumably MDC did it this way, because it allows more CSS rules to be combined and it handles high contrast mode automatically. The problem with this approach is that depending on the user's screen and zoom level, there can be rendering artifacts on Windows like a dot in the middle of the radio button. These changes resolve the rendering artifacts by using a background color for the circle and which is overridden to `CanvasText` for high contrast mode. Fixes #31466.
1 parent 9e14c55 commit 0858864

File tree

2 files changed

+60
-20
lines changed

2 files changed

+60
-20
lines changed

src/material/radio/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ sass_library(
5656
srcs = ["_radio-common.scss"],
5757
deps = [
5858
":m2",
59+
"//src/cdk:sass_lib",
5960
"//src/material/core/tokens:token_utils",
6061
],
6162
)

src/material/radio/_radio-common.scss

Lines changed: 59 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
@use '@angular/cdk';
12
@use './m3-radio';
23
@use '../core/tokens/token-utils';
34

@@ -47,9 +48,14 @@ $fallbacks: m3-radio.get-tokens();
4748
}
4849

4950
&:hover > .mdc-radio__native-control:enabled:checked + .mdc-radio__background {
50-
> .mdc-radio__outer-circle,
51+
$token: 'radio-selected-hover-icon-color';
52+
53+
> .mdc-radio__outer-circle {
54+
border-color: token-utils.slot($token, $fallbacks);
55+
}
56+
5157
> .mdc-radio__inner-circle {
52-
border-color: token-utils.slot(radio-selected-hover-icon-color, $fallbacks);
58+
background-color: token-utils.slot($token, $fallbacks, currentColor);
5359
}
5460
}
5561

@@ -60,9 +66,14 @@ $fallbacks: m3-radio.get-tokens();
6066
}
6167

6268
&:active > .mdc-radio__native-control:enabled:checked + .mdc-radio__background {
63-
> .mdc-radio__outer-circle,
69+
$token: 'radio-selected-pressed-icon-color';
70+
71+
> .mdc-radio__outer-circle {
72+
border-color: token-utils.slot($token, $fallbacks);
73+
}
74+
6475
> .mdc-radio__inner-circle {
65-
border-color: token-utils.slot(radio-selected-pressed-icon-color, $fallbacks);
76+
background-color: token-utils.slot($token, $fallbacks, currentColor);
6677
}
6778
}
6879
}
@@ -113,11 +124,16 @@ $fallbacks: m3-radio.get-tokens();
113124
box-sizing: border-box;
114125
width: 100%;
115126
height: 100%;
116-
transform: scale(0, 0);
117-
border-width: 10px;
118-
border-style: solid;
127+
transform: scale(0);
119128
border-radius: 50%;
120-
transition: _exit-transition(transform), _exit-transition(border-color);
129+
transition: _exit-transition(transform), _exit-transition(background-color);
130+
131+
@include cdk.high-contrast {
132+
// Override the color, because solid colors don't show up by default in
133+
// high contrast mode. We need !important here, because the various state
134+
// selectors are really specific and duplicating them will be brittle.
135+
background-color: CanvasText !important;
136+
}
121137
}
122138

123139
.mdc-radio__native-control {
@@ -142,7 +158,7 @@ $fallbacks: m3-radio.get-tokens();
142158
}
143159

144160
> .mdc-radio__inner-circle {
145-
transition: _enter-transition(transform), _enter-transition(border-color);
161+
transition: _enter-transition(transform), _enter-transition(background-color);
146162
}
147163
}
148164
}
@@ -162,12 +178,18 @@ $fallbacks: m3-radio.get-tokens();
162178
}
163179

164180
+ .mdc-radio__background {
181+
$color-token: 'radio-disabled-selected-icon-color';
182+
$opacity-token: token-utils.slot(radio-disabled-selected-icon-opacity, $fallbacks);
165183
cursor: default;
166184

167-
> .mdc-radio__inner-circle,
168185
> .mdc-radio__outer-circle {
169-
border-color: token-utils.slot(radio-disabled-selected-icon-color, $fallbacks);
170-
opacity: token-utils.slot(radio-disabled-selected-icon-opacity, $fallbacks);
186+
border-color: token-utils.slot($color-token, $fallbacks);
187+
opacity: $opacity-token;
188+
}
189+
190+
> .mdc-radio__inner-circle {
191+
background-color: token-utils.slot($color-token, $fallbacks, currentColor);
192+
opacity: $opacity-token;
171193
}
172194
}
173195
}
@@ -178,25 +200,35 @@ $fallbacks: m3-radio.get-tokens();
178200
}
179201

180202
&:checked + .mdc-radio__background {
181-
> .mdc-radio__outer-circle,
203+
$token: 'radio-selected-icon-color';
204+
205+
> .mdc-radio__outer-circle {
206+
border-color: token-utils.slot($token, $fallbacks);
207+
}
208+
182209
> .mdc-radio__inner-circle {
183-
border-color: token-utils.slot(radio-selected-icon-color, $fallbacks);
210+
background-color: token-utils.slot($token, $fallbacks, currentColor);
184211
}
185212
}
186213

187214
@if ($is-interactive) {
188215
&:focus:checked + .mdc-radio__background {
189-
> .mdc-radio__inner-circle,
216+
$token: 'radio-selected-focus-icon-color';
217+
190218
> .mdc-radio__outer-circle {
191-
border-color: token-utils.slot(radio-selected-focus-icon-color, $fallbacks);
219+
border-color: token-utils.slot($token, $fallbacks);
220+
}
221+
222+
> .mdc-radio__inner-circle {
223+
background-color: token-utils.slot($token, $fallbacks, currentColor);
192224
}
193225
}
194226
}
195227
}
196228

197229
&:checked + .mdc-radio__background > .mdc-radio__inner-circle {
198230
transform: scale(0.5);
199-
transition: _enter-transition(transform), _enter-transition(border-color);
231+
transition: _enter-transition(transform), _enter-transition(background-color);
200232
}
201233
}
202234

@@ -215,10 +247,17 @@ $fallbacks: m3-radio.get-tokens();
215247
&:hover .mdc-radio__native-control:checked + .mdc-radio__background,
216248
.mdc-radio__native-control:checked:focus + .mdc-radio__background,
217249
.mdc-radio__native-control + .mdc-radio__background {
218-
> .mdc-radio__inner-circle,
250+
$color-token: 'radio-disabled-selected-icon-color';
251+
$opacity-token: token-utils.slot(radio-disabled-selected-icon-opacity, $fallbacks);
252+
219253
> .mdc-radio__outer-circle {
220-
border-color: token-utils.slot(radio-disabled-selected-icon-color, $fallbacks);
221-
opacity: token-utils.slot(radio-disabled-selected-icon-opacity, $fallbacks);
254+
border-color: token-utils.slot($color-token, $fallbacks);
255+
opacity: $opacity-token;
256+
}
257+
258+
> .mdc-radio__inner-circle {
259+
background-color: token-utils.slot($color-token, $fallbacks, currentColor);
260+
opacity: $opacity-token;
222261
}
223262
}
224263
}

0 commit comments

Comments
 (0)