UNPKG

10.8 kBSCSSView Raw
1//
2// Copyright 2021 Google Inc.
3//
4// Permission is hereby granted, free of charge, to any person obtaining a copy
5// of this software and associated documentation files (the "Software"), to deal
6// in the Software without restriction, including without limitation the rights
7// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8// copies of the Software, and to permit persons to whom the Software is
9// furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20// THE SOFTWARE.
21//
22
23// Selector '.mdc-*' should only be used in this project.
24// stylelint-disable selector-class-pattern
25
26@use 'sass:math';
27@use 'sass:map';
28@use 'sass:meta';
29@use '@material/density/functions' as density-functions;
30@use '@material/density/variables' as density-variables;
31@use '@material/elevation/elevation-theme';
32@use '@material/feature-targeting/feature-targeting';
33@use '@material/ripple/ripple-theme';
34@use '@material/rtl/rtl';
35@use '@material/theme/keys';
36@use '@material/theme/state';
37@use '@material/theme/theme';
38@use '@material/theme/theme-color';
39@use '@material/touch-target/mixins' as touch-target-mixins;
40
41$ripple-target: '.mdc-icon-button__ripple';
42
43$icon-size: 24px !default;
44$size: 48px !default;
45$minimum-height: 28px !default;
46$maximum-height: $size !default;
47$container-shape: 50%;
48$density-scale: density-variables.$default-scale !default;
49$density-config: (
50 size: (
51 default: $size,
52 maximum: $maximum-height,
53 minimum: $minimum-height,
54 ),
55) !default;
56
57$_custom-property-prefix: 'icon-button';
58
59$light-theme: (
60 disabled-icon-color: theme-color.$on-surface,
61 disabled-icon-opacity: 0.38,
62 icon-color: theme-color.$primary,
63 icon-size: $icon-size,
64 focus-icon-color: theme-color.$primary,
65 focus-state-layer-color: theme-color.$primary,
66 focus-state-layer-opacity: 0.12,
67 hover-icon-color: theme-color.$primary,
68 hover-state-layer-color: theme-color.$primary,
69 hover-state-layer-opacity: 0.08,
70 pressed-icon-color: theme-color.$primary,
71 pressed-state-layer-color: theme-color.$primary,
72 pressed-state-layer-opacity: 0.12,
73 state-layer-size: $size,
74);
75
76@mixin theme($theme) {
77 @include theme.validate-theme($light-theme, $theme);
78
79 @include keys.declare-custom-properties(
80 $theme,
81 $prefix: $_custom-property-prefix
82 );
83}
84
85@mixin theme-styles($theme) {
86 @include theme.validate-theme($light-theme, $theme);
87
88 $theme: keys.create-theme-properties(
89 $theme,
90 $prefix: $_custom-property-prefix
91 );
92
93 @include _state-layer-size($size: map.get($theme, state-layer-size));
94 @include _icon-size(map.get($theme, icon-size));
95 @include _disabled-icon-opacity(map.get($theme, disabled-icon-opacity));
96 @include _icon-color-with-map(
97 (
98 default: map.get($theme, icon-color),
99 disabled: map.get($theme, disabled-icon-color),
100 focus: map.get($theme, focus-icon-color),
101 hover: map.get($theme, hover-icon-color),
102 pressed: map.get($theme, pressed-icon-color),
103 )
104 );
105
106 // States styles
107 @include ripple-theme.theme-styles(
108 (
109 focus-state-layer-color: map.get($theme, focus-state-layer-color),
110 focus-state-layer-opacity: map.get($theme, focus-state-layer-opacity),
111 hover-state-layer-color: map.get($theme, hover-state-layer-color),
112 hover-state-layer-opacity: map.get($theme, hover-state-layer-opacity),
113 pressed-state-layer-color: map.get($theme, pressed-state-layer-color),
114 pressed-state-layer-opacity: map.get($theme, pressed-state-layer-opacity),
115 ),
116 $ripple-target: $ripple-target
117 );
118}
119
120///
121/// Sets the density scale for icon button.
122///
123/// @param {Number | String} $density-scale - Density scale value for component.
124/// Supported density scale values range from `-5` to `0`, with `0` being the default.
125///
126@mixin density($density-scale, $query: feature-targeting.all()) {
127 $size: density-functions.prop-value(
128 $density-config: $density-config,
129 $density-scale: $density-scale,
130 $property-name: size,
131 );
132
133 @include size($size, $query: $query);
134}
135
136///
137/// Sets the size of the icon-button.
138///
139/// @param {Number} $size - Size value for icon-button.
140/// Size will set the width, height, and padding for the overall component.
141///
142@mixin size($size, $query: feature-targeting.all()) {
143 $feat-structure: feature-targeting.create-target($query, structure);
144
145 @include feature-targeting.targets($feat-structure) {
146 width: $size;
147 height: $size;
148 padding: math.div($size - $icon-size, 2);
149 }
150
151 &.mdc-icon-button--reduced-size {
152 $component-size: $size;
153 // Icon button ripple size is capped at 40px for icon buttons with densities
154 // -1 and 0 (icon buttons with sizes 44x44 and 48x48px).
155 // See http://b/192353968 for more info.
156 @if $size >= 40px and $size <= 48px {
157 $component-size: 40px;
158 }
159
160 @include feature-targeting.targets($feat-structure) {
161 width: $component-size;
162 height: $component-size;
163 padding: math.div($component-size - $icon-size, 2);
164 }
165
166 &.mdc-icon-button--touch {
167 @include touch-target-mixins.margin(
168 $component-height: $component-size,
169 $component-width: $component-size,
170 $touch-target-height: $size,
171 $touch-target-width: $size,
172 $query: $query
173 );
174 }
175 }
176
177 .mdc-icon-button__touch {
178 @include touch-target-mixins.touch-target(
179 $set-width: true,
180 $query: $query,
181 $height: $size,
182 $width: $size
183 );
184 }
185}
186
187///
188/// Sets the width, height and padding of icon button. Also changes the size of
189/// the icon itself based on button size.
190///
191/// @param {Number} $width - Width value for icon-button.
192/// @param {Number} $height - Height value for icon-button. (default: $width)
193/// @param {Number} $padding - Padding value for icon-button. (default: max($width, $height) / 2)
194/// @deprecated
195/// This mixin provides too much of low level customization.
196/// Please use mdc-icon-button-size instead.
197///
198@mixin icon-size(
199 $width,
200 $height: $width,
201 $padding: math.div(math.max($width, $height), 2),
202 $query: feature-targeting.all()
203) {
204 $feat-structure: feature-targeting.create-target($query, structure);
205
206 @include feature-targeting.targets($feat-structure) {
207 width: $width + $padding * 2;
208 height: $height + $padding * 2;
209 padding: $padding;
210 font-size: math.max($width, $height);
211 }
212
213 // stylelint-disable-next-line selector-max-type
214 svg,
215 img {
216 @include feature-targeting.targets($feat-structure) {
217 width: $width;
218 height: $height;
219 }
220 }
221}
222
223///
224/// Sets the font color and the ripple color to the provided color value.
225/// @param {Color} $color - The desired font and ripple color.
226///
227@mixin ink-color($color, $query: feature-targeting.all()) {
228 @include ink-color_($color, $query: $query);
229 @include ripple-theme.states(
230 $color,
231 $query: $query,
232 $ripple-target: $ripple-target
233 );
234}
235
236///
237/// Flips icon only in RTL context.
238///
239@mixin flip-icon-in-rtl($query: feature-targeting.all()) {
240 $feat-structure: feature-targeting.create-target($query, structure);
241
242 .mdc-button__icon {
243 @include rtl.rtl {
244 @include feature-targeting.targets($feat-structure) {
245 @include rtl.ignore-next-line();
246 transform: rotate(180deg);
247 }
248 }
249 }
250}
251
252///
253/// Sets the font color to the provided color value for a disabled icon button.
254/// @param {Color} $color - The desired font color.
255///
256@mixin disabled-ink-color($color, $query: feature-targeting.all()) {
257 @include if-disabled_ {
258 @include ink-color_($color, $query: $query);
259 }
260}
261
262///
263/// Includes ad-hoc high contrast mode support.
264///
265@mixin high-contrast-mode-shim($query: feature-targeting.all()) {
266 $feat-structure: feature-targeting.create-target($query, structure);
267
268 @include feature-targeting.targets($feat-structure) {
269 // TODO(b/175806874): Use the DOM border mixin after the ripple is moved
270 // away from :before to a dedicated element.
271 outline: solid 3px transparent;
272
273 &:focus {
274 outline: double 5px transparent;
275 }
276 }
277}
278
279///
280/// Sets the font color to the provided color value. This can be wrapped in
281/// a state qualifier such as `mdc-icon-button-if-disabled_`.
282/// @access private
283///
284@mixin ink-color_($color, $query: feature-targeting.all()) {
285 $feat-color: feature-targeting.create-target($query, color);
286
287 @include feature-targeting.targets($feat-color) {
288 @include theme.property(color, $color);
289 }
290}
291
292@mixin _state-layer-size($size) {
293 @include theme.property(height, $size);
294 @include theme.property(width, $size);
295}
296
297@mixin _icon-size($size) {
298 @include theme.property(font-size, $size);
299
300 svg,
301 img {
302 @include theme.property(width, $size);
303 @include theme.property(height, $size);
304 }
305}
306
307///
308/// Sets the icon opacity to the given opacity.
309/// @access private
310///
311@mixin _disabled-icon-opacity($opacity) {
312 &:disabled {
313 @include theme.property(opacity, $opacity);
314 }
315}
316
317///
318/// Sets the icon color to the given color.
319/// @param {map} $color-map - The desired icon color, specified as a map of
320/// colors with states {default, disabled, focus, hover, pressed} as keys.
321/// @access private
322///
323@mixin _icon-color-with-map($color-map) {
324 @include ink-color_(state.get-default-state($color-map));
325
326 $disabled: state.get-disabled-state($color-map);
327 @if $disabled {
328 &:disabled {
329 @include ink-color_($disabled);
330 }
331 }
332
333 $focus: state.get-focus-state($color-map);
334 @if $focus {
335 @include ripple-theme.focus {
336 @include ink-color_($focus);
337 }
338 }
339
340 $hover: state.get-hover-state($color-map);
341 @if $hover {
342 &:hover {
343 @include ink-color_($hover);
344 }
345 }
346
347 $pressed: state.get-pressed-state($color-map);
348 @if $pressed {
349 @include ripple-theme.active {
350 @include ink-color_($pressed);
351 }
352 }
353}
354
355@mixin _states-colors($color-map) {
356 // TODO(b/191298796): support focused & pressed key colors.
357
358 $hover: map.get($color-map, hover);
359 @if $hover {
360 @include ripple-theme.states-base-color(
361 $color: $hover,
362 $ripple-target: $ripple-target
363 );
364 }
365}
366
367///
368/// Helps style the icon button in its disabled state.
369/// @access private
370///
371@mixin if-disabled_ {
372 &:disabled {
373 @content;
374 }
375}