npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@stackific/md3

v2.0.0

Published

Material Design 3 framework — SCSS + plain-JS. M3 tokens, palette utilities, dialogs, menus, sliders, snackbars; optional runtime theme generation when the consumer brings material-dynamic-colors.

Downloads

295

Readme

@stackific/md3

Material Design 3 framework. Compiled CSS + plain-JS runtime. Drop into any HTML page; no framework required.

  • 100% class-driven — every component is a class (button, card, field, nav…). md3 never styles bare HTML tags, so your own <button>, <h1>, or <nav> are never restyled — no fighting the framework, and no !important arms race.
  • Light, dark, and OS-following auto modes — CSS-driven, no JS needed.
  • 18 baked brand themes, swap with one attribute.
  • Optional runtime theme generation from a hex / image / file (via material-dynamic-colors).
  • Material Symbols icon fonts ship inside the CSS — no Google Fonts import.

Table of contents


Install

CDN — paste into any HTML page:

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@stackific/md3/dist/md3.css">
<script type="module" src="https://cdn.jsdelivr.net/npm/@stackific/md3/dist/md3.js"></script>

npm / pnpm / yarn:

npm install @stackific/md3
# or: pnpm add @stackific/md3
# or: yarn add @stackific/md3
import "@stackific/md3";        // runtime, registers globalThis.ui
import "@stackific/md3/style";  // compiled CSS

