Material Components for the Web

Web

Theming Guide

Overview

MDC-Web includes a theming system designed to make it easy to change your application’s colors. It provides multiple options for implementing themes, allowing for maximum flexibility. At the moment, MDC-Web supports theming with Sass and with CSS Custom Property, with plans for CDN support as well, once that service is available.

Colors

MDC-Web theming, like Material Design theming, uses two main colors: primary and secondary. The primary color is used throughout most of the application and components, as the main color for your application. The secondary color is used for floating action buttons and other interactive elements, serving as visual contrast to the primary.

In addition to the primary and secondary colors, MDC-Web also defines a background color, which is used as a background in components, and usually as the page background as well.

Finally, MDC-Web has a number of text colors, which are used for rendering text and other shapes on top of the primary, secondary and background colors. These are specified as either dark or light, in order to provide sufficient contrast to what’s behind them, and have different levels of opacity depending on usage:

Building a themed application

Let’s start with a simple application, which displays several cards for different categories. We ultimately want each card to have a color scheme that matches its category, but we’ll start with the default theming provided by MDC-Web.

You can take a look at the end result here, but let’s start from scratch.

Note: We won’t cover the basics of starting an MDC-Web project in this guide, so please take a look at the getting started guide if you need more information.

Step 1: No theming

Here’s the markup:

<!DOCTYPE html>
<html class="mdc-typography">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Elements</title>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
    <link
      rel="stylesheet"
      href="https://unpkg.com/material-components-web@latest/dist/material-components-web.min.css">
    <style>
      .cards {
        display: flex;
        flex-wrap: wrap;
      }

      .element-card {
        width: 16em;
        margin: 16px;
      }

      .element-card > .mdc-card__media {
        height: 9em;
      }

      #demo-absolute-fab {
        position: fixed;
        bottom: 1rem;
        right: 1rem;
        z-index: 1;
      }
    </style>
  </head>
  <body>
    <h1>Choose your element</h1>
    <div class="cards">
      <div class="mdc-card element-card earth">
        <section class="mdc-card__media">
          <h1 class="mdc-card__title mdc-card__title--large">Earth</h1>
          <h2 class="mdc-card__subtitle">A solid decision.</h2>
        </section>
        <section class="mdc-card__supporting-text">
          Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
          Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
        </section>
      </div>

      <div class="mdc-card element-card wind">
        <section class="mdc-card__media">
          <h1 class="mdc-card__title mdc-card__title--large">Wind</h1>
          <h2 class="mdc-card__subtitle">Stormy weather ahead.</h2>
        </section>
        <section class="mdc-card__supporting-text">
          Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
          Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
        </section>
      </div>

      <div class="mdc-card element-card fire">
        <section class="mdc-card__media">
          <h1 class="mdc-card__title mdc-card__title--large">Fire</h1>
          <h2 class="mdc-card__subtitle">Hot-headed much?</h2>
        </section>
        <section class="mdc-card__supporting-text">
          Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
          Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
        </section>
      </div>

      <div class="mdc-card element-card water">
        <section class="mdc-card__media">
          <h1 class="mdc-card__title mdc-card__title--large">Water</h1>
          <h2 class="mdc-card__subtitle">Go with the flow.</h2>
        </section>
        <section class="mdc-card__supporting-text">
          Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
          Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
        </section>
      </div>
    </div>
    <button class="mdc-fab material-icons" id="demo-absolute-fab" aria-label="Favorite">
      <span class="mdc-fab__icon">
        favorite
      </span>
    </button>
  </body>
</html>

You’ll see that we have a number of pretty empty looking cards, with black text on a white background. The only hint of color comes from the FAB, which adopts the secondary color by default.

Step 2: Use the MDC-Web colors in your own markup

Not everything has a --primary option, though, particularly where it comes to your own markup.

Let’s make things look a bit more interesting, by using the primary color as a background to the cards’ media area. 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 used as the primary:

/* Bad approach */
.element-card > .mdc-card__media {
  background-color: #3f51b5;
}

However, that would not take advantage of MDC-Web’s theming and would thus be brittle; changes to theming would need to be copied to your CSS rules, adding a maintenance cost.

MDC-Web provides a number of CSS classes as part of the mdc-theme module to help you tackle this problem in a more maintainable way. Here are the classes that deal with primary, secondary and background colors:

