Skip to content

Commit 5b13b5c

Browse files
asyncLizcopybara-github
authored andcommittedAug 29, 2023
fix(tabs)!: split md-tab into md-primary-tab and md-secondary-tab
BREAKING CHANGE: remove `variant` attributes and change `md-tab` to `md-primary-tab`, or `md-secondary-tab` if using `variant="secondary" PiperOrigin-RevId: 561077231
1 parent 5ba348d commit 5b13b5c

25 files changed

+749
-556
lines changed
 

‎all.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ import './select/outlined-select.js';
5050
import './select/select-option.js';
5151
import './slider/slider.js';
5252
import './switch/switch.js';
53-
import './tabs/tab.js';
53+
import './tabs/primary-tab.js';
54+
import './tabs/secondary-tab.js';
5455
import './tabs/tabs.js';
5556
import './textfield/filled-text-field.js';
5657
import './textfield/outlined-text-field.js';
@@ -96,7 +97,8 @@ export * from './select/outlined-select.js';
9697
export * from './select/select-option.js';
9798
export * from './slider/slider.js';
9899
export * from './switch/switch.js';
99-
export * from './tabs/tab.js';
100+
export * from './tabs/primary-tab.js';
101+
export * from './tabs/secondary-tab.js';
100102
export * from './tabs/tabs.js';
101103
export * from './textfield/filled-text-field.js';
102104
export * from './textfield/outlined-text-field.js';

‎catalog/src/ssr.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ import '@material/web/list/list.js';
1818
import '@material/web/list/list-item.js';
1919
import '@material/web/progress/circular-progress.js';
2020
import '@material/web/tabs/tabs.js';
21-
import '@material/web/tabs/tab.js';
21+
import '@material/web/tabs/primary-tab.js';
22+
import '@material/web/tabs/secondary-tab.js';
2223
import '@material/web/iconbutton/icon-button.js';
2324
import '@material/web/iconbutton/filled-icon-button.js';
2425
import '@material/web/iconbutton/filled-tonal-icon-button.js';

‎common.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import './progress/linear-progress.js';
3434
import './radio/radio.js';
3535
import './select/outlined-select.js';
3636
import './select/select-option.js';
37-
import './tabs/tab.js';
37+
import './tabs/primary-tab.js';
3838
import './tabs/tabs.js';
3939
import './textfield/outlined-text-field.js';
4040
// go/keep-sorted end
@@ -61,7 +61,7 @@ export * from './progress/linear-progress.js';
6161
export * from './radio/radio.js';
6262
export * from './select/outlined-select.js';
6363
export * from './select/select-option.js';
64-
export * from './tabs/tab.js';
64+
export * from './tabs/primary-tab.js';
6565
export * from './tabs/tabs.js';
6666
export * from './textfield/outlined-text-field.js';
6767
// go/keep-sorted end

‎docs/components/tabs.md

+140-46
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ dirname: tabs
1212

1313
<!--*
1414
# Document freshness: For more information, see go/fresh-source.
15-
freshness: { owner: 'ajakubowicz' reviewed: '2023-08-01' }
15+
freshness: {
16+
owner: 'lizmitchell'
17+
owner: 'ajakubowicz'
18+
reviewed: '2023-08-25'
19+
}
1620
tag: 'docType:reference'
1721
*-->
1822

@@ -44,6 +48,11 @@ related content that are at the same level of hierarchy.
4448
* [Source code](https://github.com/material-components/material-web/tree/main/tabs)
4549
<!-- {.external} -->
4650

51+
## Types
52+
53+
1. [Primary tabs](#primary-tabs)
54+
1. [Secondary tabs](#secondary-tabs)
55+
4756
<!-- catalog-only-start -->
4857

4958
<!--
@@ -58,44 +67,30 @@ related content that are at the same level of hierarchy.
5867

5968
## Usage
6069

61-
There are two tabs variants: `primary` (default), and `secondary`.
62-
63-
Tabs consist of a parent `<md-tabs>` tag, containing multiple `<md-tab>`
64-
children.
70+
Tabs contain multiple primary or secondary tab children. Use the same type of
71+
tab in a tab bar.
6572

66-
![A primary and secondary variant tab bar stacked on each other. Primar tabs
67-
show: "Keyboard" and "Guitar". Secondary tabs are: "Travel", "Hotel", and
68-
"Activities".](images/tabs/usage.png "Primary and Secondary tabs")
73+
<!-- no-catalog-start -->
74+
<!-- TODO: add image -->
75+
<!-- no-catalog-end -->
76+
<!-- TODO: catalog-include "figures/<component>/usage.html" -->
6977

7078
```html
7179
<md-tabs>
72-
<md-tab>
73-
<md-icon aria-hidden="true" slot="icon">piano</md-icon>
74-
Keyboard
75-
</md-tab>
76-
<md-tab>
77-
<md-icon aria-hidden="true" slot="icon">tune</md-icon>
78-
Guitar
79-
</md-tab>
80+
<md-primary-tab>Video</md-primary-tab>
81+
<md-primary-tab>Photos</md-primary-tab>
82+
<md-primary-tab>Audio</md-primary-tab>
8083
</md-tabs>
8184

82-
83-
<md-tabs variant="secondary">
84-
<md-tab inline-icon>
85-
<md-icon aria-hidden="true" slot="icon">flight</md-icon>
86-
Travel
87-
</md-tab>
88-
<md-tab inline-icon>
89-
<md-icon aria-hidden="true" slot="icon">hotel</md-icon>
90-
Hotel
91-
</md-tab>
92-
<md-tab inline-icon>
93-
<md-icon aria-hidden="true" slot="icon">hiking</md-icon>
94-
Activities
95-
</md-tab>
85+
<md-tabs>
86+
<md-secondary-tab>Birds</md-secondary-tab>
87+
<md-secondary-tab>Cats</md-secondary-tab>
88+
<md-secondary-tab>Dogs</md-secondary-tab>
9689
</md-tabs>
9790
```
9891

92+
### Selection
93+
9994
To observe changes to tab selections, add an event listener to `<md-tabs>`,
10095
listening for the `change` event.
10196

@@ -107,20 +102,82 @@ tabs.addEventListener('change', (event: Event) => {
107102
});
108103
```
109104

110-
### Scrollable
105+
### Icons
106+
107+
Tabs may optionally show an icon.
108+
109+
Icons communicate the type of content within a tab. Icons should be simple and
110+
recognizable.
111+
112+
<!-- no-catalog-start -->
113+
<!-- TODO: add image -->
114+
<!-- no-catalog-end -->
115+
<!-- TODO: catalog-include "figures/<component>/usage.html" -->
116+
117+
```html
118+
<md-tabs>
119+
<md-primary-tab>
120+
<md-icon slot="icon">piano</md-icon>
121+
Keyboard
122+
</md-primary-tab>
123+
<md-primary-tab>
124+
<md-icon slot="icon">tune</md-icon>
125+
Guitar
126+
</md-primary-tab>
127+
</md-tabs>
128+
```
129+
130+
## Primary tabs
111131