Local files — download dist/md3.css, dist/md3.js, and the dist/assets/* they reference from the published package, then serve them from your own host:

<link rel="stylesheet" href="/md3/md3.css">
<script type="module" src="/md3/md3.js"></script>

For Vite users, build with assetsInlineLimit: 0 to keep the bundled CSS at its original size (otherwise the font files get inlined as data URIs).

Optional — runtime theme generation from a hex / image / file:

npm install material-dynamic-colors
import "material-dynamic-colors";   // once, at app entry
await ui("theme", "#1447E6");

Without it, only baked-theme swaps and precomputed { light, dark } objects work via ui("theme", …).


Quick start

<!doctype html>
<html data-theme="stackific" data-mode="auto">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@stackific/md3/dist/md3.css">
    <script type="module" src="https://cdn.jsdelivr.net/npm/@stackific/md3/dist/md3.js"></script>
    <title>Hello md3</title>
  </head>
  <body>
    <main class="main responsive">
      <h1 class="h1">Hello, md3</h1>
      <nav class="nav">
        <button class="button">Primary</button>
        <button class="button border">Outlined</button>
        <button class="button transparent circle"><i class="icon">search</i></button>
      </nav>
      <article class="card">
        <h5 class="h5">Card</h5>
        <p class="p">Add md3 component classes to semantic markup — your own tags stay untouched.</p>
      </article>
    </main>
  </body>
</html>

That's it. You should see an md3-styled page that picks light or dark automatically from your OS preference.


Bootstrap

<html data-theme="stackific" data-mode="auto">

| Attribute | Values | Notes | | ------------ | ---------------------------- | ------------------------------------------------------ | | data-theme | any baked theme name | Pure attribute swap; no JS required for static pages. Omit to use the default (stackific). | | data-mode | auto | light | dark | auto follows prefers-color-scheme in CSS (no JS). | | data-ui | #some-id | On any element, clicking delegates to ui("#some-id"). Anchors without href also respond to Enter. |

Mode is persisted to localStorage["md3:mode"], theme to localStorage["md3:theme"]. The stored value beats the hardcoded data-mode / data-theme on <html>.


Runtime API

A single global function ui(...) (also import { ui } from "@stackific/md3"). The runtime auto-rescans the DOM via a MutationObserver, so calling ui() after injecting markup is usually optional.

| Call | Effect | | ----------------------------------- | --------------------------------------------------------------- | | ui() | Re-scan the DOM (fields, sliders, ripples, progress, triggers). | | ui("guid") | Returns a UUIDv4 string. | | ui("mode") | Returns current mode: "auto" | "light" | "dark". | | ui("mode", "dark") | Set mode. Persists to localStorage["md3:mode"]. | | ui("themes") | Returns array of baked theme names. | | ui("theme", "stackific") | Swap baked theme. Persists to localStorage["md3:theme"]. | | await ui("theme", "#1447E6") | Generate theme from hex. Requires material-dynamic-colors. | | await ui("theme", url \| File \| Blob) | Generate theme from image / blob. Requires material-dynamic-colors. | | ui("theme", { light, dark }) | Apply precomputed CSS-string per mode. | | await ui("theme") | Returns the currently-applied { light, dark } token strings. | | ui("#my-dialog") | Toggle a <dialog>. | | ui("#my-menu") | Toggle a <menu>. | | ui("#my-snackbar") | Show snackbar for 6000 ms. | | ui("#my-snackbar", 3000) | Show snackbar for N ms. | | ui("#my-snackbar", -1) | Show until clicked. | | ui("#my-page") | Activate a .page (siblings deactivate). | | ui("#anything-else") | Toggle .active on the target. |

ui("#id") toggles: calling it on an open element closes it, calling it on a closed one opens it.

The JS file is almost optional. Theme/mode swapping, dialogs, menus, snackbars, overlays, pages, and tabs all work through the CSS .active class plus data-ui="#id" clicks the browser handles natively. The runtime is required only for: slider value rendering, textarea auto-resize (on browsers without field-sizing), the file/color/password field wiring, and await ui("theme", …) MDC generation.


Main layout

Any element that contains <main class="main"> becomes a grid with slots for edge .navs, .header, .main, and .footer. You don't need to use every slot.

<nav class="nav left">...</nav>
<nav class="nav right">...</nav>
<nav class="nav top">...</nav>
<nav class="nav bottom">...</nav>
<header class="header responsive | fixed">...</header>
<main class="main responsive">...</main>
<footer class="footer responsive | fixed">...</footer>

Grid composition:

nav.left | nav.top    | nav.right
nav.left | header     | nav.right
nav.left | main       | nav.right
nav.left | footer     | nav.right
nav.left | nav.bottom | nav.right

.responsive caps content at 75 rem and centers it. .fixed makes header / footer sticky. For RTL languages set dir="rtl" on <body> or any ancestor.

Compact (mobile)

<nav class="nav bottom">
  <a><i class="icon">home</i><span>Home</span></a>
  <a><i class="icon">search</i><span>Search</span></a>
  <a><i class="icon">share</i><span>Share</span></a>
</nav>
<main class="main responsive">
  <h3 class="h3">Compact</h3>
</main>

Medium (tablet rail)

<nav class="nav left">
  <a><i class="icon">home</i><span>Home</span></a>
  <a><i class="icon">search</i><span>Search</span></a>
  <a><i class="icon">share</i><span>Share</span></a>
</nav>
<main class="main responsive">
  <h3 class="h3">Medium</h3>
</main>

Expanded (desktop drawer)

<nav class="nav left max">
  <a><i class="icon">home</i><span>Home</span></a>
  <a><i class="icon">search</i><span>Search</span></a>
  <a><i class="icon">share</i><span>Share</span></a>
</nav>
<main class="main responsive">
  <h3 class="h3">Expanded</h3>
</main>

Multi-pane

<nav class="nav left">…</nav>
<main class="main responsive">
  <div class="grid">
    <div class="s12 m6 l6"><h3 class="h3">Pane 1</h3></div>
    <div class="s12 m6 l6"><h3 class="h3">Pane 2</h3></div>
  </div>
</main>

Empty state

<div class="fill medium-height middle-align center-align">
  <div class="center-align">
    <i class="icon extra">mail</i>
    <h5 class="h5">You have no new messages</h5>
    <p class="p">Click the button to start a conversation</p>
    <div class="space"></div>
    <nav class="nav center-align">
      <button class="button round">Send a message</button>
    </nav>
  </div>
</div>

Components

Badge

Corner indicators placed inside another element. Default error-colored.

<button class="button circle">
  <i class="icon">mail</i>
  <div class="badge">3</div>
</button>

<button class="button chip circle">
  <i class="icon">mail</i>
  <div class="badge">3</div>
</button>

| Class | Result | | -------------------------------------------------------------------- | ------------------------------------- | | (none) | Top-right of parent, error colors. | | .top / .bottom / .left / .right | Corner override. | | .fill / .primary / .secondary / .tertiary | Role-colored background. | | .border | Outlined on surface background. | | .circle / .square / .round / .no-round / corner-round classes | Shape variants. | | .min | Dot only (no text). | | .none | Inline (not absolutely positioned). |

Button

<button class="button">Button</button>
<a class="button">Link button</a>

<button class="button">
  <i class="icon">home</i>
  <span>Button</span>
</button>

<button class="button"> and <a class="button"> are interchangeable. Variants combine on the same element.

| Class | Result | | -------------------------------------------------------------------------------------- | ------------------------------------------------------------ | | (none) | Filled with primary, 2.5 rem high, fully-rounded. | | .small / .large / .extra | 2 / 3 / 3.5 rem high. | | .border | Outlined; primary-text on transparent background. | | .fill | secondary-container background. | | .primary / .secondary / .tertiary | Role-colored background. | | .transparent | Drops background and box-shadow; for icon buttons. | | .square / .circle | No padding; equal block/inline size. | | .round / .no-round / .small-round / .large-round | Corner-radius scale. | | .left-round / .right-round / .top-round / .bottom-round | Per-corner radius. | | .responsive | Fills the container's inline-size. | | .horizontal / .vertical | Row vs column flow of children. | | .extend | Collapses to icon; expands label on hover or .active. | | .active | primary-container background. | | [disabled] | 50% opacity, no pointer-events. |

Responsive button

Stretches to fill its container.

<button class="button responsive">Button</button>

<button class="button responsive">
  <i class="icon">home</i>
  <span>Button</span>
</button>

FAB

Floating Action Button — the primary action of a screen.

<button class="button circle extra">
  <i class="icon">add</i>
</button>

<button class="button square extra">
  <i class="icon">add</i>
</button>

<button class="button circle extra left-round top-round">
  <i class="icon">add</i>
</button>

Extended FAB

Wider FAB with a text label.

<button class="button extend circle">
  <i class="icon">add</i>
  <span>Create</span>
</button>

<button class="button extend square">
  <i class="icon">add</i>
  <span>Create</span>
</button>

FAB menu

A FAB that opens a menu of related actions.

<div>
  <button class="button circle extra">
    <i class="icon">more_horiz</i>
  </button>
  <menu class="menu group no-wrap small-space top">
    <li><button class="button fill"><i class="icon">search</i><span>Search</span></button></li>
    <li><button class="button fill"><i class="icon">home</i><span>Home</span></button></li>
    <li><button class="button fill"><i class="icon">more_vert</i><span>About</span></button></li>
  </menu>
</div>

Icon button

A transparent button shaped only on hover/press.

<button class="button transparent circle">
  <i class="icon">search</i>
</button>

<button class="button transparent circle">
  <img class="responsive" src="/avatar.png">
</button>

Button groups

Standard groups, connected groups, and split buttons. Children should use directional corner rounds.

<nav class="nav group">
  <button class="button left-round">Left</button>
  <button class="button no-round">Center</button>
  <button class="button right-round">Right</button>
</nav>

<nav class="nav group connected">
  <button class="button left-round">Left</button>
  <button class="button no-round">Center</button>
  <button class="button right-round">Right</button>
</nav>

<nav class="nav group split">
  <button class="button left-round">
    <i class="icon">add_circle</i>
    <span>Action</span>
  </button>
  <button class="button right-round square">
    <i class="icon">keyboard_arrow_down</i>
  </button>
</nav>

Add .secondary / .tertiary on nav.group.split to colorize the group.

Card

<article class="card">
  <h5 class="h5">Title</h5>
  <p class="p">Description</p>
  <nav class="nav">
    <button class="button">Action</button>
  </nav>
</article>

| Class | Result | | ------------------------------------------------------------------------------------------- | ----------------------------------- | | (none) | Elevated, surface-container-low. | | .border | No shadow, outline border. | | .fill / .primary-container / .secondary-container / .tertiary-container | Role-colored background. | | .small / .medium / .large | 12 / 20 / 32 rem block-size. | | .round / .no-round / .left-round / .right-round / .top-round / .bottom-round | Corner radius. | | .padding / .no-padding / .tiny-padding / .small-padding / .medium-padding / .large-padding | Inner padding override. | | .elevate / .no-elevate / .small-elevate / .medium-elevate / .large-elevate | Shadow override. |

Card with leading media

<article class="card">
  <div class="row">
    <img class="circle large" src="/avatar.png">
    <div class="max">
      <h5 class="h5">Title</h5>
      <div>Supporting text</div>
    </div>
  </div>
  <nav class="nav">
    <button class="button">Action</button>
  </nav>
</article>

Card with hero media

<article class="card no-padding">
  <img class="responsive medium" src="/hero.jpg">
  <div class="row absolute bottom left right padding bottom-shadow">
    <h5 class="h5">Title</h5>
    <div class="max"></div>
    <button class="button circle transparent"><i class="icon">more_vert</i></button>
  </div>
</article>

Side-by-side card

<article class="card no-padding">
  <div class="grid no-space">
    <div class="s6"><img class="responsive" src="/photo.jpg"></div>
    <div class="s6">
      <div class="padding">
        <h5 class="h5">Title</h5>
        <p class="p">Description</p>
        <nav class="nav"><button class="button border round">Action</button></nav>
      </div>
    </div>
  </div>
</article>

Checkbox

<label class="checkbox">
  <input type="checkbox">
  <span></span>
</label>

<label class="checkbox">
  <input type="checkbox">
  <span>Click here</span>
</label>

Sizes: .small, (default), .large, .extra.

With icons

<label class="checkbox icons">
  <input type="checkbox">
  <span>
    <i class="icon">close</i>
    <i class="icon">done</i>
  </span>
</label>

Inside a field

<div class="field middle-align">
  <nav class="nav">
    <label class="checkbox"><input type="checkbox"><span>Item 1</span></label>
    <label class="checkbox"><input type="checkbox"><span>Item 2</span></label>
    <label class="checkbox"><input type="checkbox"><span>Item 3</span></label>
  </nav>
  <output>Helper text</output>
</div>

Indeterminate state

Set via JavaScript — there is no HTML attribute for this:

document.getElementById("my-checkbox").indeterminate = true;

Chip

<button class="button chip">Chip</button>

<button class="button chip">
  <i class="icon">home</i>
  <span>Chip</span>
</button>

<a class="chip">Link chip</a>

<button class="chip"> and <a class="chip"> are interchangeable.

| Class | Result | | ------------------------------------------------------------------------ | ------------------------------- | | (none) | 2 rem, outlined. | | .small / .medium / .large | 2 / 2.5 / 3 rem. | | .fill | Filled (no border). | | .primary / .secondary / .tertiary | Role-colored background. | | .border / .no-border | Border control. | | .circle / .square | Equal block/inline size. | | .round / .no-round / corner-round classes | Corner radius. | | .horizontal / .vertical | Child flow. |

Canonical chip patterns:

<!-- Suggestion -->
<button class="button chip">Suggestion</button>

<!-- Input (removable) -->
<button class="button chip">
  <span>Input</span>
  <i class="icon">close</i>
</button>

<!-- Filter -->
<button class="button chip">
  <i class="icon">done</i>
  <span>Filter</span>
</button>

<!-- Assist with leading icon -->
<button class="button chip">
  <i class="icon primary-text">today</i>
  <span>Assist</span>
</button>

<!-- With image -->
<button class="button chip">
  <img src="/favicon.png">
  <span>Image</span>
</button>

Container

The main content of a page.

<main class="main responsive">…</main>

.responsive caps inline-size at 75 rem and centers; .max removes the cap.

Dialog

<dialog class="dialog">
  <h5 class="h5">Title</h5>
  <p class="p">Content of dialog</p>
  <nav class="nav right-align no-space">
    <button class="button transparent link">Cancel</button>
    <button class="button transparent link">Confirm</button>
  </nav>
</dialog>

| Class | Result | | ------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | | (none) | Centered modal, 80% max block-size. | | .small / .medium / .large | 25% / 50% / 75%. | | .left / .right | Slides in from the side, full height. With .small 20 rem / .medium 32 rem / .large 44 rem. | | .top / .bottom | Slides in vertically, full width. With .small 16 rem / .medium 24 rem / .large 32 rem. | | .max | Full-screen. | | .modal | Opens via native showModal() (native backdrop, Escape, scroll lock). | | .fill / .primary-container / .secondary-container / .tertiary-container | Role-colored background. | | .border / .round / .no-round / corner-round classes | Border and corner radius. | | .padding / .no-padding / .tiny-padding / .small-padding / .medium-padding / .large-padding | Padding override. | | .elevate / .no-elevate / .small-elevate / .medium-elevate / .large-elevate | Shadow override. | | .active | Open state (also [open] from native API). |

Opening and closing — 5 methods

1. Add/remove active class

<dialog class="dialog active">…</dialog>

2. Native HTML <dialog> API

document.querySelector("#dialog").show();        // non-modal
document.querySelector("#dialog").showModal();   // modal
document.querySelector("#dialog").close();

3. data-ui attribute

<button class="button" data-ui="#dialog">Open</button>

<dialog class="dialog" id="dialog">
  <h5 class="h5">Title</h5>
  <nav class="nav right-align no-space">
    <button class="button" data-ui="#dialog">Cancel</button>
    <button class="button" data-ui="#dialog">Confirm</button>
  </nav>
</dialog>

4. ui() call

ui("#dialog");

5. Popover API

<button class="button" popovertarget="dialog">Open</button>

<dialog class="dialog" id="dialog" popover>
  <h5 class="h5">Title</h5>
</dialog>
document.querySelector("#dialog").showPopover();
document.querySelector("#dialog").hidePopover();
document.querySelector("#dialog").togglePopover();

Custom overlay

Place an .overlay sibling immediately before the dialog; modifier classes on the overlay are honored:

<div class="overlay blur"></div>
<dialog class="dialog" id="dialog">…</dialog>

Navigation drawer

<div class="overlay"></div>
<dialog id="drawer" class="dialog left">
  <header class="header">
    <nav class="nav">
      <img class="circle large" src="/avatar.png">
      <h6 class="h6 max">Title</h6>
      <button class="button transparent circle large" data-ui="#drawer">
        <i class="icon">close</i>
      </button>
    </nav>
  </header>
  <ul class="list">
    <li class="wave round"><i class="icon">inbox</i><span class="max">Inbox</span><b>24</b></li>
    <li class="wave round"><i class="icon">send</i><span>Outbox</span></li>
    <li class="wave round"><i class="icon">favorite</i><span>Favorites</span></li>
    <li class="wave round"><i class="icon">delete</i><span>Trash</span></li>
  </ul>
</dialog>

Divider

<hr class="divider">
<hr class="divider small">
<hr class="divider medium">
<hr class="divider large">

<div class="divider"></div>
<div class="divider vertical"></div>

.small / .medium / .large control vertical margin (0.5 / 1 / 1.5 rem). .vertical produces a vertical rule.

Expansion

Use native <details> with <summary class="summary">.

<details>
  <summary class="summary">Header</summary>
  <p class="p">Body content</p>
</details>

Multi-level:

<details>
  <summary class="summary">Level 1</summary>
  <details>
    <summary class="summary">Level 2</summary>
    <details>
      <summary class="summary">Level 3</summary>
      <p class="p">Body</p>
    </details>
  </details>
</details>

Custom summary (hides the marker):

<details>
  <summary class="summary none">
    <button class="button">Custom trigger</button>
  </summary>
  <p class="p">Body</p>
</details>

Field

Wrapper for inputs, selects, textareas, and composite controls.

<div class="field border">
  <input type="text">
</div>

.label is optional; with it, the label floats above the input when focused or filled.

<div class="field label border">
  <input type="text" placeholder=" ">
  <label>Label</label>
</div>

| Class | Result | | ------------------------------------------- | ------------------------------------------------- | | .label | Adds a floating label child. | | .border | Outlined. | | .fill | Filled (surface-variant). | | .round | Pill shape. | | .small / (default) / .large / .extra | 2.5 / 3 / 3.5 / 4 rem input height. | | .prefix | Extra start-padding for a leading icon child. | | .suffix | Extra end-padding for a trailing icon child. | | .invalid | Error coloring; shows <output class="invalid">. |

Input

<div class="field border">
  <input type="text">
</div>

<div class="field border">
  <input type="text">
  <output>Helper text</output>
</div>

<div class="field invalid border">
  <input type="text">
  <output class="invalid">Error text</output>
</div>

<div class="field label border">
  <input type="text" placeholder=" ">
  <label>Label</label>
</div>

<div class="field label prefix border">
  <i class="icon">search</i>
  <input type="text">
  <label>Label</label>
</div>

<div class="field label suffix border">
  <input type="text">
  <label>Label</label>
  <i class="icon">visibility</i>
</div>

<div class="field label prefix suffix border">
  <i class="icon">search</i>
  <input type="text">
  <label>Label</label>
  <i class="icon">tune</i>
</div>

Clickable prefix/suffix icons — wrap in <a> or use <i class="front">:

<div class="field label prefix border">
  <a><i class="icon">search</i></a>
  <input type="text">
  <label>Label</label>
</div>

<div class="field label prefix border">
  <i class="icon front">search</i>
  <input type="text">
  <label>Label</label>
</div>

Floating-label triggers — either add placeholder=" " (pure CSS) or let the runtime toggle .active:

<!-- CSS-only -->
<div class="field label border">
  <input type="text" placeholder=" ">
  <label>Label</label>
</div>

<!-- Manual control -->
<div class="field label border">
  <input type="text" class="active">
  <label class="active">Label</label>
</div>

Password fields auto-wire a visibility toggle when a child <i>visibility</i> is present:

<div class="field label suffix border">
  <input type="password">
  <label>Password</label>
  <i class="icon">visibility</i>
</div>

Select

<div class="field suffix border">
  <select>
    <option>Item 1</option>
    <option>Item 2</option>
    <option>Item 3</option>
  </select>
  <i class="icon">arrow_drop_down</i>
</div>

<div class="field label suffix border">
  <select>
    <option>Item 1</option>
    <option>Item 2</option>
  </select>
  <label>Label</label>
  <i class="icon">arrow_drop_down</i>
  <output>Helper text</output>
</div>

Textarea

Auto-resizes unless [rows] is set.

<div class="field border">
  <textarea></textarea>
</div>

<div class="field border">
  <textarea rows="10"></textarea>
</div>

<div class="field label border">
  <textarea placeholder=" "></textarea>
  <label>Label</label>
</div>

Search

Search field with auto-suggestion menu.

<div class="field large prefix round fill">
  <i class="icon front">search</i>
  <input>
  <menu class="menu min">
    <li class="transparent">
      <div class="field large prefix">
        <i class="icon front">arrow_back</i>
        <input>
      </div>
    </li>
    <li><i class="icon">history</i><div>Recent 1</div></li>
    <li><i class="icon">history</i><div>Recent 2</div></li>
    <li><i class="icon">history</i><div>Recent 3</div></li>
  </menu>
</div>

FAB-anchored search:

<div>
  <button class="button extra circle fill">
    <i class="icon">search</i>
  </button>
  <menu class="menu no-wrap left min">
    <li class="transparent">
      <div class="field large prefix">
        <i class="icon front">arrow_back</i>
        <input>
      </div>
    </li>
    <li><i class="icon">history</i><div>Recent 1</div></li>
  </menu>
</div>

Custom inputs (file, color, date, time)

Wrap a hidden input in a button trigger; the runtime mirrors values back into a sibling text input.

<div>
  <button class="button circle"><i class="icon">attach_file</i></button>
  <input type="file">
</div>

<div>
  <button class="button circle"><i class="icon">palette</i></button>
  <input type="color">
</div>

<div>
  <button class="button circle"><i class="icon">today</i></button>
  <input type="date">
</div>

<div>
  <button class="button circle"><i class="icon">schedule</i></button>
  <input type="time">
</div>

<!-- With label -->
<div>
  <button class="button"><i class="icon">attach_file</i><span>File</span></button>
  <input type="file">
</div>

Grid

12-column grid with 1 rem gap. Cell wrappers live immediately inside .grid — don't put .grid and .s1.s12 on the same element.

<div class="grid">
  <div class="s12 m6 l3"><h5 class="h5">First</h5></div>
  <div class="s12 m6 l3"><h5 class="h5">Second</h5></div>
  <div class="s12 m6 l3"><h5 class="h5">Third</h5></div>
  <div class="s12 m6 l3"><h5 class="h5">Fourth</h5></div>
</div>

| Class | Active | | ---------------- | ------------------ | | .s1.s12 | Always. | | .m1.m12 | ≥ 601 px. | | .l1.l12 | ≥ 993 px. |

Gap modifiers on .grid: .no-space, .space, .small-space, .medium-space (1.5 rem), .large-space (2 rem).

Do put component elements inside the cell:

<div class="grid">
  <div class="s12 m6 l3"><article class="card">…</article></div>
  <div class="s12 m6 l3"><div class="field">…</div></div>
</div>

🚫 Don't put grid classes directly on the element:

<div class="grid">
  <article class="card s12 m6 l3">…</article>      <!-- breaks layout -->
</div>

Header & Footer

<header class="header">
  <nav class="nav">
    <button class="button circle transparent"><i class="icon">arrow_back</i></button>
    <h5 class="h5 max">Title</h5>
    <button class="button circle transparent"><i class="icon">more_vert</i></button>
  </nav>
</header>

<footer class="footer">
  <nav class="nav">
    <button class="button circle transparent"><i class="icon">check_box</i></button>
    <button class="button circle transparent"><i class="icon">brush</i></button>
    <div class="max"></div>
    <button class="button square round extra primary"><i class="icon">add</i></button>
  </nav>
</footer>

| Class | Result | | ---------------------------------- | ------------------------------------------------------------- | | (none) | 4 rem min (header), 5 rem min (footer). | | .fixed | Sticky inside the container. | | .responsive | Caps content at 75 rem and centers it. | | .max | Removes the 75 rem cap. |

When .fixed and a scrollable parent are used together, header/footer stick to the parent's top/bottom edge:

<article class="card small-width small-height scroll">
  <header class="header fixed bold">Fixed header</header>
  <p class="p">Long body content…</p>
  <p class="p">…that scrolls underneath.</p>
  <footer class="footer fixed bold">Fixed footer</footer>
</article>

Icon

Material Symbols ligature. Four font variants ship inside the CSS bundle.

<i class="icon">home</i>

<i class="icon">
  <svg viewBox="0 0 24 24">
    <path d="M10,20V14H14V20H19V12H22L12,3L2,12H5V20H10Z"></path>
  </svg>
</i>

<i class="icon">
  <img src="/icon.png">
</i>

| Class | Size | | -------- | -------- | | .tiny | 1 rem | | .small | 1.25 rem | | (none) | 1.5 rem | | .large | 1.75 rem | | .extra | 2 rem | | .fill | Filled (FILL=1) variation. Also auto-applied inside <a class="active"> / <button class="active">. |

Switch font variant via --font-icon:

:root { --font-icon: "Material Symbols Rounded"; }
/* "Material Symbols Outlined" (default), "Material Symbols Rounded",
   "Material Symbols Sharp", "Material Symbols Subset" (smaller bundle), or none */