Class Description
mdc-theme--primary Sets the text color to the theme primary color.
mdc-theme--primary-light Sets the text color to the theme primary color (light variant).
mdc-theme--primary-dark Sets the text color to the theme primary color (dark variant).
mdc-theme--secondary Sets the text color to the theme secondary color.
mdc-theme--secondary-light Sets the text color to the theme secondary color (light variant).
mdc-theme--secondary-dark Sets the text color to the theme secondary color (dark variant).
mdc-theme--background Sets the background color to the theme background color.
mdc-theme--primary-bg Sets the background color to the theme primary color.
mdc-theme--primary-light-bg Sets the background color to the theme primary color (light variant).
mdc-theme--primary-dark-bg Sets the background color to the theme primary color (dark variant).
mdc-theme--secondary-bg Sets the background color to the theme secondary color.
mdc-theme--secondary-light-bg Sets the background color to the theme secondary color (light variant).
mdc-theme--secondary-dark-bg Sets the background color to the theme secondary color (dark variant).

From here, we can see that we want to apply mdc-theme--primary-bg to the cards’ media areas:

<div class="mdc-card element-card">
  <section class="mdc-card__media mdc-theme--primary-bg">
    <h1 class="mdc-card__title mdc-card__title--large">Earth</h1>
    <h2 class="mdc-card__subtitle">A solid decision.</h2>
  </section>
  <section class="mdc-card__supporting-text">
    Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
    Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
  </section>
</div>

All the cards now use the default primary color (Indigo 500 from the Material palette) as the background for the media area.

However, you’ll notice that the text in the media area is still black, which provides very little contrast to the default primary color. Not all primary colors are dark, though, so you can’t just switch the text color to white and call it a day. Ideally, we want a solution which is as maintainable as the mdc-theme--primary-bg class, and which takes into account the primary color, in order to determine whether to overlay white or black text on top.

mdc-theme provides utility classes for that purpose as well. Namely, for overlaying text on a primary color background, there are:

Class Description
mdc-theme--text-primary-on-primary Set text to suitable color for primary text on top of a theme primary color background.
mdc-theme--text-primary-on-primary-light Set text to suitable color for primary text on top of a theme primary color background (light variant).
mdc-theme--text-primary-on-primary-dark Set text to suitable color for primary text on top of a theme primary color background (dark variant).
mdc-theme--text-secondary-on-primary Set text to suitable color for secondary text on top of a theme primary color background.
mdc-theme--text-secondary-on-primary-light Set text to suitable color for secondary text on top of a theme primary color background (light variant).
mdc-theme--text-secondary-on-primary-dark Set text to suitable color for secondary text on top of a theme primary color background (dark variant).
mdc-theme--text-hint-on-primary Set text to suitable color for hint text on top of a theme primary color background.
mdc-theme--text-hint-on-primary-light Set text to suitable color for hint text on top of a theme primary color background (light variant).
mdc-theme--text-hint-on-primary-dark Set text to suitable color for hint text on top of a theme primary color background (dark variant).
mdc-theme--text-disabled-on-primary Set text to suitable color for disabled text on top of a theme primary color background.
mdc-theme--text-disabled-on-primary-light Set text to suitable color for disabled text on top of a theme primary color background (light variant).
mdc-theme--text-disabled-on-primary-dark Set text to suitable color for disabled text on top of a theme primary color background (dark variant).
mdc-theme--text-icon-on-primary Set text to suitable color for icons on top of a theme primary color background.
mdc-theme--text-icon-on-primary-light Set text to suitable color for icons on top of a theme primary color background (light variant).
mdc-theme--text-icon-on-primary-dark Set text to suitable color for icons on top of a theme primary color background (dark variant).

Note: primary, secondary, hint, disabled and icon refer to the text’s function. The fact that we use the word primary in the different contexts of primary color and primary function text can be confusing at first.

From here, we can see the right choice is mdc-theme--text-primary-on-primary. We could think of applying it to the media area, but that won’t work because of scoping issues. If we apply it directly to the title and subtitle, though:

<div class="mdc-card element-card">
  <section class="mdc-card__media mdc-theme--primary-bg">
    <h1 class="mdc-card__title mdc-card__title--large mdc-theme--text-primary-on-primary">Earth</h1>
    <h2 class="mdc-card__subtitle mdc-theme--text-primary-on-primary">A solid decision.</h2>
  </section>
  <section class="mdc-card__supporting-text">
    Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
    Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
  </section>
</div>

The text is now white, which provides much better contrast. If we were to change the primary color to a light color, however, the text would be dark again, for the same reason. So how do we change the primary color?

Step 3: Changing the theme with Sass

The application-wide theme colors that are used as the default across your entire application can be set in Sass. This is as easy as defining three variables ($mdc-theme-primary, $mdc-theme-secondary and $mdc-theme-background) in your Sass file, before importing any MDC-Web modules.

// My main Sass file.
$mdc-theme-primary: #9c27b0;
$mdc-theme-secondary: #76ff03;
$mdc-theme-background: #fff;

