Skip to content

Commit 6ab4c6c

Browse files
author
Sérgio Gomes
committed
docs(all): Add theming guide.
1 parent dd9e7e0 commit 6ab4c6c

File tree

1 file changed

+397
-0
lines changed

1 file changed

+397
-0
lines changed

docs/theming.md

Lines changed: 397 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,397 @@
1+
# MDL V2 Theming Guide
2+
3+
## Overview
4+
5+
MDL includes a theming system designed to make it easy to change your application's colors. It provides multiple options
6+
for implementing themes, allowing for maximum flexibility. At the moment, MDL supports theming with Sass and with CSS
7+
Custom Property, with plans for CDN support as well, once that service is available.
8+
9+
## Colors
10+
11+
MDL theming, like Material Design theming, uses two main colors: **primary** and **accent**. The primary color is used
12+
throughout most of the application and components, as the main color for your application. The accent color is used
13+
for floating action buttons and other interactive elements, serving as visual contrast to the primary.
14+
15+
In addition to the primary and accent colors, MDL also defines a _background_ color, which is used as a background in
16+
components, and usually as the page background as well.
17+
18+
Finally, MDL has a number of text colors, which are used for rendering text and other shapes on top of the primary,
19+
accent and background colors. These are specified as either dark or light, in order to provide sufficient contrast to
20+
what's behind them, and have
21+
[different levels of opacity depending on usage](https://material.google.com/style/color.html#color-color-schemes):
22+
- Primary, used for most text.
23+
- Secondary, used for text which is lower in the visual hierarchy.
24+
- Hint, used for text hints (such as those in text fields and labels).
25+
- Disabled, used for text in disabled components and content.
26+
- Icon, used for icons.
27+
28+
## Building a themed application
29+
30+
Let's start with a simple application, which displays several cards for different categories. We ultimately want each
31+
card to have a color scheme that matches its category, but we'll start with the default theming provided by MDL.
32+
33+
You can [take a look at the end result here](https://plnkr.co/edit/qxt7qpPsXgkt9UbMnE36?p=preview), but let's start from
34+
scratch.
35+
36+
> Note: We won't cover the basics of starting an MDL v2 project in this guide, so please take a look at the
37+
[getting started guide](./getting-started.md) if you need more information.
38+
39+
### Step 1: No theming
40+
41+
Here's the markup:
42+
43+
```html
44+
<!DOCTYPE html>
45+
<html class="mdl-typography">
46+
<head>
47+
<meta charset="utf-8">
48+
<meta name="viewport" content="width=device-width,initial-scale=1">
49+
<title>Elements</title>
50+
<link rel="stylesheet" href="/node_modules/material-design-lite/dist/material-design-lite.css">
51+
<style>
52+
.cards {
53+
display: flex;
54+
flex-wrap: wrap;
55+
}
56+
57+
.element-card {
58+
width: 16em;
59+
margin: 16px;
60+
}
61+
62+
.element-card > .mdl-card__media {
63+
height: 9em;
64+
}
65+
</style>
66+
</head>
67+
<body>
68+
<h1>Choose your element</h1>
69+
<div class="cards">
70+
<div class="mdl-card element-card earth">
71+
<section class="mdl-card__media">
72+
<h1 class="mdl-card__title mdl-card__title--large">Earth</h1>
73+
<h2 class="mdl-card__subtitle">A solid decision.</h2>
74+
</section>
75+
<section class="mdl-card__actions">
76+
<button class="mdl-button mdl-button--primary mdl-button--compact mdl-card__action">Pick this element</button>
77+
</section>
78+
</div>
79+
80+
<div class="mdl-card element-card wind">
81+
<section class="mdl-card__media">
82+
<h1 class="mdl-card__title mdl-card__title--large">Wind</h1>
83+
<h2 class="mdl-card__subtitle">Stormy weather ahead.</h2>
84+
</section>
85+
<section class="mdl-card__actions">
86+
<button class="mdl-button mdl-button--primary mdl-button--compact mdl-card__action">Pick this element</button>
87+
</section>
88+
</div>
89+
90+
<div class="mdl-card element-card fire">
91+
<section class="mdl-card__media">
92+
<h1 class="mdl-card__title mdl-card__title--large">Fire</h1>
93+
<h2 class="mdl-card__subtitle">Hot-headed much?</h2>
94+
</section>
95+
<section class="mdl-card__actions">
96+
<button class="mdl-button mdl-button--primary mdl-button--compact mdl-card__action">Pick this element</button>
97+
</section>
98+
</div>
99+
100+
<div class="mdl-card element-card water">
101+
<section class="mdl-card__media">
102+
<h1 class="mdl-card__title mdl-card__title--large">Water</h1>
103+
<h2 class="mdl-card__subtitle">Go with the flow.</h2>
104+
</section>
105+
<section class="mdl-card__actions">
106+
<button class="mdl-button mdl-button--primary mdl-button--compact mdl-card__action">Pick this element</button>
107+
</section>
108+
</div>
109+
</div>
110+
</body>
111+
</html>
112+
```
113+
114+
You'll see that we have a number of pretty empty looking cards, with black text on a white background. The only hint of
115+
color comes from the buttons, which we've made use the primary color by adding the `mdl-button--primary` class.
116+
117+
118+
### Step 2: Use the MDL colors in your own markup
119+
120+
Not everything has a `--primary` option, though, particularly where it comes to your own markup.
121+
122+
Let's make things look a bit more interesting, by using the primary color as a background to the cards' media area.
123+
One way of doing this would be to write your own custom CSS rules, and set the background to the same color that's being
124+
used as the primary:
125+
126+
```css
127+
/* Bad approach */
128+
.element-card > .mdl-card__media {
129+
background-color: #3f51b5;
130+
}
131+
```
132+
133+
However, that would not take advantage of MDL's theming and would thus be brittle; changes to theming would need to be
134+
copied to your CSS rules, adding a maintenance cost.
135+
136+
MDL provides a number of CSS classes as part of the `mdl-theme` module to help you tackle this problem in a more
137+
maintainable way. Here are the classes that deal with primary, accent and background colors:
138+
139+
| Class | Description |
140+
| ----------------------- | ----------------------------------------------------------- |
141+
| `mdl-theme--primary` | Sets the text color to the theme primary color. |
142+
| `mdl-theme--accent` | Sets the text color to the theme accent color. |
143+
| `mdl-theme--background` | Sets the background color to the theme background color. |
144+
| `mdl-theme--primary-bg` | Sets the background color to the theme primary color. |
145+
| `mdl-theme--accent-bg` | Sets the background color to the theme accent color. |
146+
147+
From here, we can see that we want to apply `mdl-theme--primary-bg` to the cards' media areas:
148+
149+
```html
150+
<div class="mdl-card element-card">
151+
<section class="mdl-card__media mdl-theme--primary-bg">
152+
<h1 class="mdl-card__title mdl-card__title--large">Earth</h1>
153+
<h2 class="mdl-card__subtitle">A solid decision.</h2>
154+
</section>
155+
<section class="mdl-card__actions">
156+
<button class="mdl-button mdl-button--primary mdl-button--compact mdl-card__action">Pick this element</button>
157+
</section>
158+
</div>
159+
```
160+
161+
All the cards now use the default primary color (Indigo 500 from the Material palette) as the background for the media
162+
area, the same color that's being used for the text in the buttons.
163+
164+
However, you'll notice that the text in the media area is still black, which provides very little contrast to the
165+
default primary color. Not all primary colors are dark, though, so you can't just switch the text color to white and
166+
call it a day. Ideally, we want a solution which is as maintainable as the `mdl-theme--primary-bg` class, and which
167+
takes into account the primary color, in order to determine whether to overlay white or black text on top.
168+
169+
`mdl-theme` provides utility classes for that purpose as well. Namely, for overlaying text on a primary color
170+
background, there are:
171+
172+
| Class | Description |
173+
| -------------------------------------- | ----------------------------------------------------------------------------------------- |
174+
| `mdl-theme--text-primary-on-primary` | Set text to suitable color for primary text on top of a theme primary color background. |
175+
| `mdl-theme--text-secondary-on-primary` | Set text to suitable color for secondary text on top of a theme primary color background. |
176+
| `mdl-theme--text-hint-on-primary` | Set text to suitable color for hint text on top of a theme primary color background. |
177+
| `mdl-theme--text-disabled-on-primary` | Set text to suitable color for disabled text on top of a theme primary color background. |
178+
| `mdl-theme--text-icon-on-primary` | Set text to suitable color for icons on top of a theme primary color background. |
179+
180+
> Note: `primary`, `secondary`, `hint`, `disabled` and `icon` refer to the text's function. The fact that we use the
181+
word _primary_ in the different contexts of _primary color_ and _primary function text_ can be confusing at first.
182+
183+
From here, we can see the right choice is `mdl-theme--text-primary-on-primary`. We could think of applying it to the
184+
media area, but that won't work because of scoping issues. If we apply it directly to the title and subtitle, though:
185+
186+
```html
187+
<div class="mdl-card element-card">
188+
<section class="mdl-card__media mdl-theme--primary-bg">
189+
<h1 class="mdl-card__title mdl-card__title--large mdl-theme--text-primary-on-primary">Earth</h1>
190+
<h2 class="mdl-card__subtitle mdl-theme--text-primary-on-primary">A solid decision.</h2>
191+
</section>
192+
<section class="mdl-card__actions">
193+
<button class="mdl-button mdl-button--primary mdl-button--compact mdl-card__action">Pick this element</button>
194+
</section>
195+
</div>
196+
```
197+
198+
The text is now white, which provides much better contrast. If we were to change the primary color to a light color,
199+
however, the text would be dark again, for the same reason. So how _do_ we change the primary color?
200+
201+
202+
### Step 3: Changing the theme with Sass
203+
204+
The application-wide theme colors that are used as the default across your entire application can be set in Sass.
205+
This is as easy as defining three variables (`$mdl-theme-primary`, `$mdl-theme-accent` and `$mdl-theme-background`) in
206+
your Sass file, before importing any MDL modules.
207+
208+
```scss
209+
// My main Sass file.
210+
$mdl-theme-primary: #9c27b0;
211+
$mdl-theme-accent: #76ff03;
212+
$mdl-theme-background: #fff;
213+
214+
@import "material-design-lite";
215+
```
216+
217+
These definitions will override the defaults included in the `mdl-theme` module, which every themeable component depends
218+
on. As for the text colors, these will all be automatically calculated from the primary, accent and background you
219+
provide, as part of the Sass definitions in `mdl-theme`. Pretty simple!
220+
221+
> Note: theme colors don't have to be part of the Material palette; you can use any valid color. You may want to read
222+
the [color section](https://material.google.com/style/color.html) in the Material Design spec to inform your pick of an
223+
alternative palette.
224+
225+
226+
### Step 4: Changing the theme with CSS Custom Properties
227+
228+
Changing the theme colors with Sass affects the whole application, which is great if you want consistency across the
229+
board. What we want here is slightly different, though: we want each card to have its own internally consistent theme.
230+
231+
So how do we keep all the current theme color "plumbing" for maintainability, while having different themes in different
232+
places? CSS Custom properties to the rescue!
233+
234+
The generated MDL CSS uses CSS Custom Properties with hardcoded fallbacks, which are set to the colors provided in Sass.
235+
This means that you can define your default theme in Sass (like we did above), but override it in CSS, dependent on
236+
context or user preference.
237+
238+
Let's take a closer look at how MDL does things. Here's an excerpt of a compiled MDL CSS rule:
239+
240+
```css
241+
.mdl-button--primary.mdl-button--raised {
242+
background-color: #3f51b5;
243+
background-color: var(--mdl-theme-primary, #3f51b5);
244+
}
245+
```
246+
247+
Here, you can see that MDL sets a fallback for the color, for browsers that don't support CSS Custom Properties. If
248+
they do, however, that declaration gets overriden by a `var()` lookup, using the same fallback as the default value
249+
(in case the custom property definition isn't found).
250+
251+
As such, you can easily override the colors that get used in MDL components by simply redefining the custom property at
252+
some level. So if we want to apply it to our cards, we can take advantage of the element classes we had set up:
253+
254+
```css
255+
.element-card.earth {
256+
--mdl-theme-primary: #795548;
257+
}
258+
259+
.element-card.wind {
260+
--mdl-theme-primary: #9e9e9e;
261+
}
262+
263+
.element-card.fire {
264+
--mdl-theme-primary: #f44336;
265+
}
266+
267+
.element-card.water {
268+
--mdl-theme-primary: #00bcd4;
269+
}
270+
```
271+
272+
It works! You can see that the colors get applied to both the backgrounds and the buttons. If the cards had any other
273+
components, they'd use the correct colors as well.
274+
275+
The custom properties used by MDL follow a similar naming convention to the Sass variables and CSS classes:
276+
277+
| Custom property | Description |
278+
| ------------------------ | --------------------------- |
279+
| `--mdl-theme-primary` | The theme primary color. |
280+
| `--mdl-theme-accent` | The theme accent color. |
281+
| `--mdl-theme-background` | The theme background color. |
282+
283+
However, if you look closely at the page, we're not quite done yet. The text colors are incorrect: the wind and water
284+
cards should have dark text, rather than white. So what's happening?
285+
286+
The problem is that we only set the `--mdl-theme-primary` custom property. Whereas setting `$mdl-theme-primary` in Sass
287+
allows for calculating all the related text colors, it's currently not possible to perform those complex contrast
288+
calculations in CSS. This means you'll also have to set all the related text colors:
289+
290+
| Custom property | Description |
291+
| ------------------------------------------ | ---------------------------------------------------------- |
292+
| `--mdl-theme-text-primary-on-primary` | Primary text on top of a theme primary color background. |
293+
| `--mdl-theme-text-secondary-on-primary` | Secondary text on top of a theme primary color background. |
294+
| `--mdl-theme-text-hint-on-primary` | Hint text on top of a theme primary color background. |
295+
| `--mdl-theme-text-disabled-on-primary` | Disabled text on top of a theme primary color background. |
296+
| `--mdl-theme-text-icon-on-primary` | Icons on top of a theme primary color background. |
297+
298+
The same pattern is followed for text colors on _accent_ and _background_:
299+
300+
| Custom property | Description |
301+
| ------------------------------------------ | ---------------------------------------------------------- |
302+
| `--mdl-theme-text-primary-on-accent` | Primary text on top of a theme accent color background. |
303+
| `--mdl-theme-text-secondary-on-accent` | Secondary text on top of a theme accent color background. |
304+
| `--mdl-theme-text-hint-on-accent` | Hint text on top of a theme accent color background. |
305+
| `--mdl-theme-text-disabled-on-accent` | Disabled text on top of a theme accent color background. |
306+
| `--mdl-theme-text-icon-on-accent` | Icons on top of a theme accent color background. |
307+
308+
| Custom property | Description |
309+
| ------------------------------------------ | ---------------------------------------------------------- |
310+
| `--mdl-theme-text-primary-on-background` | Primary text on top of the theme background color. |
311+
| `--mdl-theme-text-secondary-on-background` | Secondary text on top of the theme background color. |
312+
| `--mdl-theme-text-hint-on-background` | Hint text on top of the theme background color. |
313+
| `--mdl-theme-text-disabled-on-background` | Disabled text on top of the theme background color. |
314+
| `--mdl-theme-text-icon-on-background` | Icons on top of the theme background color. |
315+
316+
In addition, we also define custom properties for known dark and light backgrounds:
317+
318+
319+
| Custom property | Description |
320+
| ------------------------------------------ | ---------------------------------------------------------- |
321+
| `--mdl-theme-text-primary-on-light` | Primary text on top of a light-colored background. |
322+
| `--mdl-theme-text-secondary-on-light` | Secondary text on top of a light-colored background. |
323+
| `--mdl-theme-text-hint-on-light` | Hint text on top of a light-colored background. |
324+
| `--mdl-theme-text-disabled-on-light` | Disabled text on top of a light-colored background. |
325+
| `--mdl-theme-text-icon-on-light` | Icons on top of a light-colored background. |
326+
327+
| Custom property | Description |
328+
| ------------------------------------------ | ---------------------------------------------------------- |
329+
| `--mdl-theme-text-primary-on-dark` | Primary text on top of a dark-colored background. |
330+
| `--mdl-theme-text-secondary-on-dark` | Secondary text on top of a dark-colored background. |
331+
| `--mdl-theme-text-hint-on-dark` | Hint text on top of a dark-colored background. |
332+
| `--mdl-theme-text-disabled-on-dark` | Disabled text on top of a dark-colored background. |
333+
| `--mdl-theme-text-icon-on-dark` | Icons on top of a dark-colored background. |
334+
335+
336+
Ideally, we should set all of the text colors on primary, since we never know which one an MDL component might use.
337+
Since we're just using buttons, though, let's keep it simple for now:
338+
339+
```css
340+
.element-card.earth {
341+
--mdl-theme-primary: #795548;
342+
--mdl-theme-text-primary-on-primary: var(--mdl-theme-text-primary-on-dark);
343+
}
344+
345+
.element-card.wind {
346+
--mdl-theme-primary: #9e9e9e;
347+
--mdl-theme-text-primary-on-primary: var(--mdl-theme-text-primary-on-light);
348+
}
349+
350+
.element-card.fire {
351+
--mdl-theme-primary: #f44336;
352+
--mdl-theme-text-primary-on-primary: var(--mdl-theme-text-primary-on-dark);
353+
}
354+
355+
.element-card.water {
356+
--mdl-theme-primary: #00bcd4;
357+
--mdl-theme-text-primary-on-primary: var(--mdl-theme-text-primary-on-light);
358+
}
359+
```
360+
361+
> Note: in the future we plan to provide a Javascript utility method for changing all derived colors and making this
362+
use-case easier.
363+
364+
365+
## Dark Themes
366+
367+
Beyond what we've covered in this document so far, there's also the concept of a _dark theme_. All MDL components have
368+
been designed to work with both light themes (that assume a light-colored background) and dark themes (with dark-colored
369+
backgrounds), but the default is always light.
370+
371+
> Note: When using a dark theme, you probably want to choose a dark color as the background for your page, and adjust
372+
the MDL `background` color to match.
373+
374+
In order to apply a dark theme to a single element, you can use its `--dark` class. For example, for a button:
375+
376+
```html
377+
<button class="mdl-button mdl-button--raised mdl-button--dark">
378+
Raised dark button
379+
</button>
380+
```
381+
382+
Alternatively, you can set your entire page (or a portion of it) to a dark theme by using the `mdl-theme--dark` class:
383+
384+
```html
385+
<section class="mdl-theme--dark">
386+
<button class="mdl-button mdl-button--raised">
387+
Still dark
388+
</button>
389+
390+
<button class="mdl-button">
391+
Me too!
392+
</button>
393+
</section>
394+
```
395+
396+
> Note: there's currently no way to set a light portion inside of a dark one, so if you want to achieve that effect
397+
you'll need to selectively apply dark classes to everything except the light bits.

0 commit comments

Comments
 (0)