Sharing one SVG sprite across multiple icons:

<svg viewBox="0 0 24 24" style="display: none">
  <g id="home"><path d="M10,20V14H14V20H19V12H22L12,3L2,12H5V20H10Z"/></g>
  <g id="star"><path d="M12,17.27L18.18,21L16.54,13.97L22,9.24L14.81,8.62L12,2L9.19,8.62L2,9.24L7.45,13.97L5.82,21L12,17.27Z"/></g>
</svg>

<i class="icon"><svg viewBox="0 0 24 24"><use href="#home"/></svg></i>
<i class="icon"><svg viewBox="0 0 24 24"><use href="#star"/></svg></i>

Third-party icon fonts work too (Font Awesome, MDI, …) — load them yourself:

<i class="icon fa-regular fa-clock"></i>
<i class="icon mdi mdi-clock-outline"></i>

Layout

Positioning containers — absolute (relative to parent) or fixed (relative to viewport).

<article class="card small">
  <div class="absolute left bottom right">
    <h5 class="h5">Pinned to container bottom</h5>
  </div>
</article>

<div class="fixed left bottom right">
  <h5 class="h5">Pinned to viewport bottom</h5>
</div>

| Class | Effect | | -------------------------------------------------------------- | ----------------------------------------------- | | .absolute / .fixed | Positioning context. | | .left / .right / .top / .bottom | Pin to that edge. | | .front / .back | z-index +10 / −10. | | .center / .middle | Horizontal / vertical centering. | | .small / .medium / .large | With .left.right: 20 / 28 / 44 rem block-size. With .top.bottom: same inline-size. |