112-
When a set of tabs cannot fit on screen or container, use scrollable tabs.
113-
Scrollable tabs can use longer text labels and a larger number of tabs. They are
114-
best used for browsing on touch interfaces.
132+
<!-- go/md-primary-tab -->
115133

116-
![Tabs truncated horizontally showing "Tab 1", "Tab 2", "Tab 3", and "Ta".](images/tabs/scrollable.png)
134+
Primary tabs are placed at the top of the content pane under a top app bar. They
135+
display the main content destinations.
136+
137+
<!-- no-catalog-start -->
138+
<!-- TODO: add image -->
139+
<!-- no-catalog-end -->
140+
<!-- TODO: catalog-include "figures/<component>/usage.html" -->
117141

118142
```html
119143
<md-tabs>
120-
<md-tab>Tab 1</md-tab>
121-
<md-tab>Tab 2</md-tab>
122-
<md-tab>Tab 3</md-tab>
123-
<md-tab>Tab 4</md-tab>
144+
<md-primary-tab>
145+
<md-icon slot="icon">piano</md-icon>
146+
Keyboard
147+
</md-primary-tab>
148+
<md-primary-tab>
149+
<md-icon slot="icon">tune</md-icon>
150+
Guitar
151+
</md-primary-tab>
152+
</md-tabs>
153+
```
154+
155+
## Secondary tabs
156+
157+
<!-- go/md-secondary-tab -->
158+
159+
Secondary tabs are used within a content area to further separate related
160+
content and establish hierarchy.
161+
162+
<!-- no-catalog-start -->
163+
<!-- TODO: add image -->
164+
<!-- no-catalog-end -->
165+
<!-- TODO: catalog-include "figures/<component>/usage.html" -->
166+
167+
```html
168+
<md-tabs>
169+
<md-secondary-tab inline-icon>
170+
<md-icon slot="icon">flight</md-icon>
171+
Travel
172+
</md-secondary-tab>
173+
<md-secondary-tab inline-icon>
174+
<md-icon slot="icon">hotel</md-icon>
175+
Hotel
176+
</md-secondary-tab>
177+
<md-secondary-tab inline-icon>
178+
<md-icon slot="icon">hiking</md-icon>
179+
Activities
180+
</md-secondary-tab>
124181
</md-tabs>
125182
```
126183

@@ -131,21 +188,20 @@ best used for browsing on touch interfaces.
131188
Tabs supports [Material theming](../theming.md) and can be customized in terms
132189
of color, typography, and shape.
133190

134-
### Tokens
191+
### Primary tab tokens
135192

136193
Token | Default value
137194
----------------------------------------- | -----------------------------------
138195
`--md-primary-tab-container-color` | `--md-sys-color-surface`
139-
`--md-secondary-tab-container-color` | `--md-sys-color-surface`
140196
`--md-primary-tab-label-text-type` | `500 0.875rem/1.25rem Roboto`
141197
`--md-primary-tab-active-indicator-color` | `--md-sys-color-primary`
142198
`--md-primary-tab-icon-color` | `--md-sys-color-on-surface-variant`
143199
`--md-primary-tab-container-shape` | `0px`
144200

