UNPKG

17 kBSCSSView Raw
1//
2// Copyright 2017 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// stylelint-disable selector-class-pattern --
24// Selector '.mdc-*' should only be used in this project.
25
26@use 'sass:math';
27@use 'sass:list';
28@use 'sass:map';
29@use 'sass:string';
30@use '@material/feature-targeting/feature-targeting';
31@use '@material/theme/custom-properties';
32@use '@material/theme/keys';
33@use '@material/theme/theme';
34
35/// @deprecated Avoid calling this function directly. Instead, configure the
36/// `$styles-<style>` variable Maps.
37@function set-styles_($base-styles, $scale-styles, $override-styles) {
38 $options: (
39 custom-property-prefix: typography,
40 );
41
42 $base-styles: keys.set-values($base-styles, $options: $options);
43
44 @each $style, $style-props in $scale-styles {
45 @each $base-key in map.keys($base-styles) {
46 // Ignore the return result, it's not needed
47 $unused: keys.add-link(keys.combine($style, $base-key), $base-key);
48 }
49
50 // Merge base properties for all styles.
51 $style-props: map.merge($base-styles, $style-props);
52
53 // Merge overrides onto each style.
54 $style-props: map.merge($style-props, map.get($override-styles, $style));
55
56 // Register keys for this style
57 @each $property, $value in $style-props {
58 $unused: keys.set-value(
59 keys.combine($style, $property),
60 $value,
61 $options: $options
62 );
63 }
64
65 // Override original styles with new styles.
66 $scale-styles: map.merge($scale-styles, (#{$style}: $style-props));
67 }
68
69 @return $scale-styles;
70}
71
72@function get-letter-spacing_($tracking, $font-size) {
73 @return math.div($tracking, $font-size * 16) * 1em;
74}
75
76@function px-to-rem($px) {
77 @if custom-properties.is-custom-prop($px) {
78 @return custom-properties.set-fallback(
79 $px,
80 _px-to-rem(custom-properties.get-fallback($px))
81 );
82 }
83 @return _px-to-rem($px);
84}
85
86@function _px-to-rem($px) {
87 @if $px == null {
88 @return null;
89 }
90 @return math.div($px, 16px) * 1rem;
91}
92
93$font-family: string.unquote('Roboto, sans-serif') !default;
94
95// Override styles
96$styles-headline1: () !default;
97$styles-headline2: () !default;
98$styles-headline3: () !default;
99$styles-headline4: () !default;
100$styles-headline5: () !default;
101$styles-headline6: () !default;
102$styles-subtitle1: () !default;
103$styles-subtitle2: () !default;
104$styles-body1: () !default;
105$styles-body2: () !default;
106$styles-caption: () !default;
107$styles-button: () !default;
108$styles-overline: () !default;
109
110/// @deprecated Do not override this variable. Use the $styles-<style> override
111/// Map variables instead, or $font-family to set the base font family.
112$base: (
113 font-family: $font-family,
114) !default;
115
116$font-weight-values: (
117 thin: 100,
118 light: 300,
119 regular: 400,
120 medium: 500,
121 bold: 700,
122 black: 900,
123) !default;
124
125/// @deprecated Do not override this variable. Use the $styles-<style> override
126/// Map variables instead.
127$styles: set-styles_(
128 $base,
129 (
130 headline1: (
131 font-size: px-to-rem(96px),
132 line-height: px-to-rem(96px),
133 font-weight: map.get($font-weight-values, light),
134 letter-spacing: get-letter-spacing_(-1.5, 6),
135 text-decoration: inherit,
136 text-transform: inherit,
137 ),
138 headline2: (
139 font-size: px-to-rem(60px),
140 line-height: px-to-rem(60px),
141 font-weight: map.get($font-weight-values, light),
142 letter-spacing: get-letter-spacing_(-0.5, 3.75),
143 text-decoration: inherit,
144 text-transform: inherit,
145 ),
146 headline3: (
147 font-size: px-to-rem(48px),
148 line-height: px-to-rem(50px),
149 font-weight: map.get($font-weight-values, regular),
150 letter-spacing: normal,
151 text-decoration: inherit,
152 text-transform: inherit,
153 ),
154 headline4: (
155 font-size: px-to-rem(34px),
156 line-height: px-to-rem(40px),
157 font-weight: map.get($font-weight-values, regular),
158 letter-spacing: get-letter-spacing_(0.25, 2.125),
159 text-decoration: inherit,
160 text-transform: inherit,
161 ),
162 headline5: (
163 font-size: px-to-rem(24px),
164 line-height: px-to-rem(32px),
165 font-weight: map.get($font-weight-values, regular),
166 letter-spacing: normal,
167 text-decoration: inherit,
168 text-transform: inherit,
169 ),
170 headline6: (
171 font-size: px-to-rem(20px),
172 line-height: px-to-rem(32px),
173 font-weight: map.get($font-weight-values, medium),
174 letter-spacing: get-letter-spacing_(0.25, 1.25),
175 text-decoration: inherit,
176 text-transform: inherit,
177 ),
178 subtitle1: (
179 font-size: px-to-rem(16px),
180 line-height: px-to-rem(28px),
181 font-weight: map.get($font-weight-values, regular),
182 letter-spacing: get-letter-spacing_(0.15, 1),
183 text-decoration: inherit,
184 text-transform: inherit,
185 ),
186 subtitle2: (
187 font-size: px-to-rem(14px),
188 line-height: px-to-rem(22px),
189 font-weight: map.get($font-weight-values, medium),
190 letter-spacing: get-letter-spacing_(0.1, 0.875),
191 text-decoration: inherit,
192 text-transform: inherit,
193 ),
194 body1: (
195 font-size: px-to-rem(16px),
196 line-height: px-to-rem(24px),
197 font-weight: map.get($font-weight-values, regular),
198 letter-spacing: get-letter-spacing_(0.5, 1),
199 text-decoration: inherit,
200 text-transform: inherit,
201 ),
202 body2: (
203 font-size: px-to-rem(14px),
204 line-height: px-to-rem(20px),
205 font-weight: map.get($font-weight-values, regular),
206 letter-spacing: get-letter-spacing_(0.25, 0.875),
207 text-decoration: inherit,
208 text-transform: inherit,
209 ),
210 caption: (
211 font-size: px-to-rem(12px),
212 line-height: px-to-rem(20px),
213 font-weight: map.get($font-weight-values, regular),
214 letter-spacing: get-letter-spacing_(0.4, 0.75),
215 text-decoration: inherit,
216 text-transform: inherit,
217 ),
218 button: (
219 font-size: px-to-rem(14px),
220 line-height: px-to-rem(36px),
221 font-weight: map.get($font-weight-values, medium),
222 letter-spacing: get-letter-spacing_(1.25, 0.875),
223 text-decoration: none,
224 text-transform: uppercase,
225 ),
226 overline: (
227 font-size: px-to-rem(12px),
228 line-height: px-to-rem(32px),
229 font-weight: map.get($font-weight-values, medium),
230 letter-spacing: get-letter-spacing_(2, 0.75),
231 text-decoration: none,
232 text-transform: uppercase,
233 ),
234 ),
235 (
236 headline1: $styles-headline1,
237 headline2: $styles-headline2,
238 headline3: $styles-headline3,
239 headline4: $styles-headline4,
240 headline5: $styles-headline5,
241 headline6: $styles-headline6,
242 subtitle1: $styles-subtitle1,
243 subtitle2: $styles-subtitle2,
244 body1: $styles-body1,
245 body2: $styles-body2,
246 caption: $styles-caption,
247 button: $styles-button,
248 overline: $styles-overline,
249 )
250) !default;
251
252// A copy of the styles Map that is used to detect compile-time changes for
253// Angular support.
254$_styles-copy: $styles;
255
256@function is-typography-style($style) {
257 @return map.has-key($styles, $style);
258}
259
260@function get-typography-styles() {
261 @return map.keys($styles);
262}
263
264@mixin core-styles($query: feature-targeting.all()) {
265 .mdc-typography {
266 @include base($query: $query);
267 }
268
269 @each $style in get-typography-styles() {
270 .mdc-typography--#{$style} {
271 @include typography($style, $query: $query);
272 }
273 }
274}
275
276@mixin base($query: feature-targeting.all()) {
277 $feat-typography: feature-targeting.create-target($query, typography);
278
279 @include smooth-font($query: $query);
280 @include feature-targeting.targets($feat-typography) {
281 @include theme.property(font-family, font-family);
282 }
283}
284
285@mixin typography($style, $query: feature-targeting.all(), $exclude-props: ()) {
286 $feat-typography: feature-targeting.create-target($query, typography);
287
288 @if not is-typography-style($style) {
289 @error "Invalid style specified! #{$style} doesn't exist. Choose one of #{get-typography-styles()}";
290 }
291
292 @include smooth-font($query: $query);
293 @include feature-targeting.targets($feat-typography) {
294 @each $key in keys.get-keys($style) {
295 // <style>-<property>: headline1-font-size
296 // Slice the string past the first key separator to retrieve the
297 // property name
298 $property: string.slice($key, string.index($key, '-') + 1);
299 @if list.index($exclude-props, $property) == null {
300 $current-global-value: map.get($styles, $style, $property);
301 $configured-global-value: map.get($_styles-copy, $style, $property);
302 @if $current-global-value != $configured-global-value {
303 // A compile time change was made to $mdc-typography-styles. To
304 // support Angular, use this value instead of the key's value.
305 @if $current-global-value {
306 // Only emit if the overridden value exists
307 $custom-prop: keys.create-custom-property($key);
308 $custom-prop: custom-properties.set-fallback(
309 $custom-prop,
310 $current-global-value
311 );
312 @include theme.property($property, $custom-prop);
313 }
314 } @else {
315 // Otherwise, use the key, which may be different from the original
316 // configured global value.
317 @include theme.property($property, $key);
318 }
319 }
320 }
321 }
322}
323
324/// Applies antialiasing via font-smoothing to text.
325@mixin smooth-font($query: feature-targeting.all()) {
326 $feat-typography: feature-targeting.create-target($query, typography);
327
328 @include feature-targeting.targets($feat-typography) {
329 -moz-osx-font-smoothing: grayscale;
330 -webkit-font-smoothing: antialiased;
331 }
332}
333
334// Element must be `display: block` or `display: inline-block` for this to work.
335@mixin overflow-ellipsis($query: feature-targeting.all()) {
336 $feat-structure: feature-targeting.create-target($query, structure);
337
338 @include feature-targeting.targets($feat-structure) {
339 text-overflow: ellipsis;
340 white-space: nowrap;
341 overflow: hidden;
342 }
343}
344
345/// Sets a container's baseline that text content will align to.
346///
347/// If the `$display` is set to a flexbox display, only `$top` baseline may be
348/// set. A separate element must be added as a child of the container with a
349/// `$bottom` baseline.
350///
351/// @param {Number} $top - the distance from the top of the container to the
352/// text's baseline.
353/// @param {Number} $bottom - the distance from the text's baseline to the
354/// bottom of the container.
355/// @param {String} $display - the display type of the container. May be `flex`,
356/// `inline-flex`, `block`, or `inline-block`.
357@mixin baseline(
358 $top: 0,
359 $bottom: 0,
360 $display: block,
361 $query: feature-targeting.all()
362) {
363 $validDisplayTypes: (flex, inline-flex, block, inline-block);
364
365 @if list.index($validDisplayTypes, $display) == null {
366 @error "mdc-typography: invalid display specified! #{$display} must be one of #{$validDisplayTypes}";
367 }
368
369 $isFlexbox: $display == 'flex' or $display == 'inline-flex';
370 $feat-structure: feature-targeting.create-target($query, structure);
371
372 @include feature-targeting.targets($feat-structure) {
373 display: $display;
374
375 @if $isFlexbox {
376 align-items: baseline;
377 }
378 }
379
380 @if $top > 0 {
381 @include baseline-top($top, $query: $query);
382 }
383
384 @if $bottom > 0 {
385 @if $isFlexbox {
386 @error "mdc-typography: invalid baseline with display type. #{$display} cannot specifiy $bottom. Add a separate child element with its own $bottom.";
387 }
388
389 @include baseline-bottom($bottom, $query: $query);
390 }
391}
392
393/// Sets the baseline of flow text content.
394///
395/// Separate `$top` and `$bottom` baselines may be specified. You should ensure
396/// that the `$top` baseline matches the previous text content's $bottom
397/// baseline to ensure text is positioned appropriately.
398///
399/// See go/css-baseline for reference on how this mixin works.
400///
401/// This is intended for text flow content only (e.g. `<h1>`, `<p>`, `<span>`,
402/// or `<div>` with only text content). Use `baseline()` to set the baseline of
403/// containers that are flexbox or have non-flow content children.
404///
405/// @param {Number} $top - the distance from the top of the container to the
406/// text's baseline.
407/// @param {Number} $bottom - the distance from the text's baseline to the
408/// bottom of the container.
409/// @param {Boolean} $lineHeight - the line-height to use for the text. This
410/// is the distance between baselines of multiple lines of text.
411/// @param {String} $display - the display type of the container. May be `block`
412/// or `inline-block`.
413@mixin text-baseline(
414 $top: 0,
415 $bottom: 0,
416 $display: block,
417 $lineHeight: normal,
418 $query: feature-targeting.all()
419) {
420 $validDisplayTypes: (block, inline-block);
421
422 @if list.index($validDisplayTypes, $display) == null {
423 @error "mdc-typography: invalid display specified! #{$display} must be one of #{$validDisplayTypes}";
424 }
425
426 $feat-structure: feature-targeting.create-target($query, structure);
427
428 @include baseline(
429 $display: $display,
430 $top: $top,
431 $bottom: $bottom,
432 $query: $query
433 );
434 @include feature-targeting.targets($feat-structure) {
435 @if $top > 0 {
436 margin-top: 0;
437 /* @alternate */
438 line-height: #{$lineHeight};
439 }
440
441 @if $bottom > 0 {
442 margin-bottom: -1 * $bottom;
443 }
444 }
445}
446
447/// Creates a baseline strut from the top of a container. This mixin is for
448/// advanced users, prefer `baseline()`.
449///
450/// @param {Number} $distance - The distance from the top of the container to
451/// the text's baseline.
452@mixin baseline-top($distance, $query: feature-targeting.all()) {
453 $feat-structure: feature-targeting.create-target($query, structure);
454
455 &::before {
456 @include feature-targeting.targets($feat-structure) {
457 @include baseline-strut_($distance);
458
459 vertical-align: 0;
460 }
461 }
462}
463
464/// Creates a baseline strut from the baseline to the bottom of a container.
465/// This mixin is for advanced users, prefer `baseline()`.
466///
467/// @param {Number} $distance - The distance from the text's baseline to the
468/// bottom of the container.
469@mixin baseline-bottom($distance, $query: feature-targeting.all()) {
470 $feat-structure: feature-targeting.create-target($query, structure);
471
472 &::after {
473 @include feature-targeting.targets($feat-structure) {
474 @include baseline-strut_($distance);
475
476 vertical-align: -1 * $distance;
477 }
478 }
479}
480
481/// Adds an invisible, zero-width prefix to a container's text.
482/// This ensures that the baseline is always where the text would be, instead
483/// of defaulting to the container bottom when text is empty. Do not use this
484/// mixin if the `baseline` mixin is already applied.
485@mixin zero-width-prefix($query: feature-targeting.all()) {
486 $feat-structure: feature-targeting.create-target($query, structure);
487
488 &::before {
489 @include feature-targeting.targets($feat-structure) {
490 content: '\200b';
491 }
492 }
493}
494
495@mixin baseline-strut_($distance) {
496 display: inline-block;
497 width: 0;
498 height: $distance;
499 content: '';
500}
501
502@function get-font($typography) {
503 @return map.get($styles, $typography, font-family);
504}
505
506@function get-line-height($typography) {
507 @return map.get($styles, $typography, line-height);
508}
509
510@function get-size($typography) {
511 @return map.get($styles, $typography, font-size);
512}
513
514@function get-weight($typography) {
515 @return map.get($styles, $typography, font-weight);
516}
517
518@function get-tracking($typography) {
519 @return map.get($styles, $typography, letter-spacing);
520}
521
522$_typography-theme: (
523 font: null,
524 line-height: null,
525 size: null,
526 weight: null,
527 tracking: null,
528);
529
530@mixin theme-styles($theme) {
531 @include theme.validate-theme-keys($_typography-theme, $theme);
532
533 @include theme.property(font-family, map.get($theme, font));
534 @include theme.property(line-height, map.get($theme, line-height));
535 @include theme.property(font-size, map.get($theme, size));
536 @include theme.property(font-weight, map.get($theme, weight));
537 @include theme.property(letter-spacing, map.get($theme, tracking));
538}