Skip to content

Commit 3400f36

Browse files
asynclizcopybara-github
authored andcommitted
feat(labs): add icon button utility class component
PiperOrigin-RevId: 900897488
1 parent 5df2633 commit 3400f36

6 files changed

Lines changed: 999 additions & 0 deletions

File tree

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
//
2+
// Copyright 2026 Google LLC
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
6+
@mixin root {
7+
--container-color: transparent;
8+
--container-height: auto;
9+
--container-shape: calc(var(--container-height) / 2);
10+
--icon-color: currentColor;
11+
--icon-size: 24px;
12+
--outline-color: transparent;
13+
--outline-width: 1px;
14+
--leading-space: 0;
15+
--trailing-space: 0;
16+
}
17+
18+
@mixin selected {
19+
--container-shape: var(--md-sys-shape-corner-md);
20+
--icon-color: var(--md-sys-color-primary);
21+
}
22+
23+
@mixin filled {
24+
--container-color: var(--md-sys-color-primary);
25+
--icon-color: var(--md-sys-color-on-primary);
26+
}
27+
28+
@mixin filled-unselected {
29+
--container-color: var(--md-sys-color-surface-container);
30+
--icon-color: var(--md-sys-color-on-surface-variant);
31+
}
32+
33+
@mixin filled-selected {
34+
--container-color: var(--md-sys-color-primary);
35+
--icon-color: var(--md-sys-color-on-primary);
36+
}
37+
38+
@mixin tonal {
39+
--container-color: var(--md-sys-color-secondary-container);
40+
--icon-color: var(--md-sys-color-on-secondary-container);
41+
}
42+
43+
@mixin tonal-selected {
44+
--container-color: var(--md-sys-color-secondary);
45+
--icon-color: var(--md-sys-color-on-secondary);
46+
}
47+
48+
@mixin outlined {
49+
--icon-color: var(--md-sys-color-on-surface-variant);
50+
--outline-color: var(--md-sys-color-outline-variant);
51+
}
52+
53+
@mixin outlined-selected {
54+
--container-color: var(--md-sys-color-inverse-surface);
55+
--icon-color: var(--md-sys-color-inverse-on-surface);
56+
}
57+
58+
@mixin standard {
59+
--icon-color: var(--md-sys-color-on-surface-variant);
60+
}
61+
62+
@mixin standard-selected {
63+
--icon-color: var(--md-sys-color-primary);
64+
}
65+
66+
@mixin xs {
67+
--container-height: 32px;
68+
--icon-size: 20px;
69+
--leading-space: 6px;
70+
--trailing-space: 6px;
71+
}
72+
73+
@mixin xs-narrow {
74+
--leading-space: 4px;
75+
--trailing-space: 4px;
76+
}
77+
78+
@mixin xs-wide {
79+
--leading-space: 10px;
80+
--trailing-space: 10px;
81+
}
82+
83+
@mixin sm {
84+
--container-height: 40px;
85+
--leading-space: 8px;
86+
--trailing-space: 8px;
87+
}
88+
89+
@mixin sm-narrow {
90+
--leading-space: 4px;
91+
--trailing-space: 4px;
92+
}
93+
94+
@mixin sm-wide {
95+
--leading-space: 14px;
96+
--trailing-space: 14px;
97+
}
98+
99+
@mixin md {
100+
--container-height: 56px;
101+
--leading-space: 16px;
102+
--trailing-space: 16px;
103+
}
104+
105+
@mixin md-narrow {
106+
--leading-space: 12px;
107+
--trailing-space: 12px;
108+
}
109+
110+
@mixin md-wide {
111+
--leading-space: 24px;
112+
--trailing-space: 24px;
113+
}
114+
115+
@mixin md-selected {
116+
--container-shape: var(--md-sys-shape-corner-lg);
117+
}
118+
119+
@mixin lg {
120+
--container-height: 96px;
121+
--icon-size: 32px;
122+
--leading-space: 32px;
123+
--trailing-space: 32px;
124+
}
125+
126+
@mixin lg-narrow {
127+
--leading-space: 16px;
128+
--trailing-space: 16px;
129+
}
130+
131+
@mixin lg-wide {
132+
--leading-space: 48px;
133+
--outline-width: 2px;
134+
--trailing-space: 48px;
135+
}
136+
137+
@mixin lg-selected {
138+
--container-shape: var(--md-sys-shape-corner-xl);
139+
}
140+
141+
@mixin xl {
142+
--container-height: 136px;
143+
--icon-size: 40px;
144+
--leading-space: 48px;
145+
--outline-width: 3px;
146+
--trailing-space: 48px;
147+
}
148+
149+
@mixin xl-narrow {
150+
--leading-space: 32px;
151+
--trailing-space: 32px;
152+
}
153+
154+
@mixin xl-wide {
155+
--leading-space: 72px;
156+
--trailing-space: 72px;
157+
}
158+
159+
@mixin xl-selected {
160+
--container-shape: var(--md-sys-shape-corner-xl);
161+
}
162+
163+
@mixin square {
164+
--container-shape: var(--md-sys-shape-corner-md);
165+
}
166+
167+
@mixin square-md {
168+
--container-shape: var(--md-sys-shape-corner-lg);
169+
}
170+
171+
@mixin square-lg {
172+
--container-shape: var(--md-sys-shape-corner-xl);
173+
}
174+
175+
@mixin square-xl {
176+
--container-shape: var(--md-sys-shape-corner-xl);
177+
}
178+
179+
@mixin square-selected {
180+
--container-shape: calc(var(--container-height) / 2);
181+
}
182+
183+
@mixin pressed {
184+
--container-shape: var(--md-sys-shape-corner-sm);
185+
}
186+
187+
@mixin pressed-md {
188+
--container-shape: var(--md-sys-shape-corner-md);
189+
}
190+
191+
@mixin pressed-lg {
192+
--container-shape: var(--md-sys-shape-corner-lg);
193+
}
194+
195+
@mixin pressed-xl {
196+
--container-shape: var(--md-sys-shape-corner-lg);
197+
}
198+
199+
@mixin disabled {
200+
--container-color: hsl(from var(--md-sys-color-on-surface) h s l / 10%);
201+
--icon-color: hsl(from var(--md-sys-color-on-surface) h s l / 38%);
202+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/**
2+
* @license
3+
* Copyright 2026 Google LLC
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
import './material-collection.js';
8+
import './index.js';
9+
10+
import {
11+
KnobTypesToKnobs,
12+
MaterialCollection,
13+
materialInitsToStoryInits,
14+
setUpDemo,
15+
} from './material-collection.js';
16+
import {
17+
IconButtonColor,
18+
IconButtonSize,
19+
IconButtonWidth,
20+
} from '@material/web/labs/gb/components/iconbutton/icon-button.js';
21+
import {
22+
boolInput,
23+
Knob,
24+
selectDropdown,
25+
textInput,
26+
} from './index.js';
27+
28+
import {stories, StoryKnobs} from './stories.js';
29+
30+
const collection = new MaterialCollection<KnobTypesToKnobs<StoryKnobs>>(
31+
'Icon Button',
32+
[
33+
new Knob('icon', {
34+
ui: textInput(),
35+
defaultValue: 'add',
36+
}),
37+
new Knob('color', {
38+
ui: selectDropdown<IconButtonColor>({
39+
options: [
40+
{value: 'standard', label: 'Standard'},
41+
{value: 'filled', label: 'Filled'},
42+
{value: 'tonal', label: 'Tonal'},
43+
{value: 'outlined', label: 'Outlined'},
44+
],
45+
}),
46+
defaultValue: 'filled' as const,
47+
}),
48+
new Knob('size', {
49+
ui: selectDropdown<IconButtonSize>({
50+
options: [
51+
{value: 'xs', label: 'X-Small'},
52+
{value: 'sm', label: 'Small'},
53+
{value: 'md', label: 'Medium'},
54+
{value: 'lg', label: 'Large'},
55+
{value: 'xl', label: 'X-Large'},
56+
],
57+
}),
58+
defaultValue: 'sm' as const,
59+
}),
60+
new Knob('width', {
61+
ui: selectDropdown<IconButtonWidth>({
62+
options: [
63+
{value: '', label: 'Default'},
64+
{value: 'narrow', label: 'Narrow'},
65+
{value: 'wide', label: 'Wide'},
66+
],
67+
}),
68+
defaultValue: '' as const,
69+
}),
70+
new Knob('square', {ui: boolInput()}),
71+
new Knob('disabled', {ui: boolInput()}),
72+
new Knob('softDisabled', {ui: boolInput()}),
73+
new Knob('toggle', {ui: boolInput()}),
74+
],
75+
);
76+
77+
collection.addStories(...materialInitsToStoryInits(stories));
78+
79+
setUpDemo(collection, {fonts: 'roboto', icons: 'material-symbols'});

0 commit comments

Comments
 (0)