<article class="card small">
  <span class="absolute center middle">Centered</span>
</article>

<article class="card small center-align middle-align">
  <span>Centered via alignment</span>
</article>

List

<ul class="list">
  <li>Item</li>
  <li>Item</li>
</ul>

<ol class="list">
  <li>Item</li>
</ol>

| Class | Result | | --------------------------------------------------------- | ---------------------------- | | (none) | 3.5 rem rows, no dividers. | | .border | Bottom divider between rows. | | .no-space / .space / .small-space | 2.5 / 3 / 3.5 rem rows. | | .medium-space / .large-space | 4.5 / 5.5 rem rows. |

Nested

<ul class="list">
  <li>Item</li>
  <li>
    <ul class="list">
      <li>Nested</li>
      <li>Nested</li>
    </ul>
  </li>
</ul>

Expansion list

<ul class="list">
  <li>Item</li>
  <li>
    <details>
      <summary class="summary">Header</summary>
      <ul class="list">
        <li>Inner</li>
      </ul>
    </details>
  </li>
</ul>

Headline and supporting text

<ul class="list border">
  <li>
    <button class="button circle">A</button>
    <div class="max">
      <h6 class="h6 small">Headline</h6>
      <div>Supporting text</div>
    </div>
    <label>+15 min</label>
  </li>
