Skip to content

Commit 739b841

Browse files
committed
fix(material/badge): move structural styles out of theme (#28452)
Historically we've had the structural styles for the badge in the theme, because Angular doesn't support adding styles to a directive. This can lead to duplicate CSS and can be problematic for M3. These changes move the styles into a dummy component that is used to load the style only once per app. (cherry picked from commit 23e2f3e)
1 parent 7de0f61 commit 739b841

File tree

8 files changed

+208
-178
lines changed

8 files changed

+208
-178
lines changed

src/material/badge/BUILD.bazel

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ load(
44
"ng_module",
55
"ng_test_library",
66
"ng_web_test_suite",
7+
"sass_binary",
78
"sass_library",
89
)
910

@@ -15,6 +16,9 @@ ng_module(
1516
["**/*.ts"],
1617
exclude = ["**/*.spec.ts"],
1718
),
19+
assets = [
20+
":badge_scss",
21+
] + glob(["**/*.html"]),
1822
deps = [
1923
"//src:dev_mode_types",
2024
"//src/cdk/a11y",
@@ -23,7 +27,16 @@ ng_module(
2327
"@npm//@angular/common",
2428
"@npm//@angular/core",
2529
"@npm//@angular/platform-browser",
26-
] + glob(["**/*.html"]),
30+
],
31+
)
32+
33+
sass_binary(
34+
name = "badge_scss",
35+
src = "badge.scss",
36+
deps = [
37+
"//src/cdk:sass_lib",
38+
"//src/material/core:core_scss_lib",
39+
],
2740
)
2841

