Developed by a core team of engineers and UX designers at Google, Material Components (MDC) helps developers execute Material Design. MDC has over 20 beautiful and functional UI components for the web platform and is also available for Android and iOS.

This codelab will show you some situations where Material Components for the web (MDC-Web) can make your app more functional and beautiful, while saving you engineering time.

What you will build

In this codelab, you're going to complete Shrine, an e-commerce app that sells clothing and home goods. Your app will:

Components you'll be using

What you'll need

This codelab is focused on using Material Components. (Non-relevant concepts will not be covered.) Code blocks are for you to simply copy and paste.

Download the starter codelab app

Download starter app

Or clone it from GitHub

git clone https://github.com/material-components/material-components-web
cd material-components-web/docs/codelabs/building-beautiful-sites/starter

Install the project's dependencies

We will be using live-server module as our local web development server so we can serve our site as if it were running in a production web application. Live-server automatically updates the web page whenever any HTML, CSS, or JavaScript changes are made. There is no refreshing needed! Live-server is listed as a devDependency for this project, which means we can install it using npm.

  1. From the starter directory (it should be your current directory if you follow the above step), run npm install. You will see a lot of activity and at the end, the output will show a successful install.

Run the starter app

  1. Run npm run dev in the same directory (starter directory) in which you just ran npm install. The live-server will start. It watches the directory for source code changes and launches a web browser pointing to the page.

Voila! Shrine is running in your browser. You can scroll through the page to see a list of product cards.

While far from the end result we want, our skeleton site is looking pretty good already! Let's take a look around.

Explore index.html

Open up index.html in the starter directory. It should look like the following:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  <title>Shrine (MDC-Web Example App)</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" sizes="192x192" href="https://material.io/static/images/simple-lp/favicons/components-192x192.png">
   <link rel="shortcut icon" href="https://material.io/static/images/simple-lp/favicons/components-72x72.png">

  <link rel="stylesheet"
        href="https://cdnjs.cloudflare.com/ajax/libs/normalize/6.0.0/normalize.min.css">
  <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet">
  <link rel="stylesheet"
        href="https://unpkg.com/material-components-web@0.9.1/dist/material-components-web.min.css">
  <link rel="stylesheet" href="app.css">
</head>
<body class="mdc-typography">
  <h1 class="mdc-typography--display4">Shrine</h1>
  <main>
    <div class="mdc-card shrine-product-card">
      <section class="mdc-card__primary">
        <span class="mdc-card__title shrine-product-card__price">$20</span>
      </section>
      <img class="shrine-product-card__image" width="240" height="240" alt="Sunglasses" src="assets/sunnies.png">
    </div>
    <!-- ... ->

The first thing to notice here is that we're using a <link> tag to load a fully-built version of MDC-Web via the unpkg CDN. We publish MDC-Web via NPM. Using unpkg is a great way to experiment with MDC-Web without the need to immediately include it as a dependency. Note that we include normalize.css for consistent cross-browser rendering, as well as the Roboto font from Google Fonts.

Let's look at what's in the <body> of the document. What you'll mainly notice is a bunch of mdc-* CSS classes. These css classes are provided to you by MDC-Web (free of charge 😉). Some of the classes already in use are:

Explore app.css

You might have noticed some additional css classes in the HTML above, like shrine-product-card. These styles are defined in app.css. Open app.css and you will see the following:

html, body {
  height: 100%;
  background-color: #f2f2f2;
}

.shrine-product-card {
  margin: 0 auto;
  width: 320px;
  border-radius: 4px;
  margin-bottom: 8px;
  background-color: white;
}

.shrine-product-card__price {
  display: block;
  text-align: right;
}

.shrine-product-card__image {
  margin: 0 auto;
}

The top style declaration is used to add some basic styling to the page. However, the subsequent style declarations are attached to classes present within our mdc-card elements. MDC-Web treats its DOM structure as part of its public API. It is highly encouraged for you to create the design your product requires, by making custom style modifications to components via your own classes.