</ul>

With leading icon or avatar:

<ul class="list">
  <li>
    <i class="icon">home</i>
    <div class="max">
      <h6 class="h6 small">Headline</h6>
      <div>Supporting text</div>
    </div>
    <label>+15 min</label>
  </li>
  <li>
    <img class="round" src="/avatar.png">
    <div class="max">
      <h6 class="h6 small">Headline</h6>
      <div>Supporting text</div>
    </div>
  </li>
</ul>

Media

<img src="/image.png" class="circle extra">

<video class="circle extra">
  <source src="/video.mp4" type="video/mp4">
</video>

<svg class="circle extra" viewBox="0 0 24 24">
  <path d="…"/>
</svg>

| Class | Result | | ------------------------------------------------------------------ | --------------------------------------------------- | | .tiny / .small / (default) / .large / .extra | 2 / 2.5 / 3 / 3.5 / 4 rem square. | | .circle / .round / .square | Border-radius style. | | .no-round / .left-round / .right-round / .top-round / .bottom-round | Per-corner radius. | | .responsive | 100% inline-size. | | .responsive.tiny / .small / .medium / .large / .extra | Fixed block-size 4 / 8 / 12 / 16 / 20 rem. | | .empty-state | Max 24 rem wide; for empty-state illustrations. |

