Skip to content

Commit fd8f7f6

Browse files
authored
refactor(material/badge): switch to tokens theming API (#27492)
Reworks the badge to use the tokens-based theming API.
1 parent 89ade6a commit fd8f7f6

File tree

2 files changed

+162
-118
lines changed

2 files changed

+162
-118
lines changed

src/material/badge/_badge-theme.scss

Lines changed: 84 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,87 @@
1-
// This contains all of the styles for the badge
2-
// rather than just the color/theme because of
3-
// no style sheet support for directives.
41
@use 'sass:color';
52
@use 'sass:map';
6-
@use 'sass:meta';
73
@use 'sass:math';
84
@use '@angular/cdk';
95

106
@use '../core/theming/theming';
117
@use '../core/typography/typography';
12-
@use '../core/typography/typography-utils';
8+
@use '../core/tokens/m2/mat/badge' as tokens-mat-badge;
9+
@use '../core/tokens/token-utils';
10+
@use '../core/style/sass-utils';
1311

12+
// TODO(crisbeto): some of these variables aren't used anymore and should be deleted.
1413
$font-size: 12px;
1514
$font-weight: 600;
1615
$default-size: 22px !default;
1716
$small-size: $default-size - 6;
1817
$large-size: $default-size + 6;
1918
$_badge-structure-emitted: false !default;
2019

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+
2125
// Mixin for building offset given different sizes
22-
@mixin _badge-size($size) {
26+
@mixin _badge-size($size, $font-size-token) {
2327
// This mixin isn't used in the context of a theme so we can disable the ampersand check.
2428
// stylelint-disable material/no-ampersand-beyond-selector-start
2529
.mat-badge-content {
2630
width: $size;
2731
height: $size;
2832
line-height: $size;
29-
}
3033

31-
&.mat-badge-above {
32-
.mat-badge-content {
33-
top: math.div(-$size, 2);
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+
}
3439
}
3540
}
3641

37-
&.mat-badge-below {
38-
.mat-badge-content {
39-
bottom: math.div(-$size, 2);
40-
}
42+
&.mat-badge-above .mat-badge-content {
43+
top: math.div(-$size, 2);
4144
}
4245

43-
&.mat-badge-before {
44-
.mat-badge-content {
45-
left: -$size;
46-
}
46+
&.mat-badge-below .mat-badge-content {
47+
bottom: math.div(-$size, 2);
4748
}
4849

49-
[dir='rtl'] &.mat-badge-before {
50-
.mat-badge-content {
51-
left: auto;
52-
right: -$size;
53-
}
50+
&.mat-badge-before .mat-badge-content {
51+
left: -$size;
5452
}
5553

56-
&.mat-badge-after {
57-
.mat-badge-content {
58-
right: -$size;
59-
}
54+
[dir='rtl'] &.mat-badge-before .mat-badge-content {
55+
left: auto;
56+
right: -$size;
6057
}
6158

62-
[dir='rtl'] &.mat-badge-after {
63-
.mat-badge-content {
64-
right: auto;
65-
left: -$size;
66-
}
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;
6766
}
6867

6968
&.mat-badge-overlap {
70-
&.mat-badge-before {
71-
.mat-badge-content {
72-
left: math.div(-$size, 2);
73-
}
69+
&.mat-badge-before .mat-badge-content {
70+
left: math.div(-$size, 2);
7471
}
7572

76-
[dir='rtl'] &.mat-badge-before {
77-
.mat-badge-content {
78-
left: auto;
79-
right: math.div(-$size, 2);
80-
}
73+
[dir='rtl'] &.mat-badge-before .mat-badge-content {
74+
left: auto;
75+
right: math.div(-$size, 2);
8176
}
8277

83-
&.mat-badge-after {
84-
.mat-badge-content {
85-
right: math.div(-$size, 2);
86-
}
78+
&.mat-badge-after .mat-badge-content {
79+
right: math.div(-$size, 2);
8780
}
8881

89-
[dir='rtl'] &.mat-badge-after {
90-
.mat-badge-content {
91-
right: auto;
92-
left: math.div(-$size, 2);
93-
}
82+
[dir='rtl'] &.mat-badge-after .mat-badge-content {
83+
right: auto;
84+
left: math.div(-$size, 2);
9485
}
9586
}
9687
// stylelint-enable
@@ -101,19 +92,13 @@ $_badge-structure-emitted: false !default;
10192
@mixin _badge-structure {
10293
.mat-badge {
10394
position: relative;
104-
}
10595

106-
// The badge should make sure its host is overflow visible so that the badge content
107-
// can be rendered outside of the element. Some components such as <mat-icon> explicitly
108-
// style `overflow: hidden` so this requires extra specificity so that it does not
109-
// depend on style load order.
110-
.mat-badge.mat-badge {
111-
overflow: visible;
112-
}
113-
114-
.mat-badge-hidden {
115-
.mat-badge-content {
116-
display: none;
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;
117102
}
118103
}
119104

@@ -128,6 +113,30 @@ $_badge-structure-emitted: false !default;
128113
white-space: nowrap;
129114
text-overflow: ellipsis;
130115
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;
131140
}
132141

133142
.ng-animate-disabled .mat-badge-content,
@@ -143,89 +152,46 @@ $_badge-structure-emitted: false !default;
143152
}
144153

145154
.mat-badge-small {
146-
@include _badge-size($small-size);
155+
@include _badge-size($small-size, small-size-text-size);
147156
}
148157

149158
.mat-badge-medium {
150-
@include _badge-size($default-size);
159+
@include _badge-size($default-size, null);
151160
}
152161

153162
.mat-badge-large {
154-
@include _badge-size($large-size);
163+
@include _badge-size($large-size, large-size-text-size);
155164
}
156165
}
157166

158167
@mixin color($config-or-theme) {
159168
$config: theming.get-color-config($config-or-theme);
160169
$accent: map.get($config, accent);
161170
$warn: map.get($config, warn);
162-
$primary: map.get($config, primary);
163-
$background: map.get($config, background);
164-
$foreground: map.get($config, foreground);
165171

166-
.mat-badge-content {
167-
color: theming.get-color-from-palette($primary, default-contrast);
168-
background: theming.get-color-from-palette($primary);
169-
170-
@include cdk.high-contrast(active, off) {
171-
outline: solid 1px;
172-
border-radius: 0;
173-
}
172+
@include sass-utils.current-selector-or-root() {
173+
@include token-utils.create-token-values(tokens-mat-badge.$prefix,
174+
tokens-mat-badge.get-color-tokens($config));
174175
}
175176

176177
.mat-badge-accent {
177-
.mat-badge-content {
178-
background: theming.get-color-from-palette($accent);
179-
color: theming.get-color-from-palette($accent, default-contrast);
180-
}
178+
@include token-utils.create-token-values(tokens-mat-badge.$prefix,
179+
tokens-mat-badge.private-get-color-palette-color-tokens($accent));
181180
}
182181

183182
.mat-badge-warn {
184-
.mat-badge-content {
185-
color: theming.get-color-from-palette($warn, default-contrast);
186-
background: theming.get-color-from-palette($warn);
187-
}
188-
}
189-
190-
.mat-badge-disabled {
191-
.mat-badge-content {
192-
$app-background: theming.get-color-from-palette($background, 'background');
193-
$badge-color: theming.get-color-from-palette($foreground, disabled-button);
194-
195-
// The disabled color usually has some kind of opacity, but because the badge is overlayed
196-
// on top of something else, it won't look good if it's opaque. If it is a color *type*,
197-
// we convert it into a solid color by taking the opacity from the rgba value and using
198-
// the value to determine the percentage of the background to put into foreground when
199-
// mixing the colors together.
200-
@if (meta.type-of($badge-color) == color and meta.type-of($app-background) == color) {
201-
$badge-opacity: opacity($badge-color);
202-
background: color.mix($app-background, rgba($badge-color, 1), (1 - $badge-opacity) * 100%);
203-
}
204-
@else {
205-
background: $badge-color;
206-
}
207-
208-
color: theming.get-color-from-palette($foreground, disabled-text);
209-
}
183+
@include token-utils.create-token-values(tokens-mat-badge.$prefix,
184+
tokens-mat-badge.private-get-color-palette-color-tokens($warn));
210185
}
211186
}
212187

213188
@mixin typography($config-or-theme) {
214189
$config: typography.private-typography-to-2014-config(
215190
theming.get-typography-config($config-or-theme));
216-
.mat-badge-content {
217-
font-weight: $font-weight;
218-
font-size: $font-size;
219-
font-family: typography-utils.font-family($config);
220-
}
221-
222-
.mat-badge-small .mat-badge-content {
223-
// Set the font size to 75% of the original.
224-
font-size: $font-size * 0.75;
225-
}
226191

227-
.mat-badge-large .mat-badge-content {
228-
font-size: $font-size * 2;
192+
@include sass-utils.current-selector-or-root() {
193+
@include token-utils.create-token-values(tokens-mat-badge.$prefix,
194+
tokens-mat-badge.get-typography-tokens($config));
229195
}
230196
}
231197

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
@use 'sass:meta';
2+
@use 'sass:map';
3+
@use 'sass:color';
4+
@use '../../token-utils';
5+
@use '../../../typography/typography-utils';
6+
@use '../../../theming/theming';
7+
@use '../../../style/sass-utils';
8+
9+
// The prefix used to generate the fully qualified name for tokens in this file.
10+
$prefix: (mat, badge);
11+
12+
// Tokens that can't be configured through Angular Material's current theming API,
13+
// but may be in a future version of the theming API.
14+
@function get-unthemable-tokens() {
15+
@return ();
16+
}
17+
18+
// Tokens that can be configured through Angular Material's color theming API.
19+
@function get-color-tokens($config) {
20+
$foreground: map.get($config, foreground);
21+
$background: map.get($config, background);
22+
$primary-color-tokens: private-get-color-palette-color-tokens(map.get($config, primary));
23+
$app-background: theming.get-color-from-palette($background, 'background');
24+
$disabled-background: theming.get-color-from-palette($foreground, disabled-button);
25+
26+
// The disabled color usually has some kind of opacity, but because the badge is overlayed
27+
// on top of something else, it won't look good if it's opaque. If it is a color *type*,
28+
// we convert it into a solid color by taking the opacity from the rgba value and using
29+
// the value to determine the percentage of the background to put into foreground when
30+
// mixing the colors together.
31+
@if (meta.type-of($disabled-background) == color and meta.type-of($app-background) == color) {
32+
$badge-opacity: opacity($disabled-background);
33+
$disabled-background: color.mix($app-background,
34+
rgba($disabled-background, 1), (1 - $badge-opacity) * 100%);
35+
}
36+
37+
@return map.merge($primary-color-tokens, (
38+
disabled-state-background-color: $disabled-background,
39+
disabled-state-text-color: theming.get-color-from-palette($foreground, disabled-text),
40+
));
41+
}
42+
43+
// Generates the tokens used to theme the badge based on a palette.
44+
@function private-get-color-palette-color-tokens($palette) {
45+
@return (
46+
background-color: theming.get-color-from-palette($palette),
47+
text-color: theming.get-color-from-palette($palette, default-contrast),
48+
);
49+
}
50+
51+
// Tokens that can be configured through Angular Material's typography theming API.
52+
@function get-typography-tokens($config) {
53+
$base-size: 12px;
54+
55+
@return (
56+
text-font: typography-utils.font-family($config),
57+
text-size: $base-size,
58+
text-weight: 600,
59+
small-size-text-size: $base-size * 0.75,
60+
large-size-text-size: $base-size * 2,
61+
);
62+
}
63+
64+
// Tokens that can be configured through Angular Material's density theming API.
65+
@function get-density-tokens($config) {
66+
@return ();
67+
}
68+
69+
// Combines the tokens generated by the above functions into a single map with placeholder values.
70+
// This is used to create token slots.
71+
@function get-token-slots() {
72+
@return sass-utils.deep-merge-all(
73+
get-unthemable-tokens(),
74+
get-color-tokens(token-utils.$placeholder-color-config),
75+
get-typography-tokens(token-utils.$placeholder-typography-config),
76+
get-density-tokens(token-utils.$placeholder-density-config)
77+
);
78+
}

0 commit comments

Comments
 (0)