Now that you're familiar with the starter code, let's implement our first feature.

Let's say you bring your beautiful e-commerce skeleton site to a designer on your team. The first thing the designer suggests is to add a toolbar to better convey the branding and ensure the user always knows where they are.

It is easy to implement the toolbar using MDC-Web's mdc-toolbar component. Use a fixed toolbar, because it has elevation and floats above the main content.

Add the Material Icons font

To show the hamburger menu navigation icon in the mocks, add the Material Icons font to the web page.

In index.html, add the following directly before the <link> to material-components-web.css

<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">

You may use the icon font as soon as it loads onto the web page.

Add the proper mdc-toolbar markup

In index.html, replace the h1 tag with the following markup:

<header id="shrine-header"
        class="mdc-toolbar mdc-toolbar--fixed mdc-theme--text-primary-on-background">
  <div class="mdc-toolbar__row">
    <section class="mdc-toolbar__section mdc-toolbar__section--align-start">
      <a id="shrine-nav-icon" class="material-icons" href="#"
         aria-label="Click to show the navigation menu"
         aria-controls="shrine-nav-menu">menu</a>
      <h1 id="shrine-logo"          
          class="mdc-toolbar__title"><span>Shrine</span></h1>
    </section>
  </div>
</header>

Add the custom mdc-toolbar styles

Add the following styles to app.css:

#shrine-header {
  background-color: var(--mdc-theme-background);
  color: var(--mdc-theme-text-primary-on-background);
}

#shrine-header .mdc-toolbar__section {
  overflow: visible;
}

#shrine-logo {
  background: url(assets/logo.png) left center no-repeat;
  background-size: contain;
  width: 100%;
  height: 100%;
}

/* Hide actual text for screen readers */
#shrine-logo > span {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  border: 0;
}

#shrine-nav-icon {
  width: 24px;
  height: 24px;
  margin-right: 40px;
  text-decoration: none;
}

#shrine-nav-icon:visited,
#shrine-nav-icon:active,
#shrine-nav-icon:focus {
  color: var(--mdc-theme-text-primary-on-background);
}

Adjust the main content margin to account for a fixed toolbar

Alter the opening <main> tag so it will look like this:

<main class="mdc-toolbar-fixed-adjust">

You will now see a toolbar! There was no JavaScript required!

Explanation:

Finishing Touch: Add an ink ripple to the nav icon

The user will touch or click on options in the navigation menu and receive feedback from the ink ripple, the insignia of Material Design. The ink ripple is the first component we're using that requires JavaScript, so we'll add it to the page. Thankfully, instantiating MDC-Web JS components is simple.

Alter the <a id="shrine-nav-icon" ...> element to look like this:

<a id="shrine-nav-icon" class="material-icons mdc-ripple-surface"  
    href="#" 
   aria-label="Click to show the navigation menu"
   aria-controls="shrine-nav-menu"
   data-mdc-auto-init="MDCRipple"
   data-mdc-ripple-is-unbounded>menu</a>

Add the following markup at the bottom of index.html, right above the closing </body> tag:

<script src="https://unpkg.com/material-components-web@0.9.1/dist/material-components-web.min.js">
</script>
<script>mdc.autoInit()</script>

That's it! You will see an ink ripple when you click on the navigation icon.

There are a few key items to notice:

The toolbar is complete. To make the navigation icon functional, let's add a navigation drawer.

Now that our app is set up to use JavaScript, adding a navigation drawer should be simple. Here's the mock our designer has given us for the navigation drawer:

Let's add this to our app. Use the Temporary Drawer from the mdc-drawer package in order to achieve this functionality.

Add the drawer markup

Add the markup below for the temporary drawer in index.html, right below the markup for the toolbar and before the <main> element:

<aside id="shrine-nav-menu" class="mdc-temporary-drawer" data-mdc-auto-init="MDCTemporaryDrawer">
  <nav class="mdc-temporary-drawer__drawer">
    <header class="mdc-temporary-drawer__header"></header>
    <nav class="mdc-temporary-drawer__content mdc-list">
      <a class="mdc-list-item" href="#">Home</a>
      <a class="mdc-list-item" href="#">Clothing</a>
      <a class="mdc-list-item" href="#">Popsicles</a>
    </nav>
  </nav>
</aside>

Add the drawer custom styles

Add the following to app.css to style the drawer to reflect the mocks:

#shrine-nav-menu {
  text-transform: uppercase;
  padding-left: 16px;
}

#shrine-nav-menu .mdc-temporary-drawer__drawer {
  background-color: #fafafa;
}

#shrine-nav-menu .mdc-temporary-drawer__header {
  background: url(assets/logo.png) 32px 32px no-repeat;
  background-size: 30%;
}

#shrine-nav-menu .mdc-temporary-drawer__header::before {
  padding-top: 30%;
}

#shrine-nav-menu .mdc-temporary-drawer__content {
  background: url(assets/diamond.svg) -32px bottom no-repeat;
  background-size: 50%;
}

#shrine-nav-menu .mdc-list-item {
  height: 32px;
  padding-left: 32px;
  letter-spacing: .2em;
}

Add JavaScript to open the drawer when the navigation icon is clicked

Alter the <script>mdc.autoInit()</script> tag to show the following:

<script>
  mdc.autoInit();
  document.getElementById('shrine-nav-icon').addEventListener('click', function(evt) {
    evt.preventDefault();
    document.getElementById('shrine-nav-menu').MDCTemporaryDrawer.open = true;
  });
</script>

Clicking the navigation icon within the toolbar will display a navigation drawer.

The Shrine app is coming along nicely! The designers are very pleased. The final step in the design is to lay out the product items to look better than a bunch of vertical cards. Use mdc-layout-grid because it implements Material Design's Responsive UI Grid, making product items look great across all form factors.

Turn the main content element into a layout grid

In index.html, alter the <main> element so that it looks like the following:

<main id="shrine-products" class="mdc-layout-grid mdc-toolbar-fixed-adjust">

Wrap all of the product items in grid cells

Wrap each one of the mdc-card product items in a mdc-layout-grid__cell div, like this:

<div class="mdc-layout-grid__cell">
  <div class="mdc-card shrine-product-card">
    <section class="mdc-card__primary">
      <span class="mdc-card__title shrine-product-card__price">$20</span>
    </section>
    <img class="shrine-product-card__image" width="240" height="240" alt="Sunglasses" src="assets/sunnies.png">
  </div>
</div>
<!-- ... -->

Add the custom styles for the Shrine product grid

Add the following at the bottom of app.css:

#shrine-products {
  --mdc-layout-grid-gutter: 8px;
}

#shrine-products .mdc-layout-grid__cell {
  display: flex;
  justify-content: center;
}

Remove the unneeded styles from the shrine-product-card css class

Alter the .shrine-product-card class so that it looks like the following:

.shrine-product-card {
  width: 320px;
  border-radius: 4px;
  margin-bottom: 8px;
  background-color: white;
}


That's it! You should have a responsive product grid that looks great across all form factors. Test it out yourself by resizing the browser window.

By using some simple markup, ~100 lines of CSS, ~4 lines of JavaScript, and the Material Components for the web library, you have the start of a beautiful e-commerce app that conforms to the Material Design guidelines and looks great across all devices. Your designer is happy, and you get to leave work early.

Next steps

While this plain HTML/CSS/JavaScript approach is good for this simple example, a production e-commerce site (or any modern web application) will most likely be using a framework. MDC-Web was built from the ground up with framework interoperability in mind, and can seamlessly integrate into all modern web development frameworks. We encourage you to check out the framework integration guide, look at some framework integration examples, and explore the rest of Material Components. Thanks so much for trying MDC-Web. We hope you enjoyed this codelab!