Menu

<div>
  <button class="button">
    <span>Menu</span>
    <i class="icon">arrow_drop_down</i>
  </button>
  <menu class="menu">
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
  </menu>
</div>

With links:

<div>
  <button class="button">
    <span>Links</span>
    <i class="icon">arrow_drop_down</i>
  </button>
  <menu class="menu">
    <li><a href="#">Item 1</a></li>
    <li><a href="#">Item 2</a></li>
  </menu>
</div>

With divider:

<menu class="menu">
  <li>Item 1</li>
  <li><hr class="divider"></li>
  <li>Item 2</li>
</menu>

Selected state:

<menu class="menu">
  <li>Item 1</li>
  <li class="active">Item 2</li>
  <li>Item 3</li>
</menu>

| Class | Result | | --------------------------------------------------------------------------------------- | ----------------------------------------------- | | (none) | Anchored below trigger, full width. | | .no-wrap | Width fits content; never wraps. | | .wrap | Allow item wrap. | | .min | Compact anchored popover. | | .max | Full-screen. | | .top / .bottom / .left / .right | Open direction / alignment. | | .border | Outlined. | | .group | Visually merges with parent (no shadow / bg). | | .no-space / .space / .tiny-space / .small-space / .medium-space / .large-space / .extra-space | Gap scale. |

Submenu

<div>
  <button class="button">
    <span>Menu</span>
    <i class="icon">arrow_drop_down</i>
  </button>
  <menu class="menu">
    <li>
      <span>Submenu</span>
      <menu class="menu">
        <li>Item</li>
        <li>Item</li>
      </menu>
    </li>
  </menu>
</div>

Grouped menu

.group makes the menu transparent and stacks groups visually.

<div>
  <button class="button">
    <span>Grouped</span>
    <i class="icon">arrow_drop_down</i>
  </button>
  <menu class="menu group">
    <li>
      <menu class="menu">
        <li>Item</li>
        <li>Item</li>
      </menu>
    </li>
    <li>
      <menu class="menu">
        <li>Item</li>
        <li>Item</li>
      </menu>
    </li>
  </menu>
</div>

Opening and closing

Three patterns:

  1. Active class: <menu class="active">…</menu>
  2. data-ui attribute: <div data-ui="#menu"><menu id="menu">…</menu></div>
  3. ui() call: ui("#menu")

Clicking outside an open menu closes it automatically.

Navigation

<nav class="nav"> and .row are interchangeable containers — <nav> is semantic, .row is not.

<nav class="nav">
  <button class="button">Button</button>
  <a class="chip">Chip</a>
  <a><img class="circle" src="/avatar.png"></a>
  <label class="checkbox"><input type="checkbox"></label>
</nav>

<div class="row">
  <div>min</div>
  <div class="max">max</div>
  <div>min</div>
</div>

| Class on <nav> / .row | Result | | ----------------------------------------------------------------------------------------------- | ------------------------------------- | | .no-space / .tiny-space / .small-space / .medium-space / .large-space | Gap: 0 / 0.5 / 1 / 1.5 / 2 rem. | | .left-align / .center-align / .right-align | Justify-content (also text-align). | | .top-align / .middle-align / .bottom-align | Align-items. | | .horizontal / .vertical | Flow direction. | | .wrap / .no-wrap | Flex-wrap behavior. | | .min | Inline-flex; shrinks to content. | | .max | flex: 1 (children with .max grow). | | .border / .round / .no-round / corner-round classes | Border and corner radius. | | .margin / .no-margin / .tiny-margin / .small-margin / .medium-margin / .large-margin | Outer margin. | | .fill / .primary-container / .secondary-container / .tertiary-container | Role-colored background. | | .elevate / .no-elevate / .small-elevate / .medium-elevate / .large-elevate | Shadow. |

Navigation rail (vertical edge)

<nav class="nav left">
  <a><i class="icon">home</i><div>Home</div></a>
  <a><i class="icon">search</i><div>Search</div></a>
  <a><i class="icon">more_vert</i><div>More</div></a>
</nav>

<nav class="nav left max">
  <a><i class="icon">home</i><div>Home</div></a>
  <a><i class="icon">search</i><div>Search</div></a>
</nav>

Navigation bar (horizontal edge)

<nav class="nav bottom">
  <a><i class="icon">home</i><div>Home</div></a>
  <a><i class="icon">search</i><div>Search</div></a>
  <a><i class="icon">more_vert</i><div>More</div></a>
</nav>

<nav class="nav top max">
  <a><i class="icon">home</i><div>Home</div></a>
</nav>

Tabbed nav

<nav class="nav tabbed">
  <a class="active"><i class="icon">info</i><span>Overview</span></a>
  <a><i class="icon">style</i><span>Specs</span></a>
  <a><i class="icon">design_services</i><span>Guidelines</span></a>
</nav>

nav.tabbed: default 4 rem height, .small 3 rem, .large 5 rem.

Toolbar

<nav class="nav toolbar">
  <a><i class="icon">videocam_off</i></a>
  <a><i class="icon">mic</i></a>
  <a class="active"><i class="icon">front_hand</i></a>
  <a><i class="icon">more_vert</i></a>
</nav>

.fill (primary-container), .vertical, .max (full width) all work on nav.toolbar.

Group / connected / split

<nav class="nav group">
  <button class="button left-round">Left</button>
  <button class="button no-round">Center</button>
  <button class="button right-round">Right</button>
</nav>

<nav class="nav group connected">
  <button class="button left-round">Left</button>
  <button class="button no-round">Center</button>
  <button class="button right-round">Right</button>
</nav>

<nav class="nav group split">
  <button class="button left-round"><i class="icon">add_circle</i><span>Action</span></button>
  <button class="button right-round square"><i class="icon">keyboard_arrow_down</i></button>
</nav>

List form