@import "material-components-web/material-components-web";

These definitions will override the defaults included in the mdc-theme module, which every themeable component depends on. As for the text colors, these will all be automatically calculated from the primary, secondary and background you provide, as part of the Sass definitions in mdc-theme. Pretty simple!

Note: theme colors don’t have to be part of the Material palette; you can use any valid color. You may want to read the color section in the Material Design spec to inform your pick of an alternative palette.

Step 4: Changing the theme with CSS Custom Properties

Changing the theme colors with Sass affects the whole application, which is great if you want consistency across the board. What we want here is slightly different, though: we want each card to have its own internally consistent theme.

So how do we keep all the current theme color “plumbing” for maintainability, while having different themes in different places? CSS Custom properties to the rescue!

The generated MDC-Web CSS uses CSS Custom Properties with hardcoded fallbacks, which are set to the colors provided in Sass. This means that you can define your default theme in Sass (like we did above), but override it in CSS, dependent on context or user preference.

Let’s take a closer look at how MDC-Web does things. Here’s an excerpt of a compiled MDC-Web CSS rule:

.mdc-fab {
  background-color: #ff4081;
  background-color: var(--mdc-theme-secondary, #ff4081);
}

Here, you can see that MDC-Web sets a fallback for the color, for browsers that don’t support CSS Custom Properties. If they do, however, that declaration gets overriden by a var() lookup, using the same fallback as the default value (in case the custom property definition isn’t found).

As such, you can easily override the colors that get used in MDC-Web components by simply redefining the custom property at some level. So if we want to apply it to our cards, we can take advantage of the element classes we had set up:

.element-card.earth {
  --mdc-theme-primary: #795548;
}

.element-card.wind {
  --mdc-theme-primary: #9e9e9e;
}

.element-card.fire {
  --mdc-theme-primary: #f44336;
}

.element-card.water {
  --mdc-theme-primary: #00bcd4;
}

It works! You can see that the colors get applied to the backgrounds. If the cards had any other components, they’d use the correct colors as well.

The custom properties used by MDC-Web follow a similar naming convention to the Sass variables and CSS classes:

Custom property Description
--mdc-theme-primary The theme primary color.
--mdc-theme-primary-light The theme primary color (light variant).
--mdc-theme-primary-dark The theme primary color (dark variant).
--mdc-theme-secondary The theme secondary color.
--mdc-theme-secondary-light The theme secondary color (light variant).
--mdc-theme-secondary-dark The theme secondary color (dark variant).
--mdc-theme-background The theme background color.

However, if you look closely at the page, we’re not quite done yet. The text colors are incorrect: the wind and water cards should have dark text, rather than white. So what’s happening?

The problem is that we only set the --mdc-theme-primary custom property. Whereas setting $mdc-theme-primary in Sass allows for calculating all the related text colors, it’s currently not possible to perform those complex contrast calculations in CSS. This means you’ll also have to set all the related text colors:

Custom property Description
--mdc-theme-text-primary-on-primary Primary text on top of a theme primary color background.
--mdc-theme-text-primary-on-primary-light Primary text on top of a theme primary color background (light variant).
--mdc-theme-text-primary-on-primary-dark Primary text on top of a theme primary color background (dark variant).
--mdc-theme-text-secondary-on-primary Secondary text on top of a theme primary color background.
--mdc-theme-text-secondary-on-primary-light Secondary text on top of a theme primary color background (light variant).
--mdc-theme-text-secondary-on-primary-dark Secondary text on top of a theme primary color background (dark variant).
--mdc-theme-text-hint-on-primary Hint text on top of a theme primary color background.
--mdc-theme-text-hint-on-primary-light Hint text on top of a theme primary color background (light variant).
--mdc-theme-text-hint-on-primary-dark Hint text on top of a theme primary color background (dark variant).
--mdc-theme-text-disabled-on-primary Disabled text on top of a theme primary color background.
--mdc-theme-text-disabled-on-primary-light Disabled text on top of a theme primary color background (light variant).
--mdc-theme-text-disabled-on-primary-dark Disabled text on top of a theme primary color background (dark variant).
--mdc-theme-text-icon-on-primary Icons on top of a theme primary color background.
--mdc-theme-text-icon-on-primary-light Icons on top of a theme primary color background (light variant).
--mdc-theme-text-icon-on-primary-dark Icons on top of a theme primary color background (dark variant).

The same pattern is followed for text colors on secondary and background:

Custom property Description
--mdc-theme-text-primary-on-secondary Primary text on top of a theme secondary color background.
--mdc-theme-text-primary-on-secondary-light Primary text on top of a theme secondary color background (light variant).
--mdc-theme-text-primary-on-secondary-dark Primary text on top of a theme secondary color background (dark variant).
--mdc-theme-text-secondary-on-secondary Secondary text on top of a theme secondary color background.
--mdc-theme-text-secondary-on-secondary-light Secondary text on top of a theme secondary color background (light variant).
--mdc-theme-text-secondary-on-secondary-dark Secondary text on top of a theme secondary color background (dark variant).
--mdc-theme-text-hint-on-secondary Hint text on top of a theme secondary color background.
--mdc-theme-text-hint-on-secondary-light Hint text on top of a theme secondary color background (light variant).
--mdc-theme-text-hint-on-secondary-dark Hint text on top of a theme secondary color background (dark variant).
--mdc-theme-text-disabled-on-secondary Disabled text on top of a theme secondary color background.
--mdc-theme-text-disabled-on-secondary-light Disabled text on top of a theme secondary color background (light variant).
--mdc-theme-text-disabled-on-secondary-dark Disabled text on top of a theme secondary color background (dark variant).
--mdc-theme-text-icon-on-secondary Icons on top of a theme secondary color background.
--mdc-theme-text-icon-on-secondary-light Icons on top of a theme secondary color background (light variant).
--mdc-theme-text-icon-on-secondary-dark Icons on top of a theme secondary color background (dark variant).
Custom property Description
--mdc-theme-text-primary-on-background Primary text on top of the theme background color.
--mdc-theme-text-secondary-on-background Secondary text on top of the theme background color.
--mdc-theme-text-hint-on-background Hint text on top of the theme background color.
--mdc-theme-text-disabled-on-background Disabled text on top of the theme background color.
--mdc-theme-text-icon-on-background Icons on top of the theme background color.

In addition, we also define custom properties for known dark and light backgrounds:

Custom property Description
--mdc-theme-text-primary-on-light Primary text on top of a light-colored background.
--mdc-theme-text-secondary-on-light Secondary text on top of a light-colored background.
--mdc-theme-text-hint-on-light Hint text on top of a light-colored background.
--mdc-theme-text-disabled-on-light Disabled text on top of a light-colored background.
--mdc-theme-text-icon-on-light Icons on top of a light-colored background.
Custom property Description
--mdc-theme-text-primary-on-dark Primary text on top of a dark-colored background.
--mdc-theme-text-secondary-on-dark Secondary text on top of a dark-colored background.
--mdc-theme-text-hint-on-dark Hint text on top of a dark-colored background.
--mdc-theme-text-disabled-on-dark Disabled text on top of a dark-colored background.
--mdc-theme-text-icon-on-dark Icons on top of a dark-colored background.

Ideally, we should set all of the text colors on primary, since we never know which one an MDC-Web component might use. Since our cards only contain text and no components, let’s keep it simple for now:

.element-card.earth {
  --mdc-theme-primary: #795548;
  --mdc-theme-text-primary-on-primary: var(--mdc-theme-text-primary-on-dark);
}

.element-card.wind {
  --mdc-theme-primary: #9e9e9e;
  --mdc-theme-text-primary-on-primary: var(--mdc-theme-text-primary-on-light);
}

.element-card.fire {
  --mdc-theme-primary: #f44336;
  --mdc-theme-text-primary-on-primary: var(--mdc-theme-text-primary-on-dark);
}

.element-card.water {
  --mdc-theme-primary: #00bcd4;
  --mdc-theme-text-primary-on-primary: var(--mdc-theme-text-primary-on-light);
}

Note: in the future we plan to provide a Javascript utility method for changing all derived colors and making this use-case easier.

Dark Themes

Beyond what we’ve covered in this document so far, there’s also the concept of a dark theme. All MDC-Web components have been designed to work with both light themes (that assume a light-colored background) and dark themes (with dark-colored backgrounds), but the default is always light.

Note: When using a dark theme, you probably want to choose a dark color as the background for your page, and adjust the MDC-Web background color to match.

In order to apply a dark theme to a single element, you can use its --theme-dark class. For example, for a button:

<button class="mdc-button mdc-button--raised mdc-button--theme-dark">
  Raised dark button
</button>

Alternatively, you can set your entire page (or a portion of it) to a dark theme by using the mdc-theme--dark class:

<section class="mdc-theme--dark">
  <button class="mdc-button mdc-button--raised">
    Still dark
  </button>

  <button class="mdc-button">
    Me too!
  </button>
</section>

Note: there’s currently no way to set a light portion inside of a dark one, so if you want to achieve that effect you’ll need to selectively apply dark classes to everything except the light bits.