|
| 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