<nav class="nav">
  <ul>
    <li><button class="button">Button</button></li>
    <li><a class="chip">Chip</a></li>
  </ul>
</nav>

Overlay

Scrim that blocks the screen. Used with dialogs or as a standalone loading curtain.

<div class="overlay center-align middle-align">
  <progress class="progress circle"></progress>
</div>

| Class | Result | | --------------------------------------------------------------------------- | ------------------------------- | | .active | Shown. | | .blur / .small-blur / .medium-blur / .large-blur | Backdrop-blur effect. | | .left-align / .right-align / .center-align / .top-align / .bottom-align / .middle-align | Align inner content. |

Opening and closing — same three patterns:

  1. Active class: <div class="overlay active">…</div>
  2. data-ui: <button data-ui="#overlay">Show</button> + <div class="overlay" id="overlay">…</div>
  3. ui(): ui("#overlay")

Page

<div class="page active">
  <h5 class="h5">Title</h5>
</div>

| Class | Result | | ---------------------------------------------- | ------------------------------------- | | .active | Visible. | | .left / .right / .top / .bottom | Entry transform direction. |

Activating a page — three patterns:

  1. Active class: add .active to one, remove from siblings.
  2. data-ui: <a data-ui="#page1">Open</a> — siblings at the same level deactivate.
  3. ui(): ui("#page1").

Progress

<progress class="progress"></progress>                                <!-- indeterminate -->
<progress class="progress" value="25" max="100"></progress>           <!-- linear -->

<progress class="progress wavy"></progress>
<progress class="progress wavy" value="25" max="100"></progress>

<progress class="progress circle"></progress>                 <!-- circular indeterminate -->
<progress class="progress circle" value="25" max="100"></progress>

<progress class="progress circle wavy"></progress>
<progress class="progress circle wavy" value="25" max="100"></progress>

| Class | Result | | ---------------- | -------------------------------------------- | | (none) | Linear, 0.25 rem thick. | | .small / .medium / .large | 0.25 / 0.35 / 0.45 rem thick. | | .indeterminate | Forces animated indeterminate state. | | .wavy | Wavy SVG track (linear or circle). | | .circle | Circular; .small 1.5 rem / (default) 2.5 / .large 3.5. | | .max | Absolutely fills the parent (use inside <article>, <button>, …). |

A bare <progress></progress> is auto-promoted to indeterminate at boot.

Radio

<label class="radio">
  <input type="radio">
  <span></span>
</label>

<label class="radio">
  <input type="radio">
  <span>Click here</span>
</label>

Sizes: .small, (default), .large, .extra.

With icons

<label class="radio icons">
  <input type="radio">
  <span>
    <i class="icon">close</i>
    <i class="icon">done</i>
  </span>
</label>

Group inside a field

<div class="field middle-align">
  <nav class="nav">
    <label class="radio"><input type="radio" name="g"><span>Item 1</span></label>
    <label class="radio"><input type="radio" name="g"><span>Item 2</span></label>
    <label class="radio"><input type="radio" name="g"><span>Item 3</span></label>
  </nav>
  <output>Helper text</output>
</div>

Shape

SVG-masked decorative element. Place inside <button>, <div>, or a sized container.

<div class="shape sunny"></div>

<div class="shape sunny">
  <img class="responsive" src="/favicon.png">
</div>

<div class="small-width small-height">
  <div class="shape sunny max"></div>
</div>

| Class | Result | | ------------------------------------------------------------------------------ | ------------------------------- | | .tiny / .small / (default) / .medium / .large / .extra | 2.5 / 3 / 3.5 / 4.5 / 5.5 / 6.5 rem. | | .max | Fills the parent. | | .space / .no-space / .tiny-space / .small-space / .medium-space / .large-space / .extra-space | Inner mask padding. | | .rotate / .slow-rotate / .fast-rotate | Rotation animation (12 s / 24 s / 6 s). |

Shape names (one per class):

arch, arrow, boom, bun, burst, circle, clamshell, diamond, fan, flower, gem, ghost-ish, heart, leaf-clover4, leaft-clover8, loading-indicator, oval, pentagon, pill, pixel-circle, pixel-triangle, puffy, puffy-diamond, semicircle, sided-cookie4, sided-cookie6, sided-cookie7, sided-cookie9, sided-cookie12, slanted, soft-boom, soft-burst, square, sunny, triangle, very-sunny, wavy, wavy-circle.

Inside a button:

<button class="button circle extra transparent">
  <span class="shape sided-cookie12 max medium-space"></span>
</button>

Spinner:

<div class="shape sided-cookie12 transparent rotate">
  <button class="button responsive">
    <i class="icon">search</i>
  </button>
</div>

Slider

Default range is 0–100.

<div class="slider">
  <input type="range">
  <span></span>
</div>

<div class="slider">
  <input type="range" min="4" max="8">
  <span></span>
</div>

| Class | Track size | | ------------------------------------------------------------------ | ---------- | | .tiny | 1 rem | | .small | 1.5 rem | | .medium | 2.5 rem | | .large | 3.5 rem | | .extra | 6 rem | | .vertical | Rotates 90°. | | .max | Fills the parent (container slider). |

Dual thumb

<div class="slider">
  <input type="range" value="25">
  <input type="range" value="50">
  <span></span>
</div>

Value tooltip

<div class="slider">
  <input type="range">
  <span></span>
  <div class="tooltip"></div>
</div>

Inset icon

The icon appears only with .medium, .large, or .extra.

<div class="slider medium">
  <input type="range">
  <span><i class="icon">sunny</i></span>
</div>

Inside a field

<div class="field middle-align">
  <div class="slider">
    <input type="range">
    <span></span>
  </div>
  <output>Helper</output>
</div>

Container slider

<article class="card">
  <div class="slider max">
    <input type="range">
    <span></span>
  </div>
</article>

Snackbar

<div class="snackbar">
  <i class="icon">warning</i>
  <span>I'm a snackbar</span>
</div>

| Class | Result | | ----------------------------------------------------------- | --------------------------------------- | | (none) | Bottom-centered; auto-hides after 6 s. | | .top / .bottom | Anchor position. | | .error / .primary / .secondary / .tertiary | Role-colored background. | | .active | Shown. |

With action

<div class="snackbar">
  <div class="max">Item moved to trash</div>
  <a class="inverse-primary-text">Undo</a>
</div>