2942
sass_library(

src/material/badge/_badge-theme.scss

Lines changed: 0 additions & 169 deletions
Original file line numberDiff line numberDiff line change
@@ -1,169 +1,12 @@
11
@use 'sass:color';
22
@use 'sass:map';
3-
@use 'sass:math';
4-
@use '@angular/cdk';
53
@use '../core/theming/theming';
64
@use '../core/theming/inspection';
75
@use '../core/typography/typography';
86
@use '../core/tokens/m2/mat/badge' as tokens-mat-badge;
97
@use '../core/tokens/token-utils';
108
@use '../core/style/sass-utils';
119

12-
// TODO(crisbeto): some of these variables aren't used anymore and should be deleted.
13-
$font-size: 12px;
14-
$font-weight: 600;
15-
$default-size: 22px !default;
16-
$small-size: $default-size - 6;
17-
$large-size: $default-size + 6;
18-
$_badge-structure-emitted: false !default;
19-
20-
// Internally there are some builds that throw an error if they can't figure out the values
21-
// of CSS variables during compliation. This flag temporarily enables fallbacks for these builds.
22-
// Eventually we should clean them up.
23-
$_emit-fallback-vars: true;
24-
25-
// Mixin for building offset given different sizes
26-
@mixin _badge-size($size, $font-size-token) {
27-
// This mixin isn't used in the context of a theme so we can disable the ampersand check.
28-
// stylelint-disable material/no-ampersand-beyond-selector-start
29-
.mat-badge-content {
30-
width: $size;
31-
height: $size;
32-
line-height: $size;
33-
34-
@if ($font-size-token) {
35-
@include token-utils.use-tokens(tokens-mat-badge.$prefix,
36-
tokens-mat-badge.get-token-slots()) {
37-
@include token-utils.create-token-slot(font-size, $font-size-token, $_emit-fallback-vars);
38-
}
39-
}
40-
}
41-
42-
&.mat-badge-above .mat-badge-content {
43-
top: math.div(-$size, 2);
44-
}
45-
46-
&.mat-badge-below .mat-badge-content {
47-
bottom: math.div(-$size, 2);
48-
}
49-
50-
&.mat-badge-before .mat-badge-content {
51-
left: -$size;
52-
}
53-
54-
[dir='rtl'] &.mat-badge-before .mat-badge-content {
55-
left: auto;
56-
right: -$size;
57-
}
58-
59-
&.mat-badge-after .mat-badge-content {
60-
right: -$size;
61-
}
62-
63-
[dir='rtl'] &.mat-badge-after .mat-badge-content {
64-
right: auto;
65-
left: -$size;
66-
}
67-
68-
&.mat-badge-overlap {
69-
&.mat-badge-before .mat-badge-content {
70-
left: math.div(-$size, 2);
71-
}
72-
73-
[dir='rtl'] &.mat-badge-before .mat-badge-content {
74-
left: auto;
75-
right: math.div(-$size, 2);
76-
}
77-
78-
&.mat-badge-after .mat-badge-content {
79-
right: math.div(-$size, 2);
80-
}
81-
82-
[dir='rtl'] &.mat-badge-after .mat-badge-content {
83-
right: auto;
84-
left: math.div(-$size, 2);
85-
}
86-
}
87-
// stylelint-enable
88-
}
89-
90-
// Structural styles for the badge. They have to be included as a part of the theme,
91-
// because the badge is a directive and we have no other way of attaching styles to it.
92-
@mixin _badge-structure {
93-
.mat-badge {
94-
position: relative;
95-
96-
// The badge should make sure its host is overflow visible so that the badge content
97-
// can be rendered outside of the element. Some components such as <mat-icon> explicitly
98-
// style `overflow: hidden` so this requires extra specificity so that it does not
99-
// depend on style load order.
100-
&.mat-badge {
101-
overflow: visible;
102-
}
103-
}
104-
105-
.mat-badge-content {
106-
position: absolute;
107-
text-align: center;
108-
display: inline-block;
109-
border-radius: 50%;
110-
transition: transform 200ms ease-in-out;
111-
transform: scale(0.6);
112-
overflow: hidden;
113-
white-space: nowrap;
114-
text-overflow: ellipsis;
115-
pointer-events: none;
116-
117-
@include token-utils.use-tokens(tokens-mat-badge.$prefix, tokens-mat-badge.get-token-slots()) {
118-
@include token-utils.create-token-slot(background-color, background-color);
119-
@include token-utils.create-token-slot(color, text-color);
120-
@include token-utils.create-token-slot(font-family, text-font, $_emit-fallback-vars);
121-
@include token-utils.create-token-slot(font-size, text-size, $_emit-fallback-vars);
122-
@include token-utils.create-token-slot(font-weight, text-weight, $_emit-fallback-vars);
123-
}
124-
125-
@include cdk.high-contrast(active, off) {
126-
outline: solid 1px;
127-
border-radius: 0;
128-
}
129-
}
130-
131-
.mat-badge-disabled .mat-badge-content {
132-
@include token-utils.use-tokens(tokens-mat-badge.$prefix, tokens-mat-badge.get-token-slots()) {
133-
@include token-utils.create-token-slot(background-color, disabled-state-background-color);
134-
@include token-utils.create-token-slot(color, disabled-state-text-color);
135-
}
136-
}
137-
138-
.mat-badge-hidden .mat-badge-content {
139-
display: none;
140-
}
141-
142-
.ng-animate-disabled .mat-badge-content,
143-
.mat-badge-content._mat-animation-noopable {
144-
transition: none;
145-
}
146-
147-
// The active class is added after the element is added
148-
// so it can animate scale to default
149-
.mat-badge-content.mat-badge-active {
150-
// Scale to `none` instead of `1` to avoid blurry text in some browsers.
151-
transform: none;
152-
}
153-
154-
.mat-badge-small {
155-
@include _badge-size($small-size, small-size-text-size);
156-
}
157-
158-
.mat-badge-medium {
159-
@include _badge-size($default-size, null);
160-
}
161-
162-
.mat-badge-large {
163-
@include _badge-size($large-size, large-size-text-size);
164-
}
165-
}
166-
16710
@mixin base($theme) {
16811
@if inspection.get-theme-version($theme) == 1 {
16912
@include _theme-from-tokens(inspection.get-theme-tokens($theme, base));
@@ -218,18 +61,6 @@ $_emit-fallback-vars: true;
21861
@include _theme-from-tokens(inspection.get-theme-tokens($theme));
21962
}
22063
@else {
221-
// Try to reduce the number of times that the structural styles are emitted.
222-
@if not $_badge-structure-emitted {
223-
@include _badge-structure;
224-
225-
// Only flip the flag if the mixin is included at the top level. Otherwise the first
226-
// inclusion might be inside of a theme class which will exclude the structural styles
227-
// from all other themes.
228-
@if not & {
229-
$_badge-structure-emitted: true !global;
230-
}
231-
}
232-
23364
@include base($theme);
23465
@if inspection.theme-has($theme, color) {
23566
@include color($theme);

src/material/badge/badge-module.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
import {NgModule} from '@angular/core';
1010
import {MatCommonModule} from '@angular/material/core';
1111
import {A11yModule} from '@angular/cdk/a11y';
12-
import {MatBadge} from './badge';
12+
import {MatBadge, _MatBadgeStyleLoader} from './badge';
1313

1414
@NgModule({
15-
imports: [A11yModule, MatCommonModule, MatBadge],
15+
// Note: we _shouldn't_ have to import `_MatBadgeStyleLoader`,
16+
// but it seems to be necessary for tests.
17+
imports: [A11yModule, MatCommonModule, MatBadge, _MatBadgeStyleLoader],
1618
exports: [MatBadge, MatCommonModule],
1719
})
1820
export class MatBadgeModule {}

src/material/badge/badge.scss

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
@use 'sass:color';
2+
@use 'sass:math';
3+
@use '@angular/cdk';
4+
@use '../core/tokens/m2/mat/badge' as tokens-mat-badge;
5+
@use '../core/tokens/token-utils';
6+
7+
$default-size: 22px !default;
8+
$small-size: $default-size - 6;
9+
$large-size: $default-size + 6;
10+
11+
12+
// Mixin for building offset given different sizes
13+
@mixin _badge-size($size, $font-size-token) {
14+
.mat-badge-content {
15+
width: $size;
16+
height: $size;
17+
line-height: $size;
18+
19+
@if ($font-size-token) {
20+
@include token-utils.use-tokens(tokens-mat-badge.$prefix,
21+
tokens-mat-badge.get-token-slots()) {
22+
@include token-utils.create-token-slot(font-size, $font-size-token);
23+
}
24+
}
25+
}
26+
27+
&.mat-badge-above .mat-badge-content {
28+
top: math.div(-$size, 2);
29+
}
30+
31+
&.mat-badge-below .mat-badge-content {
32+
bottom: math.div(-$size, 2);
33+
}
34+
35+
&.mat-badge-before .mat-badge-content {
36+
left: -$size;
37+
}
38+
39+
[dir='rtl'] &.mat-badge-before .mat-badge-content {
40+
left: auto;
41+
right: -$size;
42+
}
43+
44+
&.mat-badge-after .mat-badge-content {
45+
right: -$size;
46+
}
47+
48+
[dir='rtl'] &.mat-badge-after .mat-badge-content {
49+
right: auto;
50+
left: -$size;
51+
}
52+
53+
&.mat-badge-overlap {
54+
&.mat-badge-before .mat-badge-content {
55+
left: math.div(-$size, 2);
56+
}
57+
58+
[dir='rtl'] &.mat-badge-before .mat-badge-content {
59+
left: auto;
60+
right: math.div(-$size, 2);
61+
}
62+
63+
&.mat-badge-after .mat-badge-content {
64+
right: math.div(-$size, 2);
65+
}
66+
67+
[dir='rtl'] &.mat-badge-after .mat-badge-content {
68+
right: auto;
69+
left: math.div(-$size, 2);
70+
}
71+
}
72+
}
73+
74+
.mat-badge {
75+
position: relative;
76+
77+
// The badge should make sure its host is overflow visible so that the badge content
78+
// can be rendered outside of the element. Some components such as <mat-icon> explicitly
79+
// style `overflow: hidden` so this requires extra specificity so that it does not
80+
// depend on style load order.
81+
&.mat-badge {
82+
overflow: visible;
83+
}
84+
}
85+
86+
.mat-badge-content {
87+
position: absolute;
88+
text-align: center;
89+
display: inline-block;
90+
border-radius: 50%;
91+
transition: transform 200ms ease-in-out;
92+
transform: scale(0.6);
93+
overflow: hidden;
94+
white-space: nowrap;
95+
text-overflow: ellipsis;
96+
pointer-events: none;
97+
98+
@include token-utils.use-tokens(tokens-mat-badge.$prefix, tokens-mat-badge.get-token-slots()) {
99+
@include token-utils.create-token-slot(background-color, background-color);
100+
@include token-utils.create-token-slot(color, text-color);
101+
@include token-utils.create-token-slot(font-family, text-font);
102+
@include token-utils.create-token-slot(font-size, text-size);
103+
@include token-utils.create-token-slot(font-weight, text-weight);
104+
}
105+
106+
@include cdk.high-contrast(active, off) {
107+
outline: solid 1px;
108+
border-radius: 0;
109+
}
110+
}
111+
112+
.mat-badge-disabled .mat-badge-content {
113+
@include token-utils.use-tokens(tokens-mat-badge.$prefix, tokens-mat-badge.get-token-slots()) {
114+
@include token-utils.create-token-slot(background-color, disabled-state-background-color);
115+
@include token-utils.create-token-slot(color, disabled-state-text-color);
116+
}
117+
}
118+
119+
.mat-badge-hidden .mat-badge-content {
120+
display: none;
121+
}
122+
123+
.ng-animate-disabled .mat-badge-content,
124+
.mat-badge-content._mat-animation-noopable {
125+
transition: none;
126+
}
127+
128+
// The active class is added after the element is added
129+
// so it can animate scale to default
130+
.mat-badge-content.mat-badge-active {
131+
// Scale to `none` instead of `1` to avoid blurry text in some browsers.
132+
transform: none;
133+
}
134+
135+
.mat-badge-small {
136+
@include _badge-size($small-size, small-size-text-size);
137+
}
138+
139+
.mat-badge-medium {
140+
@include _badge-size($default-size, null);
141+
}
142+
143+
.mat-badge-large {
144+
@include _badge-size($large-size, large-size-text-size);
145+
}

0 commit comments

Comments
 (0)