One stylesheet, one script, one class on <html>. No bundler required; components lazy-load their own chunks.
npm install advanced-material-webShips components, TypeScript types, theme.css, and material.css. Serve node_modules/advanced-material-web/css/theme.css (importable as advanced-material-web/theme.css) and, on the no-bundler path, the ESM entry from your static assets.
One self-contained ESM file; every <material-*> registers on load:
<html lang="en" class="light">
<head>
<link rel="stylesheet" href="https://unpkg.com/advanced-material-web/css/theme.css">
<link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0">
<script type="module" src="https://unpkg.com/advanced-material-web"></script>
</head>
<body>
<material-button variant="filled" label="It works"></material-button>
</body>
</html>https://unpkg.com/advanced-material-web resolves to the single eager bundle (cdn/material.min.js, ~590 KB, all 72 components). For pages using a handful of components, prefer the lazy loader — small entry, chunks on demand: https://unpkg.com/advanced-material-web/dist/material/material.esm.js.
<html lang="en" class="light">
<head>
<link rel="stylesheet" href="/static/material/theme.css">
<link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0">
<!-- one eager bundle … -->
<script type="module" src="/static/material/material.min.js"></script>
<!-- … or the lazy loader (chunks fetched on demand) -->
<!-- <script type="module" src="/static/material/material.esm.js"></script> -->
</head>
<body>
<material-button variant="filled" label="It works"></material-button>
</body>
</html>Three requirements, each load-bearing:
theme.cssin the host page (light DOM) — ships the--md-sys-color-*custom properties. They cascade into every shadow tree; without it components render un-themed.- A theme class on
<html>— one oflight,dark,light-medium-contrast,dark-medium-contrast,light-high-contrast,dark-high-contrast. No class → no tokens. Seetheming.md. - Material Symbols Outlined font — every
icon="..."attribute is a Material Symbols ligature name (search,arrow_back,delete). Without the font, icons render as raw text.
Everything the library styles outside a shadow root, linked like theme.css:
<link rel="stylesheet" href="/static/material/theme.css">
<link rel="stylesheet" href="/static/material/material.css">Two things are in it:
material-data-tableandmaterial-breadcrumbs. Almost every component's CSS rides inside its JS chunk, scoped to its shadow root; these two enhance markup that stays in the light DOM (a server-rendered<table>, a real<nav>), so their styling can't. Any component whose readme says "Styles live in the document stylesheet" needs this file.- The MD3 type scale and a form grid as classes —
md-typescale-body-large,md-gridwith--md-span. For pages with no Tailwind build; seereferences/theming.md.
Skip it and only those two components render unstyled; everything else is fine. Tailwind projects get the type scale and grid from their own build instead (advanced-material-web/tailwind.css) and need material.css only for the two components.
// Lazy loader — recommended, components load on demand
import { defineCustomElements } from 'advanced-material-web/loader';
import 'advanced-material-web/theme.css';
defineCustomElements();
// Or import only what you use (each self-registers)
import 'advanced-material-web/dist/components/material-button.js';The theme class and the icon font still have to be in the host page — a webfont link can't be bundled.
- Tags and attributes are kebab-case:
<material-date-field first-day-of-week="1">. - Events are
CustomEvents with adetailpayload, namedvalueChange/checkedChange/material*(materialSort,materialStepChange, …). - Form controls are form-associated:
name+ a plain<form>gives real values, real constraint validation, real reset. Never add hidden inputs. - If a component looks unstyled, the cause is nearly always a missing theme class on
<html>or a 404 on the bundle.