Opening — four patterns:

  1. Active class: add .active.

  2. data-ui: <button data-ui="#snack">Show</button>.

  3. ui(): ui("#snack") — default 6000 ms; ui("#snack", 3000) for a custom timeout; ui("#snack", -1) to keep open until click.

  4. Popover API:

    <button class="button" popovertarget="snack">Show</button>
    <div class="snackbar" id="snack" popover>I'm a snackbar</div>
    document.querySelector("#snack").showPopover();
    document.querySelector("#snack").hidePopover();
    document.querySelector("#snack").togglePopover();

Only one snackbar shows at a time — opening one dismisses any open siblings.

Switch

<label class="switch">
  <input type="checkbox">
  <span></span>
</label>

<nav class="nav">
  <div class="max">
    <h6 class="h6">Title</h6>
    <div>Complementary text</div>
  </div>
  <label class="switch">
    <input type="checkbox">
    <span></span>
  </label>
</nav>

Sizes: .small, (default), .large, .extra.

With icons

<label class="switch icons">
  <input type="checkbox">
  <span><i class="icon">wifi</i></span>
</label>

<label class="switch icons">
  <input type="checkbox">
  <span>
    <i class="icon">close</i>
    <i class="icon">done</i>
  </span>
</label>

Inside a field

<div class="field middle-align">
  <nav class="nav">
    <div class="max">
      <h6 class="h6">Title</h6>
      <div>Complementary text</div>
    </div>
    <label class="switch">
      <input type="checkbox">
      <span></span>
    </label>
  </nav>
</div>

Table

<table class="table">
  <thead>
    <tr><th>Header</th><th>Header</th></tr>
  </thead>
  <tbody>
    <tr><td>Cell</td><td>Cell</td></tr>
    <tr><td>Cell</td><td>Cell</td></tr>
  </tbody>
  <tfoot>
    <tr><th>Footer</th><th>Footer</th></tr>
  </tfoot>
</table>

| Class on <table> | Result | | ---------------------------------------------------------------------------------------- | ----------------- | | .border | Row dividers. | | .stripes | Zebra rows. | | .no-space / .space / .small-space / .medium-space / .large-space | Cell padding. | | .left-align / .center-align / .right-align | Text alignment. |

<th class="fixed">, <thead class="fixed">, <tfoot class="fixed"> for sticky headers/footers inside a scrolling container. <td class="min"> / <th class="min"> shrinks the cell to its content.

If you want to change the background of the alternate rows of the table:

.my-striped-table {
  --active: var(--secondary-container); /* any md3 role token */
}

Apply the class on the (or an ancestor).

Scroll

<div class="scroll small-height">
  <table class="table">
    <thead class="fixed">
      <tr><th>Header</th><th>Header</th></tr>
    </thead>
    <tbody>
      <tr><td>Cell</td><td>Cell</td></tr>
    </tbody>
  </table>
</div>

Tabs

<div class="tabs">
  <a class="active">Tab 1</a>
  <a>Tab 2</a>
  <a>Tab 3</a>
</div>

<div class="page padding active"><h5 class="h5">Tab 1</h5></div>
<div class="page padding"><h5 class="h5">Tab 2</h5></div>
<div class="page padding"><h5 class="h5">Tab 3</h5></div>

| Class on .tabs | Result | | --------------------------------------------------------- | --------------------- | | .small / .large | 2 / 4 rem min height. | | .min | Shorter active underline. | | .max | Tabs grow to fill. | | .left-align / .center-align / .right-align | Justify-content. | | .horizontal / .vertical | Flow direction. |

Activating tabs — two patterns:

  1. Active class: add .active to one tab <a> and one matching .page, remove from siblings.
  2. data-ui: <a data-ui="#page1">Tab 1</a> — the matching .page activates, siblings hide.

Tooltip

A child of the trigger element.

<button class="button">
  <span>Button</span>
  <span class="tooltip">I'm a tooltip</span>
</button>

<div>
  <button class="button chip round">Hover me</button>
  <div class="tooltip">I'm a tooltip</div>
</div>

| Class | Result | | -------------------------------------------------- | ----------------------------------- | | (none) | Above the parent. | | .left / .right / .bottom | Position override. | | .small / .medium / .large | 8 / 12 / 16 rem wide, multi-line. | | .max | Block-level 20 rem rich tooltip. | | .no-space / .medium-space / .large-space | Gap from anchor: 0 / −1 / −1.5 rem. |

Rich tooltip

<div>
  <button class="button chip round">Rich tooltip</button>
  <div class="tooltip max">
    <p class="p">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
    <nav class="nav">
      <button class="button">Action</button>
    </nav>
  </div>
</div>

Typography

<h1 class="h1">Display</h1>
<h2 class="h2">Display</h2>
<h3 class="h3">Display</h3>
<h4 class="h4">Headline</h4>
<h5 class="h5">Headline</h5>
<h6 class="h6">Headline</h6>

Heading size modifiers: .small, .large.

Formatting

<a class="link">link</a>
<a class="inverse-link">inverse-link</a>
<p class="p italic">italic</p>
<p class="p bold">bold</p>
<p class="p underline">underline</p>
<p class="p overline">overline</p>
<p class="p upper">upper</p>
<p class="p lower">lower</p>
<p class="p capitalize">capitalize</p>
<p class="p small-text">small (0.75 rem)</p>
<p class="p medium-text">medium (0.875 rem)</p>
<p class="p large-text">large (1 rem)</p>
<p class="p truncate">long single-line text…</p>

Line spacing

.no-line, .tiny-line (1.25 rem), .small-line (1.5), .medium-line (1.75), .large-line (2), .extra-line (2.25).

Blockquote, pre, code

<blockquote class="blockquote">Quoted text.</blockquote>

<pre class="pre">Preformatted.</pre>

<pre class="pre">
  <code class="code">console.log("hello");</code>
</pre>

<p class="p">
  The function <code class="code">console.log()</code> prints a log message.
</p>

<pre> and <blockquote> modifiers: .border, .no-border, .scroll.


Helpers

Alignments

<div class="center-align middle-align">…</div>

.left-align, .right-align, .center-align (justify + text), .top-align, .middle-align, .bottom-align (align-items).

Blurs

Backdrop-blur with a translucent background.

<header class="header blur">…</header>
<article class="card blur">…</article>
<button class="button blur">Bl