Skip to content

Commit c2b363e

Browse files
kseamonandrewseguin
authored andcommittedFeb 13, 2025·
perf(material/radio): Optimize costly css selectors. (#30470)
Descendant selectors containing :state selectors as ancestors are slow as they require the css engine to check all the way up to the dom root for matching elements. Using child selectors instead limits the scope of these checks to just the direct parent element. (cherry picked from commit 6bd31f9)
1 parent c24b179 commit c2b363e

File tree

2 files changed

+31
-28
lines changed

2 files changed

+31
-28
lines changed
 

‎src/material/radio/_radio-common.scss

+28-25
Original file line numberDiff line numberDiff line change
@@ -35,41 +35,41 @@ $_icon-size: 20px;
3535

3636
@if ($is-interactive) {
3737
// MDC's hover indication comes from their ripple which we don't use.
38-
&:hover .mdc-radio__native-control:not([disabled]):not(:focus) {
38+
&:hover > .mdc-radio__native-control:not([disabled]):not(:focus) {
3939
& ~ .mdc-radio__background::before {
4040
opacity: 0.04;
4141
transform: scale(1);
4242
}
4343
}
4444

45-
&:hover .mdc-radio__native-control:not([disabled]) ~ .mdc-radio__background {
46-
.mdc-radio__outer-circle {
45+
&:hover > .mdc-radio__native-control:not([disabled]) ~ .mdc-radio__background {
46+
> .mdc-radio__outer-circle {
4747
@include token-utils.use-tokens($tokens...) {
4848
@include token-utils.create-token-slot(border-color, unselected-hover-icon-color);
4949
}
5050
}
5151
}
5252

53-
&:hover .mdc-radio__native-control:enabled:checked + .mdc-radio__background {
54-
.mdc-radio__outer-circle,
55-
.mdc-radio__inner-circle {
53+
&:hover > .mdc-radio__native-control:enabled:checked + .mdc-radio__background {
54+
> .mdc-radio__outer-circle,
55+
> .mdc-radio__inner-circle {
5656
@include token-utils.use-tokens($tokens...) {
5757
@include token-utils.create-token-slot(border-color, selected-hover-icon-color);
5858
}
5959
}
6060
}
6161

62-
&:active .mdc-radio__native-control:enabled:not(:checked) + .mdc-radio__background {
63-
.mdc-radio__outer-circle {
62+
&:active > .mdc-radio__native-control:enabled:not(:checked) + .mdc-radio__background {
63+
> .mdc-radio__outer-circle {
6464
@include token-utils.use-tokens($tokens...) {
6565
@include token-utils.create-token-slot(border-color, unselected-pressed-icon-color);
6666
}
6767
}
6868
}
6969

70-
&:active .mdc-radio__native-control:enabled:checked + .mdc-radio__background {
71-
.mdc-radio__outer-circle,
72-
.mdc-radio__inner-circle {
70+
&:active > .mdc-radio__native-control:enabled:checked + .mdc-radio__background {
71+
> .mdc-radio__outer-circle,
72+
> .mdc-radio__inner-circle {
7373
@include token-utils.use-tokens($tokens...) {
7474
@include token-utils.create-token-slot(border-color, selected-pressed-icon-color);
7575
}
@@ -152,11 +152,11 @@ $_icon-size: 20px;
152152
+ .mdc-radio__background {
153153
transition: _enter-transition(opacity), _enter-transition(transform);
154154

155-
.mdc-radio__outer-circle {
155+
> .mdc-radio__outer-circle {
156156
transition: _enter-transition(border-color);
157157
}
158158

159-
.mdc-radio__inner-circle {
159+
> .mdc-radio__inner-circle {
160160
transition: _enter-transition(transform), _enter-transition(border-color);
161161
}
162162
}
@@ -172,16 +172,16 @@ $_icon-size: 20px;
172172

173173
&:disabled {
174174
@include token-utils.use-tokens($tokens...) {
175-
&:not(:checked) + .mdc-radio__background .mdc-radio__outer-circle {
175+
&:not(:checked) + .mdc-radio__background > .mdc-radio__outer-circle {
176176
@include token-utils.create-token-slot(border-color, disabled-unselected-icon-color);
177177
@include token-utils.create-token-slot(opacity, disabled-unselected-icon-opacity);
178178
}
179179

180180
+ .mdc-radio__background {
181181
cursor: default;
182182

183-
.mdc-radio__inner-circle,
184-
.mdc-radio__outer-circle {
183+
> .mdc-radio__inner-circle,
184+
> .mdc-radio__outer-circle {
185185
@include token-utils.create-token-slot(border-color, disabled-selected-icon-color);
186186
@include token-utils.create-token-slot(opacity, disabled-selected-icon-opacity);
187187
}
@@ -191,29 +191,29 @@ $_icon-size: 20px;
191191

192192
&:enabled {
193193
@include token-utils.use-tokens($tokens...) {
194-
&:not(:checked) + .mdc-radio__background .mdc-radio__outer-circle {
194+
&:not(:checked) + .mdc-radio__background > .mdc-radio__outer-circle {
195195
@include token-utils.create-token-slot(border-color, unselected-icon-color);
196196
}
197197

198198
&:checked + .mdc-radio__background {
199-
.mdc-radio__outer-circle,
200-
.mdc-radio__inner-circle {
199+
> .mdc-radio__outer-circle,
200+
> .mdc-radio__inner-circle {
201201
@include token-utils.create-token-slot(border-color, selected-icon-color);
202202
}
203203
}
204204

205205
@if ($is-interactive) {
206206
&:focus:checked + .mdc-radio__background {
207-
.mdc-radio__inner-circle,
208-
.mdc-radio__outer-circle {
207+
> .mdc-radio__inner-circle,
208+
> .mdc-radio__outer-circle {
209209
@include token-utils.create-token-slot(border-color, selected-focus-icon-color);
210210
}
211211
}
212212
}
213213
}
214214
}
215215

216-
&:checked + .mdc-radio__background .mdc-radio__inner-circle {
216+
&:checked + .mdc-radio__background > .mdc-radio__inner-circle {
217217
transform: scale(0.5);
218218
transition: _enter-transition(transform), _enter-transition(border-color);
219219
}
@@ -224,16 +224,19 @@ $_icon-size: 20px;
224224
pointer-events: auto;
225225

226226
@include token-utils.use-tokens($tokens...) {
227-
.mdc-radio__native-control:not(:checked) + .mdc-radio__background .mdc-radio__outer-circle {
227+
// stylelint-disable selector-combinator-space-before
228+
.mdc-radio__native-control:not(:checked) + .mdc-radio__background
229+
> .mdc-radio__outer-circle {
228230
@include token-utils.create-token-slot(border-color, disabled-unselected-icon-color);
229231
@include token-utils.create-token-slot(opacity, disabled-unselected-icon-opacity);
230232
}
233+
// stylelint-enable selector-combinator-space-before
231234

232235
&:hover .mdc-radio__native-control:checked + .mdc-radio__background,
233236
.mdc-radio__native-control:checked:focus + .mdc-radio__background,
234237
.mdc-radio__native-control + .mdc-radio__background {
235-
.mdc-radio__inner-circle,
236-
.mdc-radio__outer-circle {
238+
> .mdc-radio__inner-circle,
239+
> .mdc-radio__outer-circle {
237240
@include token-utils.create-token-slot(border-color, disabled-selected-icon-color);
238241
@include token-utils.create-token-slot(opacity, disabled-selected-icon-opacity);
239242
}

‎src/material/radio/radio.scss

+3-3
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
pointer-events: none;
5151
border-radius: 50%;
5252

53-
.mat-ripple-element {
53+
> .mat-ripple-element {
5454
opacity: 0.14;
5555
}
5656

@@ -62,8 +62,8 @@
6262
// We don't inherit the border focus style from MDC since we don't use their ripple.
6363
// Instead we need to replicate it here.
6464
@include token-utils.use-tokens(tokens-mdc-radio.$prefix, tokens-mdc-radio.get-token-slots()) {
65-
.mdc-radio .mdc-radio__native-control:focus:enabled:not(:checked) {
66-
& ~ .mdc-radio__background .mdc-radio__outer-circle {
65+
.mdc-radio > .mdc-radio__native-control:focus:enabled:not(:checked) {
66+
& ~ .mdc-radio__background > .mdc-radio__outer-circle {
6767
@include token-utils.create-token-slot(border-color, unselected-focus-icon-color);
6868
}
6969
}

0 commit comments

Comments
 (0)
Please sign in to comment.