145-
* [All tokens](https://github.com/material-components/material-web/blob/main/tokens/_md-comp-tab.scss)
201+
* [All tokens](https://github.com/material-components/material-web/blob/main/tokens/_md-comp-primary-tab.scss)
146202
<!-- {.external} -->
147203

148-
### Example
204+
### Primary tab example
149205

150206
<!-- no-catalog-start -->
151207

@@ -166,8 +222,46 @@ Token | Default value
166222
</style>
167223

168224
<md-tabs>
169-
<md-tab>Tab 1</md-tab>
170-
<md-tab>Tab 2</md-tab>
171-
<md-tab>Tab 3</md-tab>
225+
<md-primary-tab>Tab 1</md-primary-tab>
226+
<md-primary-tab>Tab 2</md-primary-tab>
227+
<md-primary-tab>Tab 3</md-primary-tab>
228+
</md-tabs>
229+
```
230+
231+
### Secondary tab tokens
232+
233+
Token | Default value
234+
------------------------------------------- | -------------
235+
`--md-secondary-tab-container-color` | `--md-sys-color-surface`
236+
`--md-secondary-tab-label-text-type` | `500 0.875rem/1.25rem Roboto`
237+
`--md-secondary-tab-active-indicator-color` | `--md-sys-color-primary`
238+
`--md-secondary-tab-icon-color` | `--md-sys-color-on-surface-variant`
239+
`--md-secondary-tab-container-shape` | `0px`
240+
241+
* [All tokens](https://github.com/material-components/material-web/blob/main/tokens/_md-comp-secondary-tab.scss)
242+
<!-- {.external} -->
243+
244+
### Secondary tab example
245+
246+
<!-- no-catalog-start -->
247+
<!-- TODO: add image -->
248+
<!-- no-catalog-end -->
249+
250+
```html
251+
<style>
252+
:root {
253+
/* System tokens */
254+
--md-sys-color-surface: #f7faf9;
255+
--md-sys-color-primary: #005353;
256+
257+
/* Component tokens */
258+
--md-secondary-tab-label-text-type: 0.8em cursive, system-ui;
259+
}
260+
</style>
261+
262+
<md-tabs>
263+
<md-secondary-tab>Tab 1</md-secondary-tab>
264+
<md-secondary-tab>Tab 2</md-secondary-tab>
265+
<md-secondary-tab>Tab 3</md-secondary-tab>
172266
</md-tabs>
173267
```

‎tabs/_tab.scss ‎tabs/_primary-tab.scss

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
// SPDX-License-Identifier: Apache-2.0
44
//
55

6-
@forward './internal/tab' show theme;
6+
@forward './internal/primary-tab' show theme;

‎tabs/_secondary-tab.scss

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
//
2+
// Copyright 2023 Google LLC
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
6+
@forward './internal/secondary-tab' show theme;

‎tabs/demo/stories.ts

+63-70
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import '@material/web/icon/icon.js';
88
import '@material/web/iconbutton/icon-button.js';
99
import '@material/web/tabs/tabs.js';
10+
import '@material/web/tabs/primary-tab.js';
11+
import '@material/web/tabs/secondary-tab.js';
1012

1113
import {MaterialStoryInit} from './material-collection.js';
1214
import {MdTabs} from '@material/web/tabs/tabs.js';
@@ -53,25 +55,24 @@ const primary: MaterialStoryInit<StoryKnobs> = {
5355

5456
return html`
5557
<md-tabs
56-
variant="primary"
5758
.selected=${knobs.selected}
5859
.selectOnFocus=${knobs.selectOnFocus}
5960
>
60-
<md-tab .inlineIcon=${inlineIcon}>
61+
<md-primary-tab .inlineIcon=${inlineIcon}>
6162
${tabContent('piano', 'Keyboard')}
62-
</md-tab>
63-
<md-tab .inlineIcon=${inlineIcon}>
63+
</md-primary-tab>
64+
<md-primary-tab .inlineIcon=${inlineIcon}>
6465
${tabContent('tune', 'Guitar')}
65-
</md-tab>
66-
<md-tab .inlineIcon=${inlineIcon}>
66+
</md-primary-tab>
67+
<md-primary-tab .inlineIcon=${inlineIcon}>
6768
${tabContent('graphic_eq', 'Drums')}
68-
</md-tab>
69-
<md-tab .inlineIcon=${inlineIcon}>
69+
</md-primary-tab>
70+
<md-primary-tab .inlineIcon=${inlineIcon}>
7071
${tabContent('speaker', 'Bass')}
71-
</md-tab>
72-
<md-tab .inlineIcon=${inlineIcon}>
72+
</md-primary-tab>
73+
<md-primary-tab .inlineIcon=${inlineIcon}>
7374
${tabContent('nightlife', 'Saxophone')}
74-
</md-tab>
75+
</md-primary-tab>
7576
</md-tabs>`;
7677
}
7778
};
@@ -85,22 +86,21 @@ const secondary: MaterialStoryInit<StoryKnobs> = {
8586

8687
return html`
8788
<md-tabs
88-
variant="secondary"
8989
.selected=${knobs.selected}
9090
.selectOnFocus=${knobs.selectOnFocus}
9191
>
92-
<md-tab .inlineIcon=${inlineIcon}>
92+
<md-secondary-tab .inlineIcon=${inlineIcon}>
9393
${tabContent('flight', 'Travel')}
94-
</md-tab>
95-
<md-tab .inlineIcon=${inlineIcon}>
94+
</md-secondary-tab>
95+
<md-secondary-tab .inlineIcon=${inlineIcon}>
9696
${tabContent('hotel', 'Hotel')}
97-
</md-tab>
98-
<md-tab .inlineIcon=${inlineIcon}>
97+
</md-secondary-tab>
98+
<md-secondary-tab .inlineIcon=${inlineIcon}>
9999
${tabContent('hiking', 'Activities')}
100-
</md-tab>
101-
<md-tab .inlineIcon=${inlineIcon}>
100+
</md-secondary-tab>
101+
<md-secondary-tab .inlineIcon=${inlineIcon}>
102102
${tabContent('restaurant', 'Food')}
103-
</md-tab>
103+
</md-secondary-tab>
104104
</md-tabs>`;
105105
}
106106
};
@@ -115,26 +115,25 @@ const scrolling: MaterialStoryInit<StoryKnobs> = {
115115
return html`
116116
<md-tabs
117117
class="scrolling"
118-
variant="primary"
119118
.selected=${knobs.selected}
120119
.selectOnFocus=${knobs.selectOnFocus}
121120
>
122121
${new Array(10).fill(html`
123-
<md-tab .inlineIcon=${inlineIcon}>
122+
<md-primary-tab .inlineIcon=${inlineIcon}>
124123
${tabContent('piano', 'Keyboard')}
125-
</md-tab>
126-
<md-tab .inlineIcon=${inlineIcon}>
124+
</md-primary-tab>
125+
<md-primary-tab .inlineIcon=${inlineIcon}>
127126
${tabContent('tune', 'Guitar')}
128-
</md-tab>
129-
<md-tab .inlineIcon=${inlineIcon}>
127+
</md-primary-tab>
128+
<md-primary-tab .inlineIcon=${inlineIcon}>
130129
${tabContent('graphic_eq', 'Drums')}
131-
</md-tab>
132-
<md-tab .inlineIcon=${inlineIcon}>
130+
</md-primary-tab>
131+
<md-primary-tab .inlineIcon=${inlineIcon}>
133132
${tabContent('speaker', 'Bass')}
134-
</md-tab>
135-
<md-tab .inlineIcon=${inlineIcon}>
133+
</md-primary-tab>
134+
<md-primary-tab .inlineIcon=${inlineIcon}>
136135
${tabContent('nightlife', 'Saxophone')}
137-
</md-tab>
136+
</md-primary-tab>
138137
`)}
139138
</md-tabs>`;
140139
}
@@ -177,22 +176,21 @@ const custom: MaterialStoryInit<StoryKnobs> = {
177176
return html`
178177
<md-tabs
179178
class="custom"
180-
variant="primary"
181179
.selected=${knobs.selected}
182180
.selectOnFocus=${knobs.selectOnFocus}
183181
>
184-
<md-tab .inlineIcon=${inlineIcon}>
182+
<md-primary-tab .inlineIcon=${inlineIcon}>
185183
${tabContent('flight', 'Travel')}
186-
</md-tab>
187-
<md-tab .inlineIcon=${inlineIcon}>
184+
</md-primary-tab>
185+
<md-primary-tab .inlineIcon=${inlineIcon}>
188186
${tabContent('hotel', 'Hotel')}
189-
</md-tab>
190-
<md-tab .inlineIcon=${inlineIcon}>
187+
</md-primary-tab>
188+
<md-primary-tab .inlineIcon=${inlineIcon}>
191189
${tabContent('hiking', 'Activities')}
192-
</md-tab>
193-
<md-tab .inlineIcon=${inlineIcon}>
190+
</md-primary-tab>
191+
<md-primary-tab .inlineIcon=${inlineIcon}>
194192
${tabContent('restaurant', 'Food')}
195-
</md-tab>
193+
</md-primary-tab>
196194
</md-tabs>`;
197195
}
198196
};
@@ -229,58 +227,54 @@ const primaryAndSecondary: MaterialStoryInit<StoryKnobs> = {
229227
return html`
230228
<div>
231229
<md-tabs
232-
variant="primary"
233230
.selected=${knobs.selected}
234231
.selectOnFocus=${knobs.selectOnFocus}
235232
@change=${handlePrimaryTabsChange}
236233
>
237-
<md-tab .inlineIcon=${inlineIcon}>
234+
<md-primary-tab .inlineIcon=${inlineIcon}>
238235
${tabContent('videocam', 'Movies')}
239-
</md-tab>
240-
<md-tab .inlineIcon=${inlineIcon}>
236+
</md-primary-tab>
237+
<md-primary-tab .inlineIcon=${inlineIcon}>
241238
${tabContent('photo', 'Photos')}
242-
</md-tab>
243-
<md-tab .inlineIcon=${inlineIcon}>
239+
</md-primary-tab>
240+
<md-primary-tab .inlineIcon=${inlineIcon}>
244241
${tabContent('audiotrack', 'Music')}
245-
</md-tab>
242+
</md-primary-tab>
246243
</md-tabs>
247244
<div>
248245
<md-tabs
249-
variant="secondary"
250246
.selected=${knobs.selected}
251247
.selectOnFocus=${knobs.selectOnFocus}
252248
@change=${handleSecondaryTabsChange}
253249
>
254-
<md-tab >Star Wars</md-tab>
255-
<md-tab>Avengers</md-tab>
256-
<md-tab>Jaws</md-tab>
257-
<md-tab>Frozen</md-tab>
250+
<md-secondary-tab>Star Wars</md-secondary-tab>
251+
<md-secondary-tab>Avengers</md-secondary-tab>
252+
<md-secondary-tab>Jaws</md-secondary-tab>
253+
<md-secondary-tab>Frozen</md-secondary-tab>
258254
</md-tabs>
259255
<div class="content"></div>
260256
<md-tabs
261257
hidden
262-
variant="secondary"
263258
.selected=${knobs.selected}
264259
.selectOnFocus=${knobs.selectOnFocus}
265260
@change=${handleSecondaryTabsChange}
266261
>
267-
<md-tab>Yosemite</md-tab>
268-
<md-tab>Mona Lisa</md-tab>
269-
<md-tab>Swiss Alps</md-tab>
270-
<md-tab>Niagra Falls</md-tab>
262+
<md-secondary-tab>Yosemite</md-secondary-tab>
263+
<md-secondary-tab>Mona Lisa</md-secondary-tab>
264+
<md-secondary-tab>Swiss Alps</md-secondary-tab>
265+
<md-secondary-tab>Niagra Falls</md-secondary-tab>
271266
</md-tabs>
272267
<div hidden class="content"></div>
273268
<md-tabs
274269
hidden
275-
variant="secondary"
276270
.selected=${knobs.selected}
277271
.selectOnFocus=${knobs.selectOnFocus}
278272
@change=${handleSecondaryTabsChange}
279273
>
280-
<md-tab>Rock</md-tab>
281-
<md-tab>Ambient</md-tab>
282-
<md-tab>Soundscapes</md-tab>
283-
<md-tab>White Noise</md-tab>
274+
<md-secondary-tab>Rock</md-secondary-tab>
275+
<md-secondary-tab>Ambient</md-secondary-tab>
276+
<md-secondary-tab>Soundscapes</md-secondary-tab>
277+
<md-secondary-tab>White Noise</md-secondary-tab>
284278
</md-tabs>
285279
<div hidden class="content"></div>
286280
</div>
@@ -303,7 +297,7 @@ const dynamic: MaterialStoryInit<StoryKnobs> = {
303297
function addTab(event: Event) {
304298
const tabs = getTabs(event);
305299
const count = tabs.childElementCount;
306-
const tab = document.createElement('md-tab');
300+
const tab = document.createElement('md-primary-tab');
307301
tab.textContent = `Tab ${count + 1}`;
308302
if (tabs.selectedItem !== undefined) {
309303
tabs.selectedItem.after(tab);
@@ -353,19 +347,18 @@ const dynamic: MaterialStoryInit<StoryKnobs> = {
353347
</div>
354348
<md-tabs
355349
class="scrolling"
356-
variant="primary"
357350
.selected=${knobs.selected}
358351
.selectOnFocus=${knobs.selectOnFocus}
359352
>
360-
<md-tab .inlineIcon=${inlineIcon}>
353+
<md-primary-tab .inlineIcon=${inlineIcon}>
361354
Tab 1
362-
</md-tab>
363-
<md-tab .inlineIcon=${inlineIcon}>
355+
</md-primary-tab>
356+
<md-primary-tab .inlineIcon=${inlineIcon}>
364357
Tab 2
365-
</md-tab>
366-
<md-tab .inlineIcon=${inlineIcon}>
358+
</md-primary-tab>
359+
<md-primary-tab .inlineIcon=${inlineIcon}>
367360
Tab 3
368-
</md-tab>
361+
</md-primary-tab>
369362
</md-tabs>`;
370363
}
371364
};

‎tabs/internal/_primary-tab.scss

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//
2+
// Copyright 2023 Google LLC
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
6+
// go/keep-sorted start
7+
@use 'sass:list';
8+
// go/keep-sorted end
9+
// go/keep-sorted start
10+
@use '../../tokens';
11+
// go/keep-sorted end
12+
13+
@mixin theme($tokens) {
14+
$supported-tokens: list.join(
15+
tokens.$md-comp-primary-tab-supported-tokens,
16+
(
17+
'container-shape-start-start',
18+
'container-shape-start-end',
19+
'container-shape-end-end',
20+
'container-shape-end-start'
21+
)
22+
);
23+
24+
@each $token, $value in $tokens {
25+
@if list.index($supported-tokens, $token) == null {
26+
@error 'Token `#{$token}` is not a supported token.';
27+
}
28+
29+
@if $value {
30+
--md-primary-tab-#{$token}: #{$value};
31+
}
32+
}
33+
}
34+
35+
@mixin styles() {
36+
$tokens: tokens.md-comp-primary-tab-values();
37+
38+
:host {
39+
@each $token, $value in $tokens {
40+
--_#{$token}: var(--md-primary-tab-#{$token}, #{$value});
41+
}
42+
43+
// Support logical shape properties
44+
--_container-shape-start-start: var(
45+
--md-primary-tab-container-shape-start-start,
46+
var(--_container-shape)
47+
);
48+
--_container-shape-start-end: var(
49+
--md-primary-tab-container-shape-start-end,
50+
var(--_container-shape)
51+
);
52+
--_container-shape-end-end: var(
53+
--md-primary-tab-container-shape-end-end,
54+
var(--_container-shape)
55+
);
56+
--_container-shape-end-start: var(
57+
--md-primary-tab-container-shape-end-start,
58+
var(--_container-shape)
59+
);
60+
}
61+
}

‎tabs/internal/_secondary-tab.scss

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
//
2+
// Copyright 2023 Google LLC
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
6+
// go/keep-sorted start
7+
@use 'sass:list';
8+
// go/keep-sorted end
9+
// go/keep-sorted start
10+
@use '../../tokens';
11+
// go/keep-sorted end
12+
13+
@mixin theme($tokens) {
14+
$supported-tokens: list.join(
15+
tokens.$md-comp-secondary-tab-supported-tokens,
16+
(
17+
'container-shape-start-start',
18+
'container-shape-start-end',
19+
'container-shape-end-end',
20+
'container-shape-end-start'
21+
)
22+
);
23+
24+
@each $token, $value in $tokens {
25+
@if list.index($supported-tokens, $token) == null {
26+
@error 'Token `#{$token}` is not a supported token.';
27+
}
28+
29+
@if $value {
30+
--md-secondary-tab-#{$token}: #{$value};
31+
}
32+
}
33+
}
34+
35+
@mixin styles() {
36+
$tokens: tokens.md-comp-secondary-tab-values();
37+
38+
:host {
39+
@each $token, $value in $tokens {
40+
--_#{$token}: var(--md-secondary-tab-#{$token}, #{$value});
41+
}
42+
43+
// Support logical shape properties
44+
--_container-shape-start-start: var(
45+
--md-secondary-tab-container-shape-start-start,
46+
var(--_container-shape)
47+
);
48+
--_container-shape-start-end: var(
49+
--md-secondary-tab-container-shape-start-end,
50+
var(--_container-shape)
51+
);
52+
--_container-shape-end-end: var(
53+
--md-secondary-tab-container-shape-end-end,
54+
var(--_container-shape)
55+
);
56+
--_container-shape-end-start: var(
57+
--md-secondary-tab-container-shape-end-start,
58+
var(--_container-shape)
59+
);
60+
}
61+
62+
.content {
63+
width: 100%;
64+
}
65+
66+
.indicator {
67+
min-width: 100%;
68+
}
69+
}

‎tabs/internal/_tab.scss

+2-97
Original file line numberDiff line numberDiff line change
@@ -15,64 +15,8 @@
1515
@use '../../tokens';
1616
// go/keep-sorted end
1717

18-
@mixin theme($tokens) {
19-
$supported-tokens: list.join(
20-
tokens.$md-comp-tab-supported-tokens,
21-
(
22-
'primary-tab-container-shape-start-start',
23-
'primary-tab-container-shape-start-end',
24-
'primary-tab-container-shape-end-end',
25-
'primary-tab-container-shape-end-start',
26-
'secondary-tab-container-shape-start-start',
27-
'secondary-tab-container-shape-start-end',
28-
'secondary-tab-container-shape-end-end',
29-
'secondary-tab-container-shape-end-start'
30-
)
31-
);
32-
33-
@each $token, $value in $tokens {
34-
@if list.index($supported-tokens, $token) == null {
35-
@error 'Token `#{$token}` is not a supported token.';
36-
}
37-
38-
@if $value {
39-
--md-#{$token}: #{$value};
40-
}
41-
}
42-
}
43-
4418
@mixin styles() {
45-
// contains tokens for all variants and applied where needed
46-
$tokens: tokens.md-comp-tab-values();
47-
4819
:host {
49-
// apply primary-tokens by default
50-
$primary-prefix: 'primary-tab-';
51-
@each $token, $value in $tokens {
52-
@if string-ext.has-prefix($token, $primary-prefix) {
53-
$token: string-ext.trim-prefix(#{$token}, $primary-prefix);
54-
--_#{$token}: var(--md-#{$primary-prefix}#{$token}, #{$value});
55-
}
56-
}
57-
58-
// Support logical shape properties
59-
--_container-shape-start-start: var(
60-
--md-primary-tab-container-shape-start-start,
61-
var(--_container-shape)
62-
);
63-
--_container-shape-start-end: var(
64-
--md-primary-tab-container-shape-start-end,
65-
var(--_container-shape)
66-
);
67-
--_container-shape-end-end: var(
68-
--md-primary-tab-container-shape-end-end,
69-
var(--_container-shape)
70-
);
71-
--_container-shape-end-start: var(
72-
--md-primary-tab-container-shape-end-start,
73-
var(--_container-shape)
74-
);
75-
7620
display: inline-flex;
7721
outline: none;
7822
-webkit-tap-highlight-color: transparent;
@@ -264,53 +208,14 @@
264208
color: var(--_active-pressed-icon-color);
265209
}
266210

267-
// secondary
268-
:host([variant~='secondary']) {
269-
// apply secondary-tab tokens
270-
$secondary-prefix: 'secondary-tab-';
271-
@each $token, $value in $tokens {
272-
@if string-ext.has-prefix($token, $secondary-prefix) {
273-
$token: string-ext.trim-prefix(#{$token}, $secondary-prefix);
274-
--_#{$token}: var(--md-#{$secondary-prefix}#{$token}, #{$value});
275-
}
276-
}
277-
278-
// Support logical shape properties
279-
--_container-shape-start-start: var(
280-
--md-secondary-tab-container-shape-start-start,
281-
var(--_container-shape)
282-
);
283-
--_container-shape-start-end: var(
284-
--md-secondary-tab-container-shape-start-end,
285-
var(--_container-shape)
286-
);
287-
--_container-shape-end-end: var(
288-
--md-secondary-tab-container-shape-end-end,
289-
var(--_container-shape)
290-
);
291-
--_container-shape-end-start: var(
292-
--md-secondary-tab-container-shape-end-start,
293-
var(--_container-shape)
294-
);
295-
}
296-
297-
:host([variant~='secondary']) .content {
298-
width: 100%;
299-
}
300-
301-
:host([variant~='secondary']) .indicator {
302-
min-width: 100%;
303-
}
304-
305211
:host,
306212
::slotted(*) {
307213
white-space: nowrap;
308214
}
309215

310216
@media (forced-colors: active) {
311-
:host,
312-
:host([variant]) {
313-
--_active-indicator-color: CanvasText;
217+
.indicator {
218+
background: CanvasText;
314219
}
315220
}
316221
}

‎tabs/internal/primary-tab-styles.scss

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//
2+
// Copyright 2023 Google LLC
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
6+
// go/keep-sorted start
7+
@use './primary-tab';
8+
// go/keep-sorted end
9+
10+
@include primary-tab.styles;

‎tabs/internal/primary-tab.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/**
2+
* @license
3+
* Copyright 2023 Google LLC
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
import {Tab} from './tab.js';
8+
9+
/**
10+
* A primary tab component.
11+
*/
12+
export class PrimaryTab extends Tab {}
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//
2+
// Copyright 2023 Google LLC
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
6+
// go/keep-sorted start
7+
@use './secondary-tab';
8+
// go/keep-sorted end
9+
10+
@include secondary-tab.styles;

‎tabs/internal/secondary-tab.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/**
2+
* @license
3+
* Copyright 2023 Google LLC
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
import {Tab} from './tab.js';
8+
9+
/**
10+
* A secondary tab component.
11+
*/
12+
export class SecondaryTab extends Tab {}

‎tabs/internal/tab.ts

+2-9
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,6 @@ export interface Tabs extends HTMLElement {
2626
previousSelectedItem?: Tab;
2727
}
2828

29-
/**
30-
* Tab variant can be `primary` or `secondary`.
31-
*/
32-
export type TabVariant = 'primary'|'secondary';
33-
3429
/**
3530
* Tab component.
3631
*/
@@ -43,10 +38,8 @@ export class Tab extends LitElement {
4338
static override shadowRootOptions:
4439
ShadowRootInit = {mode: 'open', delegatesFocus: true};
4540

46-
/**
47-
* Styling variant to display, 'primary' (default) or 'secondary'.
48-
*/
49-
@property({reflect: true}) variant: TabVariant = 'primary';
41+
/** @private indicates that the element is a tab for `<md-tabs>` */
42+
static readonly isTab = true;
5043

5144
/**
5245
* Whether or not the tab is `selected`.

‎tabs/internal/tabs.ts

+14-14
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import '../../divider/divider.js';
99
import {html, isServer, LitElement, PropertyValues} from 'lit';
1010
import {property, queryAssignedElements, state} from 'lit/decorators.js';
1111

12-
import {Tab, TabVariant} from './tab.js';
12+
import {Tab} from './tab.js';
1313

1414
const NAVIGATION_KEYS = new Map([
1515
['default', new Set(['Home', 'End'])],
@@ -47,11 +47,6 @@ export class Tabs extends LitElement {
4747
delegatesFocus: true
4848
};
4949

50-
/**
51-
* Styling variant to display, 'primary' (default) or 'secondary'.
52-
*/
53-
@property({reflect: true}) variant: TabVariant = 'primary';
54-
5550
/**
5651
* Index of the selected item.
5752
*/
@@ -66,8 +61,11 @@ export class Tabs extends LitElement {
6661
private previousSelected = -1;
6762
private readonly scrollMargin = 48;
6863

69-
@queryAssignedElements({selector: 'md-tab', flatten: true})
70-
private readonly items!: Tab[];
64+
@queryAssignedElements({flatten: true})
65+
private readonly maybeTabItems!: HTMLElement[];
66+
private get items(): Tab[] {
67+
return this.maybeTabItems.filter(isTab);
68+
}
7169

7270
// this tracks if items have changed, which triggers rendering so they can
7371
// be kept in sync
@@ -211,16 +209,14 @@ export class Tabs extends LitElement {
211209
}
212210

213211
protected override async updated(changed: PropertyValues) {
214-
const itemsOrVariantChanged =
215-
changed.has('itemsDirty') || changed.has('variant');
212+
const itemsChanged = changed.has('itemsDirty');
216213
// sync state with items.
217-
if (itemsOrVariantChanged) {
214+
if (itemsChanged) {
218215
this.items.forEach((item, i) => {
219216
item.selected = this.selected === i;
220-
item.variant = this.variant;
221217
});
222218
}
223-
if (itemsOrVariantChanged || changed.has('selected')) {
219+
if (itemsChanged || changed.has('selected')) {
224220
if (this.previousSelectedItem !== this.selectedItem) {
225221
this.previousSelectedItem?.removeAttribute(this.selectedAttribute);
226222
this.selectedItem?.setAttribute(this.selectedAttribute, '');
@@ -242,7 +238,7 @@ export class Tabs extends LitElement {
242238
return html`
243239
<div class="tabs">
244240
<slot @slotchange=${this.handleSlotChange} @click=${
245-
this.handleItemClick}></slot>
241+
this.handleItemClick}></slot>
246242
</div>
247243
<md-divider part="divider"></md-divider>
248244
`;
@@ -296,3 +292,7 @@ export class Tabs extends LitElement {
296292
this.scrollTo({behavior, top: 0, left: to});
297293
}
298294
}
295+
296+
function isTab(element: HTMLElement): element is Tab {
297+
return 'isTab' in element.constructor && element.constructor.isTab === true;
298+
}

‎tabs/primary-tab.ts

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* @license
3+
* Copyright 2023 Google LLC
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
import {customElement} from 'lit/decorators.js';
8+
9+
import {PrimaryTab} from './internal/primary-tab.js';
10+
import {styles as primaryStyles} from './internal/primary-tab-styles.css.js';
11+
import {styles as sharedStyles} from './internal/tab-styles.css.js';
12+
13+
declare global {
14+
interface HTMLElementTagNameMap {
15+
'md-primary-tab': MdPrimaryTab;
16+
}
17+
}
18+
19+
// TODO(b/267336507): add docs
20+
/**
21+
* @summary Tab allow users to display a tab within a Tabs.
22+
*
23+
*/
24+
@customElement('md-primary-tab')
25+
export class MdPrimaryTab extends PrimaryTab {
26+
static override styles = [sharedStyles, primaryStyles];
27+
}

‎tabs/secondary-tab.ts

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* @license
3+
* Copyright 2023 Google LLC
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
import {customElement} from 'lit/decorators.js';
8+
9+
import {SecondaryTab} from './internal/secondary-tab.js';
10+
import {styles as secondaryStyles} from './internal/secondary-tab-styles.css.js';
11+
import {styles as sharedStyles} from './internal/tab-styles.css.js';
12+
13+
declare global {
14+
interface HTMLElementTagNameMap {
15+
'md-secondary-tab': MdSecondaryTab;
16+
}
17+
}
18+
19+
// TODO(b/267336507): add docs
20+
/**
21+
* @summary Tab allow users to display a tab within a Tabs.
22+
*
23+
*/
24+
@customElement('md-secondary-tab')
25+
export class MdSecondaryTab extends SecondaryTab {
26+
static override styles = [sharedStyles, secondaryStyles];
27+
}

‎tabs/tab.ts

-28
This file was deleted.

‎tabs/tabs.ts

-4
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,11 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7-
import './tab.js';
8-
97
import {customElement} from 'lit/decorators.js';
108

119
import {Tabs} from './internal/tabs.js';
1210
import {styles} from './internal/tabs-styles.css.js';
1311

14-
export {MdTab, TabVariant} from './tab.js';
15-
1612
declare global {
1713
interface HTMLElementTagNameMap {
1814
'md-tabs': MdTabs;

‎tabs/tabs_test.ts

+10-9
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import {Environment} from '../testing/environment.js';
1010
import {createTokenTests} from '../testing/tokens.js';
1111

1212
import {TabsHarness} from './harness.js';
13-
import {MdTab} from './tab.js';
13+
import {MdPrimaryTab} from './primary-tab.js';
14+
import {MdSecondaryTab} from './secondary-tab.js';
1415
import {MdTabs} from './tabs.js';
1516

1617
interface TabsTestProps {
@@ -22,17 +23,16 @@ function getTabsTemplate(props?: TabsTestProps) {
2223
<md-tabs
2324
.selected=${props?.selected ?? 0}
2425
>
25-
<md-tab>A</md-tab>
26-
<md-tab>B</md-tab>
27-
<md-tab>C</md-tab>
26+
<md-primary-tab>A</md-primary-tab>
27+
<md-primary-tab>B</md-primary-tab>
28+
<md-primary-tab>C</md-primary-tab>
2829
</md-tabs>`;
2930
}
3031

3132
describe('<md-tabs>', () => {
3233
const env = new Environment();
3334

34-
async function setupTest(
35-
props?: TabsTestProps, template = getTabsTemplate) {
35+
async function setupTest(props?: TabsTestProps, template = getTabsTemplate) {
3636
const root = env.render(template(props));
3737
await env.waitForStability();
3838
const tab = root.querySelector<MdTabs>('md-tabs')!;
@@ -42,7 +42,8 @@ describe('<md-tabs>', () => {
4242

4343
describe('.styles', () => {
4444
createTokenTests(MdTabs.styles);
45-
createTokenTests(MdTab.styles);
45+
createTokenTests(MdPrimaryTab.styles);
46+
createTokenTests(MdSecondaryTab.styles);
4647
});
4748

4849
describe('properties', () => {
@@ -85,7 +86,7 @@ describe('<md-tabs>', () => {
8586
it('maintains selection when tabs are mutated', async () => {
8687
const {harness} = await setupTest({selected: 1});
8788
expect(harness.element.selectedItem.textContent).toBe('B');
88-
const tab = document.createElement('md-tab');
89+
const tab = document.createElement('md-primary-tab');
8990
tab.textContent = 'tab';
9091
// add before selected
9192
harness.element.prepend(tab);
@@ -105,4 +106,4 @@ describe('<md-tabs>', () => {
105106
expect(harness.element.selectedItem.textContent).toBe('B');
106107
});
107108
});
108-
});
109+
});

‎tokens/_index.scss

+2-1
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,13 @@
4242
md-comp-outlined-segmented-button-*;
4343
@forward './md-comp-outlined-select' as md-comp-outlined-select-*;
4444
@forward './md-comp-outlined-text-field' as md-comp-outlined-text-field-*;
45+
@forward './md-comp-primary-tab' as md-comp-primary-tab-*;
4546
@forward './md-comp-radio' as md-comp-radio-*;
4647
@forward './md-comp-ripple' as md-comp-ripple-*;
48+
@forward './md-comp-secondary-tab' as md-comp-secondary-tab-*;
4749
@forward './md-comp-slider' as md-comp-slider-*;
4850
@forward './md-comp-suggestion-chip' as md-comp-suggestion-chip-*;
4951
@forward './md-comp-switch' as md-comp-switch-*;
50-
@forward './md-comp-tab' as md-comp-tab-*;
5152
@forward './md-comp-test-table' as md-comp-test-table-*;
5253
@forward './md-comp-text-button' as md-comp-text-button-*;
5354
@forward './md-ref-palette' as md-ref-palette-*;

‎tokens/_md-comp-primary-tab.scss

+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
//
2+
// Copyright 2023 Google LLC
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
6+
// go/keep-sorted start
7+
@use 'sass:map';
8+
// go/keep-sorted end
9+
// go/keep-sorted start
10+
@use './md-sys-color';
11+
@use './md-sys-elevation';
12+
@use './md-sys-shape';
13+
@use './md-sys-state';
14+
@use './md-sys-typescale';
15+
@use './v0_172/md-comp-primary-navigation-tab';
16+
@use './values';
17+
// go/keep-sorted end
18+
19+
$_default: (
20+
'md-sys-color': md-sys-color.values-light(),
21+
'md-sys-elevation': md-sys-elevation.values(),
22+
'md-sys-shape': md-sys-shape.values(),
23+
'md-sys-state': md-sys-state.values(),
24+
'md-sys-typescale': md-sys-typescale.values(),
25+
);
26+
27+
$supported-tokens: (
28+
// go/keep-sorted start
29+
'active-focus-icon-color',
30+
'active-focus-label-text-color',
31+
'active-hover-icon-color',
32+
'active-hover-label-text-color',
33+
'active-hover-state-layer-color',
34+
'active-hover-state-layer-opacity',
35+
'active-icon-color',
36+
'active-indicator-color',
37+
'active-indicator-height',
38+
'active-indicator-shape',
39+
'active-label-text-color',
40+
'active-pressed-icon-color',
41+
'active-pressed-label-text-color',
42+
'active-pressed-state-layer-color',
43+
'active-pressed-state-layer-opacity',
44+
'container-color',
45+
'container-elevation',
46+
'container-height',
47+
'container-shape',
48+
'focus-icon-color',
49+
'focus-label-text-color',
50+
'hover-icon-color',
51+
'hover-label-text-color',
52+
'hover-state-layer-color',
53+
'hover-state-layer-opacity',
54+
'icon-color',
55+
'icon-size',
56+
'label-text-color',
57+
'label-text-type',
58+
'pressed-icon-color',
59+
'pressed-label-text-color',
60+
'pressed-state-layer-color',
61+
'pressed-state-layer-opacity',
62+
// go/keep-sorted end
63+
);
64+
65+
$unsupported-tokens: (
66+
// include an icon and the size will adjust;
67+
// height is 48 and it's 64 with icon
68+
'with-icon-and-label-text-container-height',
69+
'with-label-text-label-text-font',
70+
'with-label-text-label-text-line-height',
71+
'with-label-text-label-text-size',
72+
'with-label-text-label-text-tracking',
73+
'with-label-text-label-text-weight',
74+
'active-focus-state-layer-color',
75+
'active-focus-state-layer-opacity',
76+
'focus-state-layer-color',
77+
'focus-state-layer-opacity'
78+
);
79+
80+
@function values($deps: $_default, $exclude-hardcoded-values: false) {
81+
$tokens: md-comp-primary-navigation-tab.values(
82+
$deps,
83+
$exclude-hardcoded-values: $exclude-hardcoded-values
84+
);
85+
86+
// TODO(b/271876162): remove when tokens compiler emits typescale tokens
87+
$tokens: map.merge(
88+
$tokens,
89+
(
90+
'label-text-type': map.get($deps, 'md-sys-typescale', 'title-small'),
91+
)
92+
);
93+
94+
@return values.validate(
95+
$tokens,
96+
$supported-tokens: $supported-tokens,
97+
$unsupported-tokens: $unsupported-tokens,
98+
$renamed-tokens: (
99+
// rename inactive-
100+
'inactive-focus-state-layer-color': 'focus-state-layer-color',
101+
'inactive-focus-state-layer-opacity': 'focus-state-layer-opacity',
102+
'inactive-hover-state-layer-color': 'hover-state-layer-color',
103+
'inactive-hover-state-layer-opacity': 'hover-state-layer-opacity',
104+
'inactive-pressed-state-layer-color': 'pressed-state-layer-color',
105+
'inactive-pressed-state-layer-opacity': 'pressed-state-layer-opacity',
106+
// rename with-icon- and inactive-
107+
'with-icon-active-focus-icon-color': 'active-focus-icon-color',
108+
'with-icon-active-hover-icon-color': 'active-hover-icon-color',
109+
'with-icon-active-icon-color': 'active-icon-color',
110+
'with-icon-active-pressed-icon-color': 'active-pressed-icon-color',
111+
'with-icon-icon-size': 'icon-size',
112+
'with-icon-inactive-focus-icon-color': 'focus-icon-color',
113+
'with-icon-inactive-hover-icon-color': 'hover-icon-color',
114+
'with-icon-inactive-icon-color': 'icon-color',
115+
'with-icon-inactive-pressed-icon-color': 'pressed-icon-color',
116+
// rename with-label-text- and inactive-
117+
'with-label-text-active-focus-label-text-color':
118+
'active-focus-label-text-color',
119+
'with-label-text-active-hover-label-text-color':
120+
'active-hover-label-text-color',
121+
'with-label-text-active-label-text-color': 'active-label-text-color',
122+
'with-label-text-active-pressed-label-text-color':
123+
'active-pressed-label-text-color',
124+
'with-label-text-inactive-focus-label-text-color':
125+
'focus-label-text-color',
126+
'with-label-text-inactive-hover-label-text-color':
127+
'hover-label-text-color',
128+
'with-label-text-inactive-label-text-color': 'label-text-color',
129+
'with-label-text-inactive-pressed-label-text-color':
130+
'pressed-label-text-color',
131+
'with-label-text-label-text-type': 'label-text-type'
132+
)
133+
);
134+
}

‎tokens/_md-comp-secondary-tab.scss

+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
//
2+
// Copyright 2023 Google LLC
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
6+
// go/keep-sorted start
7+
@use 'sass:map';
8+
// go/keep-sorted end
9+
// go/keep-sorted start
10+
@use './md-sys-color';
11+
@use './md-sys-elevation';
12+
@use './md-sys-shape';
13+
@use './md-sys-state';
14+
@use './md-sys-typescale';
15+
@use './v0_172/md-comp-secondary-navigation-tab';
16+
@use './values';
17+
// go/keep-sorted end
18+
19+
$_default: (
20+
'md-sys-color': md-sys-color.values-light(),
21+
'md-sys-elevation': md-sys-elevation.values(),
22+
'md-sys-shape': md-sys-shape.values(),
23+
'md-sys-state': md-sys-state.values(),
24+
'md-sys-typescale': md-sys-typescale.values(),
25+
);
26+
27+
$supported-tokens: (
28+
// go/keep-sorted start
29+
'active-focus-icon-color',
30+
'active-focus-label-text-color',
31+
'active-hover-icon-color',
32+
'active-hover-label-text-color',
33+
'active-hover-state-layer-color',
34+
'active-hover-state-layer-opacity',
35+
'active-icon-color',
36+
'active-indicator-color',
37+
'active-indicator-height',
38+
'active-indicator-shape',
39+
'active-label-text-color',
40+
'active-pressed-icon-color',
41+
'active-pressed-label-text-color',
42+
'active-pressed-state-layer-color',
43+
'active-pressed-state-layer-opacity',
44+
'container-color',
45+
'container-elevation',
46+
'container-height',
47+
'container-shape',
48+
'focus-icon-color',
49+
'focus-label-text-color',
50+
'hover-icon-color',
51+
'hover-label-text-color',
52+
'hover-state-layer-color',
53+
'hover-state-layer-opacity',
54+
'icon-color',
55+
'icon-size',
56+
'label-text-color',
57+
'label-text-type',
58+
'pressed-icon-color',
59+
'pressed-label-text-color',
60+
'pressed-state-layer-color',
61+
'pressed-state-layer-opacity',
62+
// go/keep-sorted end
63+
);
64+
65+
$unsupported-tokens: (
66+
// include an icon and the size will adjust;
67+
// height is 48 and it's 64 with icon
68+
'container-shadow-color',
69+
'label-text-font',
70+
'label-text-line-height',
71+
'label-text-size',
72+
'label-text-tracking',
73+
'label-text-weight',
74+
'focus-state-layer-color',
75+
'focus-state-layer-opacity'
76+
);
77+
78+
@function values($deps: $_default, $exclude-hardcoded-values: false) {
79+
$tokens: md-comp-secondary-navigation-tab.values(
80+
$deps,
81+
$exclude-hardcoded-values: $exclude-hardcoded-values
82+
);
83+
84+
// TODO(b/271876162): remove when tokens compiler emits typescale tokens
85+
$tokens: map.merge(
86+
$tokens,
87+
(
88+
'label-text-type': map.get($deps, 'md-sys-typescale', 'title-small'),
89+
)
90+
);
91+
92+
$tokens: _add-missing-secondary-tokens($tokens);
93+
94+
$tokens: values.validate(
95+
$tokens,
96+
$supported-tokens: $supported-tokens,
97+
$unsupported-tokens: $unsupported-tokens,
98+
$renamed-tokens: (
99+
'inactive-label-text-color': 'label-text-color',
100+
'with-icon-active-icon-color': 'active-icon-color',
101+
'with-icon-focus-icon-color': 'focus-icon-color',
102+
'with-icon-hover-icon-color': 'hover-icon-color',
103+
'with-icon-icon-size': 'icon-size',
104+
'with-icon-inactive-icon-color': 'icon-color',
105+
'with-icon-pressed-icon-color': 'pressed-icon-color',
106+
)
107+
);
108+
109+
@return $tokens;
110+
}
111+
112+
// add missing secondary tokens to match primary variant.
113+
@function _add-missing-secondary-tokens($tokens) {
114+
$tokens: map.merge(
115+
$tokens,
116+
(
117+
'active-focus-icon-color': map.get($tokens, 'icon-color'),
118+
'active-focus-label-text-color':
119+
map.get($tokens, 'active-label-text-color'),
120+
'active-hover-icon-color': map.get($tokens, 'icon-color'),
121+
'active-hover-label-text-color':
122+
map.get($tokens, 'active-label-text-color'),
123+
'active-hover-state-layer-color':
124+
map.get($tokens, 'hover-state-layer-color'),
125+
'active-hover-state-layer-opacity':
126+
map.get($tokens, 'hover-state-layer-opacity'),
127+
'active-icon-color': map.get($tokens, 'icon-color'),
128+
'active-indicator-shape': 0,
129+
'active-pressed-icon-color': map.get($tokens, 'icon-color'),
130+
'active-pressed-label-text-color':
131+
map.get($tokens, 'active-label-text-color'),
132+
'active-pressed-state-layer-color':
133+
map.get($tokens, 'pressed-state-layer-color'),
134+
'active-pressed-state-layer-opacity':
135+
map.get($tokens, 'pressed-state-layer-opacity'),
136+
)
137+
);
138+
@return $tokens;
139+
}

‎tokens/_md-comp-tab.scss

-272
This file was deleted.

0 commit comments

Comments
 (0)
Please sign in to comment.