Web Expressive Documentation

Web Expressive is a toolkit built on web standards that simplifies and accelerates UI development for the web. It brings the design intent of Material 3 Expressive to HTML and CSS through design tokens, components, and adaptable layout patterns.

Table of contents

Getting Started

Web Expressive helps you build performant and beautiful web applications with:

Quick Start (CDN)

  1. Create a new index.html file.
  2. Copy the starter template below:
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Web Expressive Starter</title>

    <!-- 1. Typography -->
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto+Flex:wght@400..700&display=swap" />

    <!-- 2. Core (reset + tokens) -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/web-expressive@1.1.0/dist/core.css" />

    <!-- 3. A color scheme -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/web-expressive@1.1.0/dist/schemes/alpha/fuchsia-tonal.css" />

    <!-- 4. Components (as needed) -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/web-expressive@1.1.0/dist/components/button.css" />

    <style>
      html {
        --ref-typeface-base: "Roboto Flex";
      }
      .headline {
        font: var(--sys-typescale-headline-large);
      }
    </style>
  </head>
  <body>
    <h1 class="headline">Hello, World!</h1>
    <button class="button tonal large">Get Started</button>
  </body>
</html>
A buildless starter loading Web Expressive from a CDN with Roboto Flex, the Fuchsia Tonal color scheme, and a tonal button.

You can also download the standalone starter template.

From here, you can start assembling your UI with tokens, adding components, or exploring ready-made example layouts as a starting point.

Installation via npm

Install the package in your project:

npm install web-expressive

Import the required stylesheets into your global CSS:

@import "web-expressive/core.css";
@import "web-expressive/schemes/alpha/fuchsia-tonal.css"; /* Replace with any of the available schemes. */
@import "web-expressive/components/button.css";         /* Optional: import only the components you use. */

Modular Imports

Web Expressive organizes design system resources into four modular imports categories: core, schemes, components, and shapes. Embed stylesheets in your HTML <head> via CDN or import them into CSS via npm.

Resource Stylesheet Requirement Description
Core core.css Required CSS reset and system tokens (typography, spacing, corners, motion, elevation).
Scheme schemes/[collection]/[scheme].css Required Color roles for light and dark modes. Single scheme recommended.
Components components/[component].css Optional Modular interactive components. Import only what you use.
Shapes shapes/clip-tokens.css Optional 35 Material 3 Expressive abstract shape clipping and masking paths.

Load core first, followed by your chosen scheme. Then add the components and shape tokens your page uses.

Via CDN: Embed the stylesheets in your HTML <head>:

<!-- Required: core and one color scheme -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/web-expressive@1.1.0/dist/core.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/web-expressive@1.1.0/dist/schemes/alpha/fuchsia-tonal.css" />

<!-- Optional: components used on this page -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/web-expressive@1.1.0/dist/components/button.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/web-expressive@1.1.0/dist/components/icon-button.css" />

<!-- Optional: shape tokens for clip-path or mask-image -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/web-expressive@1.1.0/dist/shapes/clip-tokens.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/web-expressive@1.1.0/dist/shapes/mask-tokens.css" />

Via npm: Import the necessary modules at the top of your CSS build entry file.

/* Required: core and one color scheme */
@import "web-expressive/core.css";
@import "web-expressive/schemes/alpha/fuchsia-tonal.css";

/* Optional: components used on this page */
@import "web-expressive/components/button.css";
@import "web-expressive/components/icon-button.css";

/* Optional: shape tokens for clip-path or mask-image */
@import "web-expressive/shapes/clip-tokens.css";
@import "web-expressive/shapes/mask-tokens.css";

Replace the scheme with your preferred one and omit any optional imports you do not need.

Theming

Theming is the architectural backbone of Web Expressive. It empowers products to express distinct visual identities without starting from scratch, while providing granular control over every color, corner, typeface, and layout dimension.

Theming Overview

Web Expressive theming is built on four structural pillars:

Pillar Mechanism What It Provides
1. Color Schemes Static stylesheets via light-dark() Instant swapping between 50+ curated color schemes with automatic dark and light modes.
2. Typeface Mapping @property registered reference tokens Decoupled font loading; map any brand font to the entire typescale with one token.
3. Density & Shape APIs Container style queries (--size-items, --shape-items, --width-items) Declaratively adjust component density, width, and corner roundness per container.
4. Zero-Friction Overrides CSS Cascade Layers (@layer design-system) Project styles override library rules naturally without specificity battles or !important.

Understanding Tokens in Web Expressive

Web Expressive exposes the full set of system tokens documented in this reference as CSS custom properties prefixed with --sys-*. These are the public tokens you use to build and style your interfaces.

In addition to system tokens, Web Expressive provides configuration custom properties that function as public component APIs. These include container style query properties (--size-items, --width-items, and --shape-items) to declaratively adjust component sizing, width, and corner rounding per container, as well as typeface configuration properties (--ref-typeface-*) to customize font families across the typescale.

Web Expressive also exposes its focus-ring motion tokens (--focus-ring-motion-spring and --focus-ring-motion-duration). Focus rings are a common interaction behavior across Web Expressive components, and these tokens are made available so you can apply the same spring outline behavior to your own custom components.

When inspecting components in browser DevTools, you will also see custom properties prefixed with --_*. These are private CSS custom properties used internally to build the components. Be cautious when overriding them, as incorrect overrides can break component styling.

Choosing a Scheme

A color scheme is a group of colors assigned to semantic roles, which are then mapped to UI elements. Web Expressive represents these roles as CSS custom properties with built-in support for light and dark modes through the native CSS light-dark() function. See a color scheme stylesheet example.

Web Expressive provides two parallel collections with identical filenames and CSS variable names. Switch between them anytime by adding or removing alpha/ in your import path.

Collection Import Path Based On Visual Explorer
Alpha (Default) schemes/alpha/[scheme].css Newer Android releases featuring richer, juicier color generation. Alpha Explorer
Standard schemes/[scheme].css Official Material Design 3 guidelines and Figma toolkit. Standard Explorer

Preview all 50+ schemes live, test light and dark modes, and grab your import snippet instantly using the Alpha Schemes Explorer or the Standard Schemes Explorer.

CDN Import Example

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/web-expressive@1.1.0/dist/schemes/alpha/fuchsia-tonal.css" />

npm Import Example

@import "web-expressive/schemes/alpha/fuchsia-tonal.css";

The Four Variant Flavors

Variant Families (50+ Palettes)

Each family is derived from a key source color and provides all four tonal variants:

Family Available Files
Aqua aqua-tonal.css, aqua-neutral.css, aqua-vibrant.css, aqua-expressive.css
Berry berry-tonal.css, berry-neutral.css, berry-vibrant.css, berry-expressive.css
Carbon carbon-tonal.css, carbon-neutral.css, carbon-vibrant.css, carbon-expressive.css
Classic classic-tonal.css, classic-neutral.css, classic-vibrant.css, classic-expressive.css
Forest forest-tonal.css, forest-neutral.css, forest-vibrant.css, forest-expressive.css
Fuchsia fuchsia-tonal.css, fuchsia-neutral.css, fuchsia-vibrant.css, fuchsia-expressive.css
Paper paper-tonal.css, paper-neutral.css, paper-vibrant.css, paper-expressive.css
Porcelain porcelain-tonal.css, porcelain-neutral.css, porcelain-vibrant.css, porcelain-expressive.css
Sun sun-tonal.css, sun-neutral.css, sun-vibrant.css, sun-expressive.css

Specialized Single-Color Schemes

Single-color schemes feature untinted neutral surfaces (pure white, gray, black) while preserving color across key roles.

Scheme Filename
Blue blue.css
Green green.css
Indigo indigo.css
Orange orange.css
Red red.css
Violet violet.css
Yellow yellow.css

Specialized Dual-Color Schemes

Dual-color schemes combine harmonious dual hues for playful and expressive color combinations between the surfaces and the accents.

Scheme Filename
Blue and Green blue-green.css
Green and Orange green-orange.css
Light Purple and Blue light-purple-blue.css
Pink and Indigo pink-indigo.css
Yellow and Red yellow-red.css

Foundational Schemes

Scheme Description Filename
Baseline Standard Material 3 baseline palette baseline.css
Monochrome Pure neutral grayscale palette monochrome.css

Single vs. Multiple Schemes in One Document

To scope a secondary scheme to an isolated container, use the data-theme attribute:

<!-- Global scheme applies to main document -->
<button class="button">Global Primary</button>

<!-- Scoped secondary scheme on an isolated section -->
<section data-theme="fuchsia-tonal">
  <button class="button tonal">Scoped Fuchsia Action</button>
</section>

Caution: Scoping multiple schemes requires importing both scheme stylesheets. Use multiple schemes sparingly.

Handling Modes

Web Expressive features a unified theming engine where colors and elevation shadows adapt in perfect synchrony across light and dark modes.

1. Automatic System Adaptation (Default)

Out of the box, Web Expressive configures color-scheme: light dark; on html in core.css, while elevation tokens automatically invert their key and ambient shadow layers for dark surfaces using @media (prefers-color-scheme: dark).

Your application automatically honors the visitor's operating system preferences with zero configuration or JavaScript required.

2. Explicit Mode Control (Data Attribute or Class)

Native CSS color-scheme only manages color keywords and light-dark() calculations—it does not alter box shadows. To toggle or force modes, Web Expressive provides built-in mode selectors in core.css that update both color-scheme and elevation tokens in lockstep:

<!-- Force Dark Mode on the entire page -->
<html lang="en" data-mode="dark">

<!-- Or via class -->
<html lang="en" class="dark-mode">

Tip (3-State Toggles): To restore automatic OS adaptation (system mode), simply remove the data-mode attribute (or mode class), or set data-mode="system"; the document naturally falls back to default color-scheme: light dark and responsive elevation shadows.

3. Scoped Island Theming (Subtree Overrides)

Mode selectors work on any element in your layout. To render an inverted dark card or sidebar inside an otherwise light page, apply data-mode="dark" or .dark-mode directly to that container. All child colors and elevation shadows switch locally:

<!-- An inverted dark card inside a light document -->
<div class="card dark-mode">
  <h4>Featured Dark Card</h4>
  <button class="button tonal">Action</button>
</div>

Customizing Typefaces

Web Expressive decouples font loading from typography tokens, making font configuration clean, modular, and centralized.

In CSS, the font-family property combines specific font family names with a generic family fallback. Web Expressive separates these into two distinct custom properties: specific font names are set on --ref-typeface-* (which accepts only a comma-separated list of quoted strings, such as "Inter" or "Inter", "Arial", "Helvetica"), while the generic fallback is set on --ref-typeface-generic (such as sans-serif or serif). Because the initial value for --ref-typeface-generic is sans-serif, you only need to declare it if your typography requires a different generic family fallback.

Load your chosen web font using any provider (such as Google Fonts) or via @font-face definitions. For the typescale tokens to render accurately across all standard and emphasized styles, your loaded font family should include weights 400 (Regular), 500 (Medium), and 700 (Bold).

Important: All typeface customizations must be declared on the html selector (or :root) so typography tokens resolve correctly across the page.

[html root custom properties]
 │
 ├─► (None Defined) 
 │   └─► Fallback to [--ref-typeface-generic] ("sans-serif")
 │
 ├─► [--ref-typeface-base] (Global Cascade)
 │   └─► Cascades down to ALL [--sys-typescale-*] & [--sys-typescale-emphasized-*] tokens
 │
 ├─► Baseline Role Splits:
 │   ├── [--ref-typeface-brand] ──────────► [--sys-typescale-* (Title Large ──► Display Large)]
 │   └── [--ref-typeface-plain] ──────────► [--sys-typescale-* (Label Small ──► Title Medium)]
 │
 └─► Emphasized Role Splits:
     ├── [--ref-typeface-emphasized-brand] ► [--sys-typescale-emphasized-* (Title Large ──► Display Large)]
     └── [--ref-typeface-emphasized-plain] ► [--sys-typescale-emphasized-* (Label Small ──► Title Medium)]

To change the font family for the full typography system, override the base variable:

html {
  --ref-typeface-base: "Open Sans";
}

To split the family by role, override the brand and plain references separately:

html {
  --ref-typeface-brand: "Inter"; /* Display, Headline, Title large */
  --ref-typeface-plain: "Open Sans"; /* Title small/medium, Body, Label */
}

Emphasized typography inherits from the brand and plain references by default, but you can override those families separately:

html {
  --ref-typeface-emphasized-brand: "Anton"; /* Display Emphasized, Headline Emphasized, Title large Emphasized */
  --ref-typeface-emphasized-plain: "Anton"; /* Title small/medium, Body, Label */
}

You can provide one or more fallback fonts, but all font names must be quoted:

html {
  --ref-typeface-base: "Inter", "Arial", "-apple-system", "BlinkMacSystemFont";
}

Use --ref-typeface-generic for the generic fallback. It defaults to sans-serif and accepts one unquoted value from the following list: serif, sans-serif, system-ui, monospace, cursive, fantasy, math.

html {
  --ref-typeface-generic: cursive;
}

Tip: Prioritize changing --ref-typeface-base first. Only specialize roles if the design intent requires distinct visual weights or stylistic contrast.

To explore how Web Expressive maps custom properties to CSS font shorthands under the hood, read The Power of CSS Shorthands.

Managing Overrides

Web Expressive eliminates specificity battles. By utilizing CSS Cascade Layers, project styles override library tokens and components naturally without !important.

1. Built-in Cascade Layers (@layer design-system)

All Web Expressive rules are encapsulated within the design-system layer. For normal author declarations, unlayered styles take precedence over layered styles regardless of selector specificity:

[ Unlayered User Styles ]  (Highest layer precedence for normal author declarations)
            │
            ▼
[ User Defined Layers ]    (e.g., @layer app, declared after design-system)
            │
            ▼
[ @layer design-system ]   (Web Expressive library defaults)

2. Adapting a Scheme (Role Overrides)

Override individual roles on html (or :root) to customize specific colors without editing the library:

html {
  --sys-color-error: light-dark(oklch(50% 0.18 29), oklch(83% 0.07 22));
  --sys-color-on-error: light-dark(oklch(100% 0 0), oklch(32% 0.11 28));
  --sys-color-error-container: light-dark(oklch(92% 0.03 23), oklch(42% 0.15 28));
  --sys-color-on-error-container: light-dark(oklch(25% 0.08 28), oklch(92% 0.03 23));
}

3. Typography Token Resolution

Web Expressive uses the CSS font shorthand exclusively for typescale tokens. Shorthand values resolve at the element where they are declared. Always declare --ref-typeface-* overrides on html (or :root) to ensure the entire typescale recalculates correctly.

Tokens Reference

Design tokens are the foundational building blocks of Web Expressive. They store visual attributes, including colors, typography, elevation, motion, spacing, and shapes, as standardized CSS custom properties.

Tokens Overview

Web Expressive organizes design tokens across 7 core categories:

Category Stylesheet Source Description Token Prefix
Color schemes/[scheme].css Accessible color roles mapped via light-dark() --sys-color-*
Typography core.css 30 typescale shorthand font tokens (15 baseline, 15 emphasized) across 5 roles --sys-typescale-*
Elevation core.css 6 elevation levels represented by CSS box-shadow values --sys-elevation-*
Motion core.css Expressive motion curves, durations, and focus-ring motion tokens --sys-motion-* / --focus-ring-motion-*
Measurement Space core.css 18-step spacing scale based on an 8px unit --sys-measurement-space-*
Shape Corners core.css 10 corner radius tokens from sharp (0px) to pill (9999px) --sys-shape-corner-*
Abstract Shapes shapes/clip-tokens.css 35 organic and geometric clipping and masking paths --sys-shape-clip-* / --sys-shape-mask-*

Composing System Tokens in Custom CSS

/* A custom card built entirely by composing system tokens. */
.card {
  background: var(--sys-color-surface-container);
  color: var(--sys-color-on-surface);
  border: 1px solid var(--sys-color-outline-variant);
  border-radius: var(--sys-shape-corner-medium);
  padding: var(--sys-measurement-space-300);
  gap: var(--sys-measurement-space-150);
  box-shadow: var(--sys-elevation-level-1);
  transition:
    box-shadow var(--sys-motion-spring-fast-effects-duration) var(--sys-motion-spring-fast-effects),
    transform var(--sys-motion-spring-fast-spatial-duration) var(--sys-motion-spring-fast-spatial);

  &:hover {
    box-shadow: var(--sys-elevation-level-2);
    transform: translateY(-2px);
  }
}

.title {
  font: var(--sys-typescale-title-large);
}

.supporting-text {
  font: var(--sys-typescale-body-medium);
  color: var(--sys-color-on-surface-variant);
}

Attribution: Token descriptions and guidelines are adapted from the official Material Design 3 Styles Documentation.

Color

Per Material 3 guidelines, color roles are like the "numbers" in a paint-by-number canvas. They're the connective tissue between elements of the UI and what color goes where. These roles are implemented in design and code through tokens.

Web Expressive uses CSS custom properties with built-in light-dark() to represent these tokens.

Color roles are organized into groups: primary, secondary, tertiary, error, surface, and outline, along with optional add-on roles for fixed accents, bright and dim surfaces, and utility elevation.

General Concepts

Helpful-to-know terms used in color role names:

Pairing and Layering Colors: The color system is built on accessible color pairings. These color pairs provide an accessible minimum 3:1 contrast. To ensure accessible visual contrast in your app, apply colors only in the intended pairs or layering orders. Combining colors improperly may break contrast necessary for visual accessibility.

.card {
  background: var(--sys-color-surface-container);
  color: var(--sys-color-on-surface);
  border: 1px solid var(--sys-color-outline-variant);
}

Primary

Use primary roles for the most prominent components across the UI, such as the FAB, high-emphasis buttons, and active states.

Token Description
--sys-color-primary High-emphasis fills, texts, and icons against surface
--sys-color-on-primary Text and icons against primary
--sys-color-primary-container Standout fill color against surface, for key components like FAB
--sys-color-on-primary-container Text and icons against primary container

Secondary

Use secondary roles for less prominent components in the UI such as filter chips.

Token Description
--sys-color-secondary Less prominent fills, text, and icons against surface
--sys-color-on-secondary Text and icons against secondary
--sys-color-secondary-container Less prominent fill color against surface, for recessive components like tonal buttons
--sys-color-on-secondary-container Text and icons against secondary container

Tertiary

Use tertiary roles for contrasting accents that balance primary and secondary colors or bring heightened attention to an element such as an input field. Tertiary color roles can be applied at the designer's discretion to support broader color expression.

Token Description
--sys-color-tertiary Complementary fills, text, and icons against surface
--sys-color-on-tertiary Text and icons against tertiary
--sys-color-tertiary-container Complementary container color against surface, for components like input fields
--sys-color-on-tertiary-container Text and icons against tertiary container

Error

Use error roles to communicate error states, such as an incorrect password entered into a text field. Error color roles are made static by default with any dynamic color scheme while still adapting to light and dark modes.

Token Description
--sys-color-error Attention-grabbing color against surface for fills, icons, and text, indicating urgency
--sys-color-on-error Text and icons against error
--sys-color-error-container Attention-grabbing fill color against surface
--sys-color-on-error-container Text and icons against error container

Surface

Use surface roles for more neutral backgrounds, and container colors for components like cards, sheets, and dialogs.

For surface container roles, surface container is the default role, but the others are especially helpful for creating hierarchy and nested containers in layouts for expanded screens.

Token Description
Surface Roles
--sys-color-surface Default color for backgrounds
--sys-color-on-surface Text and icons against any surface or surface container color
--sys-color-on-surface-variant Lower-emphasis color for text and icons against any surface or surface container color
--sys-color-surface-variant Decorative surface alternative to standard surface; lower-emphasis surface variant
Surface Container Roles (Emphasis Hierarchy)
--sys-color-surface-container-lowest Lowest-emphasis container color
--sys-color-surface-container-low Low-emphasis container color
--sys-color-surface-container Default container color
--sys-color-surface-container-high High-emphasis container color
--sys-color-surface-container-highest Highest-emphasis container color
Inverse Colors (Reverse Contrast)
--sys-color-inverse-surface Background fills for elements which contrast against surface
--sys-color-inverse-on-surface Text and icons against inverse surface
--sys-color-inverse-primary Actionable elements, such as text buttons, against inverse surface

Outline

There are two outline colors to be used against a surface:

Token Description
--sys-color-outline Important boundaries, such as a text field outline
--sys-color-outline-variant Decorative elements, such as dividers, and when other elements provide 4.5:1 contrast

Applying Outline Colors:

Add-on Color Roles

Most products won't need to use these add-on color roles. However, some products require the greater flexibility and control that add-on roles provide. If you aren't sure whether your product should use the add-on roles, it probably shouldn't and you can ignore them.

Fixed Accent Colors

Primary fixed, secondary fixed, and tertiary fixed are fill colors used against surface. These colors maintain the same tone in light and dark modes, as opposed to regular container colors, which change in tone between these modes. The fixed color role may be used instead of the equivalent container role in situations where such fixed behavior is desired.

The primary fixed dim, secondary fixed dim, and tertiary fixed dim roles provide a stronger, more emphasized tone relative to the equivalent fixed color. They may be used where a deeper color but the same fixed behavior is desired.

On fixed colors are used for text and icons which sit on top of the corresponding fixed color. On fixed variant colors are used for text and icons needing lower emphasis against the corresponding fixed color.

Token Description
--sys-color-primary-fixed Fill color used against surface; maintains the same tone in light and dark modes
--sys-color-primary-fixed-dim Stronger, more emphasized tone relative to primary fixed; same tone in light and dark modes
--sys-color-on-primary-fixed Text and icons which sit on top of the primary fixed color
--sys-color-on-primary-fixed-variant Text and icons needing lower emphasis against the primary fixed color
--sys-color-secondary-fixed Fill color used against surface; maintains the same tone in light and dark modes
--sys-color-secondary-fixed-dim Stronger, more emphasized tone relative to secondary fixed; same tone in light and dark modes
--sys-color-on-secondary-fixed Text and icons which sit on top of the secondary fixed color
--sys-color-on-secondary-fixed-variant Text and icons needing lower emphasis against the secondary fixed color
--sys-color-tertiary-fixed Fill color used against surface; maintains the same tone in light and dark modes
--sys-color-tertiary-fixed-dim Stronger, more emphasized tone relative to tertiary fixed; same tone in light and dark modes
--sys-color-on-tertiary-fixed Text and icons which sit on top of the tertiary fixed color
--sys-color-on-tertiary-fixed-variant Text and icons needing lower emphasis against the tertiary fixed color

Bright and Dim Surface Roles

There are two add-on surface roles: surface dim and surface bright. While the default surface color automatically inverts between light and dark modes (it's a light color in light mode and it flips to a dark color in dark mode), the surface bright and surface dim colors keep their relative brightness across both light and dark modes.

Token Description
--sys-color-surface-dim Dimmest surface color in light and dark modes
--sys-color-surface-bright Brightest surface color in light and dark modes

Additional Framework & Utility Roles

Web Expressive schemes also expose utility elevation and canvas fallback custom properties:

Token Description
--sys-color-surface-tint Legacy surface tint color, retained for compatibility. Surface tint is deprecated in Material 3.
--sys-color-shadow Color used for elevation drop shadows
--sys-color-scrim Overlay color for backdrop scrims behind modals and sheets
--sys-color-background Foundational app background canvas (equivalent to --sys-color-surface)
--sys-color-on-background Main body text against the background canvas (equivalent to --sys-color-on-surface)

Typography

The Material 3 Expressive type scale consists of 30 type styles: 15 baseline styles and 15 emphasized styles. Both style sets follow the same scale across five distinct roles: Display, Headline, Title, Body, and Label, each available in Large, Medium, and Small.

For interactive specifications, variable font axes, and language considerations, explore the official Material Design 3 Typography Documentation.

Style Roles & Hierarchy

Roles describe the functional purpose and context of text across layouts:

When to Use Baseline vs. Emphasized Styles

Use baseline and emphasized type styles together to achieve expressive experiences:

Implementation Note: Web Expressive packages each typescale into a single CSS font shorthand token (bundling font-weight, font-size/line-height, and font-family). Always consume typescale variables via the shorthand font property. Tracking is not included in these shorthand tokens; use letter-spacing separately when needed.

/* Standard content uses baseline styles */
.headline {
  font: var(--sys-typescale-display-small);
}

/* Expressive hero headlines use emphasized styles */
@scope (.hero) {
  .headline {
    font: var(--sys-typescale-emphasized-display-large);
  }
}

Typescale tokens reference (15 baseline and 15 emphasized styles):

Role Baseline Style Emphasized Style
Display --sys-typescale-display-large 400 • 3.5625rem / 4rem (57px / 64px) --sys-typescale-emphasized-display-large 500 • 3.5625rem / 4rem (57px / 64px)
--sys-typescale-display-medium 400 • 2.8125rem / 3.25rem (45px / 52px) --sys-typescale-emphasized-display-medium 500 • 2.8125rem / 3.25rem (45px / 52px)
--sys-typescale-display-small 400 • 2.25rem / 2.75rem (36px / 44px) --sys-typescale-emphasized-display-small 500 • 2.25rem / 2.75rem (36px / 44px)
Headline --sys-typescale-headline-large 400 • 2rem / 2.5rem (32px / 40px) --sys-typescale-emphasized-headline-large 500 • 2rem / 2.5rem (32px / 40px)
--sys-typescale-headline-medium 400 • 1.75rem / 2.25rem (28px / 36px) --sys-typescale-emphasized-headline-medium 500 • 1.75rem / 2.25rem (28px / 36px)
--sys-typescale-headline-small 400 • 1.5rem / 2rem (24px / 32px) --sys-typescale-emphasized-headline-small 500 • 1.5rem / 2rem (24px / 32px)
Title --sys-typescale-title-large 400 • 1.375rem / 1.75rem (22px / 28px) --sys-typescale-emphasized-title-large 500 • 1.375rem / 1.75rem (22px / 28px)
--sys-typescale-title-medium 500 • 1rem / 1.5rem (16px / 24px) --sys-typescale-emphasized-title-medium 700 • 1rem / 1.5rem (16px / 24px)
--sys-typescale-title-small 500 • 0.875rem / 1.25rem (14px / 20px) --sys-typescale-emphasized-title-small 700 • 0.875rem / 1.25rem (14px / 20px)
Body --sys-typescale-body-large 400 • 1rem / 1.5rem (16px / 24px) --sys-typescale-emphasized-body-large 500 • 1rem / 1.5rem (16px / 24px)
--sys-typescale-body-medium 400 • 0.875rem / 1.25rem (14px / 20px) --sys-typescale-emphasized-body-medium 500 • 0.875rem / 1.25rem (14px / 20px)
--sys-typescale-body-small 400 • 0.75rem / 1rem (12px / 16px) --sys-typescale-emphasized-body-small 500 • 0.75rem / 1rem (12px / 16px)
Label --sys-typescale-label-large 500 • 0.875rem / 1.25rem (14px / 20px) --sys-typescale-emphasized-label-large 700 • 0.875rem / 1.25rem (14px / 20px)
--sys-typescale-label-medium 500 • 0.75rem / 1rem (12px / 16px) --sys-typescale-emphasized-label-medium 700 • 0.75rem / 1rem (12px / 16px)
--sys-typescale-label-small 500 • 0.6875rem / 1rem (11px / 16px) --sys-typescale-emphasized-label-small 700 • 0.6875rem / 1rem (11px / 16px)

Elevation

Elevation is the distance between two surfaces on the z-axis. Material uses six levels of elevation, from 0 to 5. An element's resting state can be on levels 0 to 3, while levels 4 and 5 are reserved for interaction states such as hover and dragged.

Web Expressive represents these levels through CSS box-shadow values. Apply a token with the box-shadow property; --sys-elevation-level-0 has the value none.

Tip: Elevation shadows automatically adapt to the user's OS preference via @media (prefers-color-scheme: dark). To force or toggle modes, use [data-mode="light|dark"] or .light-mode / .dark-mode. See Handling Modes in the Theming section.

.elevated-card {
  box-shadow: var(--sys-elevation-level-1);
}
Tokens Resting Component Examples
--sys-elevation-level-0 Filled, tonal, and outlined buttons; button groups; filled and outlined cards.
--sys-elevation-level-1 Elevated buttons, cards, and chips; modal bottom sheets, navigation drawers, and side sheets.
--sys-elevation-level-2 Scrolled app bars, menus, navigation bars, rich tooltips, and toolbars.
--sys-elevation-level-3 FABs, extended FABs, modal dialogs, date pickers, time pickers, and search.
--sys-elevation-level-4 Not assigned as a resting level. Reserved for interaction states.
--sys-elevation-level-5 Not assigned as a resting level. Reserved for interaction states.

Motion

Spatial springs are used for movement, rotation, size, and rounded corners. Effects springs are used for color and opacity, where there should be no overshoot. Each has three speeds: fast, default, and slow.

Web Expressive uses Material's recommended web curves and durations to approximate expressive springs for animations without interruptions or gestures. Apply the curve and its companion duration in a CSS transition or animation.

.interactive-element {
  transition:
    transform var(--sys-motion-spring-fast-spatial-duration) var(--sys-motion-spring-fast-spatial),
    opacity var(--sys-motion-spring-fast-effects-duration) var(--sys-motion-spring-fast-effects);
}

Implementation Note: Use companion duration variables (*-duration) with the motion curves to keep timing consistent across your interface.

Category Spring Token Duration Token & Value Common Use Cases
Spatial

(movement & scale)
--sys-motion-spring-fast-spatial --sys-motion-spring-fast-spatial-duration

(350ms)
Small components, such as switches and buttons.
--sys-motion-spring-default-spatial --sys-motion-spring-default-spatial-duration

(500ms)
Animations that partially cover the screen, such as a bottom sheet or expanded navigation rail.
--sys-motion-spring-slow-spatial --sys-motion-spring-slow-spatial-duration

(650ms)
Full-screen animations.
Effects

(fading & styling)
--sys-motion-spring-fast-effects --sys-motion-spring-fast-effects-duration

(150ms)
Color change of the switch handle.
--sys-motion-spring-default-effects --sys-motion-spring-default-effects-duration

(200ms)
Opacity of content within a navigation rail.
--sys-motion-spring-slow-effects --sys-motion-spring-slow-effects-duration

(300ms)
Full-screen content refresh.

Focus Ring Motion

Web Expressive provides two additional tokens for focus-ring motion. The ring expands beyond its resting width, then settles back. These tokens are included in core.css and use the --focus-ring-* prefix. They are Web Expressive tokens, separate from Material's system motion tokens.

Token Default Value Usage
--focus-ring-motion-spring linear(0 0%, 2.5 25%, 2.5 50%, 1 100%) The curve used to animate the focus ring's width. Use as a transition or animation timing function.
--focus-ring-motion-duration 350ms The duration of the focus-ring animation. Use as a transition or animation duration.

Buttons, toggle buttons, icon buttons, toggle icon buttons, FABs, extended FABs, and switches use these tokens for their focus rings. Override either token on html to customize it across the page, or on a component or container to customize it locally. Focus-ring duration is independent of the spatial-motion duration tokens.

.custom-control {
  outline: 0 solid var(--sys-color-secondary);
  outline-offset: 2px;

  &:focus-visible {
    outline-width: 3px;
  }

  @media (prefers-reduced-motion: no-preference) {
    transition: outline-width var(--focus-ring-motion-duration)
      var(--focus-ring-motion-spring);
  }
}

Spacing

An 18-step spacing scale based on an 8px unit, where --sys-measurement-space-100 equals 0.5rem (8px at a 16px root size). Apply spacing tokens to the margins, padding, and gaps of a component, UI element, or layout. The system can be extended to add more spacing units and patterns as needed.

.container {
  padding: var(--sys-measurement-space-300); /* 24px */
  gap: var(--sys-measurement-space-150);     /* 12px */
  margin-block-end: var(--sys-measurement-space-400); /* 32px */
}
Tokens Value (rem / px)
--sys-measurement-space-0 0rem (0px)
--sys-measurement-space-25 0.125rem (2px)
--sys-measurement-space-50 0.25rem (4px)
--sys-measurement-space-75 0.375rem (6px)
--sys-measurement-space-100 0.5rem (8px)
--sys-measurement-space-125 0.625rem (10px)
--sys-measurement-space-150 0.75rem (12px)
--sys-measurement-space-175 0.875rem (14px)
--sys-measurement-space-200 1rem (16px)
--sys-measurement-space-250 1.25rem (20px)
--sys-measurement-space-300 1.5rem (24px)
--sys-measurement-space-400 2rem (32px)
--sys-measurement-space-450 2.25rem (36px)
--sys-measurement-space-500 2.5rem (40px)
--sys-measurement-space-600 3rem (48px)
--sys-measurement-space-700 3.5rem (56px)
--sys-measurement-space-800 4rem (64px)
--sys-measurement-space-900 4.5rem (72px)

Shape Corners

Corner radii tokens define the visual softness and personality of layout containers and interactive controls.

.card {
  border-radius: var(--sys-shape-corner-medium); /* 12px */
}

.modal-sheet {
  border-radius: var(--sys-shape-corner-extra-large-increased); /* 32px */
}

.avatar {
  border-radius: var(--sys-shape-corner-full); /* 9999px (circular) */
}
Tokens Value (rem / px)
--sys-shape-corner-none 0px
--sys-shape-corner-extra-small 0.25rem (4px)
--sys-shape-corner-small 0.5rem (8px)
--sys-shape-corner-medium 0.75rem (12px)
--sys-shape-corner-large 1rem (16px)
--sys-shape-corner-large-increased 1.25rem (20px)
--sys-shape-corner-extra-large 1.75rem (28px)
--sys-shape-corner-extra-large-increased 2rem (32px)
--sys-shape-corner-extra-extra-large 3rem (48px)
--sys-shape-corner-full 9999px

Abstract Shapes

35 Material 3 Expressive shapes available as CSS clipping (clip-path) or masking (mask / mask-image) tokens. Require importing shapes/clip-tokens.css and/or shapes/mask-tokens.css.

/* Using a clip token */
.badge {
  clip-path: var(--sys-shape-clip-diamond);
}

/* Using a mask token */
.image-wrapper {
  mask: var(--sys-shape-mask-flower) no-repeat center/contain;
}
Clipping Variable Masking Variable Live Preview
--sys-shape-clip-circle --sys-shape-mask-circle
--sys-shape-clip-square --sys-shape-mask-square
--sys-shape-clip-triangle --sys-shape-mask-triangle
--sys-shape-clip-diamond --sys-shape-mask-diamond
--sys-shape-clip-clamshell --sys-shape-mask-clamshell
--sys-shape-clip-pentagon --sys-shape-mask-pentagon
--sys-shape-clip-oval --sys-shape-mask-oval
--sys-shape-clip-pill --sys-shape-mask-pill
--sys-shape-clip-semicircle --sys-shape-mask-semicircle
--sys-shape-clip-4-leaf-clover --sys-shape-mask-4-leaf-clover
--sys-shape-clip-8-leaf-clover --sys-shape-mask-8-leaf-clover
--sys-shape-clip-4-sided-cookie --sys-shape-mask-4-sided-cookie
--sys-shape-clip-6-sided-cookie --sys-shape-mask-6-sided-cookie
--sys-shape-clip-7-sided-cookie --sys-shape-mask-7-sided-cookie
--sys-shape-clip-9-sided-cookie --sys-shape-mask-9-sided-cookie
--sys-shape-clip-12-sided-cookie --sys-shape-mask-12-sided-cookie
--sys-shape-clip-sunny --sys-shape-mask-sunny
--sys-shape-clip-very-sunny --sys-shape-mask-very-sunny
--sys-shape-clip-burst --sys-shape-mask-burst
--sys-shape-clip-soft-burst --sys-shape-mask-soft-burst
--sys-shape-clip-boom --sys-shape-mask-boom
--sys-shape-clip-soft-boom --sys-shape-mask-soft-boom
--sys-shape-clip-heart --sys-shape-mask-heart
--sys-shape-clip-flower --sys-shape-mask-flower
--sys-shape-clip-gem --sys-shape-mask-gem
--sys-shape-clip-fan --sys-shape-mask-fan
--sys-shape-clip-ghost-ish --sys-shape-mask-ghost-ish
--sys-shape-clip-pixel-circle --sys-shape-mask-pixel-circle
--sys-shape-clip-pixel-triangle --sys-shape-mask-pixel-triangle
--sys-shape-clip-arch --sys-shape-mask-arch
--sys-shape-clip-arrow --sys-shape-mask-arrow
--sys-shape-clip-slanted --sys-shape-mask-slanted
--sys-shape-clip-puffy --sys-shape-mask-puffy
--sys-shape-clip-puffy-diamond --sys-shape-mask-puffy-diamond
--sys-shape-clip-bun --sys-shape-mask-bun

Components Guide

Web Expressive components implement Material 3 Expressive interaction patterns using native HTML and modern CSS with zero runtime JavaScript.

Components Overview

Web Expressive provides a curated set of high-impact, highly configurable UI primitives. Rather than replicating an exhaustive catalog, the library focuses on essential building blocks. Any components outside this set can be built by composing Web Expressive design tokens.

All action buttons (Button, Icon Button, FAB, and Extended FAB) support both <button> and <a> elements. Selection controls (Toggle Button, Toggle Icon Button, and Switch) use native input primitives with browser-managed state.

To see these components composed in full-page templates, explore our Example Layouts.

Component Primary Role & Purpose Stylesheet Export
Button Triggers actions or navigates to a new page components/button.css
Toggle Button Toggles a choice on or off, or selects an option components/toggle-button.css
Icon Button Compact icon trigger for actions in constrained spaces components/icon-button.css
Toggle Icon Button Toggles a single icon state, such as bookmarking or favoriting components/toggle-icon-button.css
Standard Button Group Clusters related buttons with interactive layout response components/standard-button-group.css
Connected Button Group Related toggle options for single or multiple selection components/connected-button-group.css
Extended FAB Prominent floating action button with an icon and label for primary page actions components/extended-fab.css
FAB Prominent floating action button for the primary constructive action on a screen components/fab.css
Switch Toggles the state of a single standalone setting immediately components/switch.css

Implicit Defaults: Modifier classes like .filled, .small, .round, and .normal are applied automatically by default when no competing modifier is present. Do not include default classes explicitly in your HTML unless overriding a container style query.

Button

Buttons communicate actions that people can take across dialogs, modals, forms, cards, and toolbars. Can be implemented using <button> for page actions or <a> for navigation links.

Choosing Buttons & Hierarchy (M3 Specification)

When choosing a button style, consider the level of emphasis required by the action:

<!-- Primary filled action button (filled & small are implicit defaults) -->
<button class="button">Action</button>

<!-- Tonal button with leading icon -->
<button class="button tonal extra-large">
  <span class="icon" aria-hidden="true">stars</span>
  Get Started
</button>

<!-- Navigation link styled as an outlined button -->
<a href="/docs" class="button outline medium">Documentation</a>

See live Button example

Toggle Button

Used for binary or mutually exclusive selection states (such as Save, Favorite, or selecting choices). When pressed or selected, toggle buttons change color and morph shape (from round when unselected to square when selected).

Web Expressive builds toggle buttons by wrapping native <input type="checkbox"> (for multi-selection) or <input type="radio"> (for single-selection) with an explicit <label for="...">, allowing the browser to manage selection states and form data natively. (Per M3 guidelines, toggle buttons do not support the text style).

<!-- Checkbox toggle button (multi-select) -->
<div class="toggle-button tonal">
  <input class="input" type="checkbox" id="check-notifications" />
  <label class="label" for="check-notifications">Notifications</label>
</div>

<!-- Radio toggle button (single-select) -->
<div class="toggle-button outline">
  <input class="input" type="radio" name="plan" id="plan-standard" />
  <label class="label" for="plan-standard">Standard</label>
</div>

<!-- Toggle button with checkmark icon when selected -->
<div class="toggle-button">
  <input class="input" type="checkbox" id="fav-toggle" />
  <label class="label" for="fav-toggle">
    <span class="icon selected" aria-hidden="true">check</span>
    Save
  </label>
</div>

See live Toggle Button example

Icon Button

The most compact and subtle type of button, icon buttons are used for optional or supplementary actions (such as Bookmark, Star, or Print). Can be implemented using <button> for actions or <a> for links. Always provide an accessible label (such as aria-label) because icon buttons contain no visible text.

<!-- Action icon button -->
<button class="icon-button" aria-label="Bookmark">
  <span class="icon" aria-hidden="true">bookmark</span>
</button>

<!-- Tonal wide icon button -->
<button class="icon-button tonal wide" aria-label="Settings">
  <span class="icon" aria-hidden="true">settings</span>
</button>

<!-- Outline navigation link icon button -->
<a href="/home" class="icon-button outline" aria-label="Go Home">
  <span class="icon" aria-hidden="true">home</span>
</a>

See live Icon Button example

Toggle Icon Button

Visual icon toggles for binary selection states like bookmarking, favoriting, or pinning items. Built with native <input type="checkbox"> or <input type="radio"> and an explicit <label for="...">. Supports outlined icons when unselected and filled icons when selected via .selected. Always provide an aria-label on the input.

<!-- Bookmark toggle icon button (checkbox) -->
<div class="toggle-icon-button standard">
  <input class="input" type="checkbox" aria-label="Bookmark" id="bm-toggle" />
  <label class="label" for="bm-toggle">
    <span class="icon" aria-hidden="true">bookmark</span>
  </label>
</div>

<!-- View selection toggle icon button (radio) -->
<div class="toggle-icon-button tonal">
  <input class="input" type="radio" name="view-mode" aria-label="Grid view" id="view-grid" />
  <label class="label" for="view-grid">
    <span class="icon" aria-hidden="true">grid_view</span>
  </label>
</div>

See live Toggle Icon Button example

Standard Button Group

Organizes buttons and adds interactive layout dynamics between them. In Material 3 Expressive, standard button groups add interaction between adjacent buttons: when a button is pressed or selected, it changes width and shape while adjacent buttons animate smoothly. Can combine standard buttons, toggle buttons, and icon buttons.

Wrap the group inside a native <fieldset class="fieldset" disabled> to disable all child buttons simultaneously without JavaScript loops.

<!-- Standard button group -->
<div class="standard-button-group" role="group" aria-label="Quick Actions">
  <button class="button tonal">Save Draft</button>
  <button class="button">Publish</button>
</div>

<!-- Disabled group via native fieldset -->
<fieldset class="fieldset" aria-label="Actions" disabled>
  <div class="standard-button-group">
    <button class="button">Option A</button>
    <button class="button">Option B</button>
  </div>
</fieldset>

See live Standard Button Group example

Connected Button Group

Connected button groups replace the deprecated segmented button from earlier Material versions. They join related toggle options into a single unified segmented strip, ideal for selecting between views, filtering categories, or choosing product variations (e.g. sizes).

Unlike standard button groups, selecting a button in a connected button group morphs only that button's shape and color without shifting adjacent buttons. The connected group spans its container width and maintains a consistent 2px inner boundary between items.

<!-- Connected button group for single selection -->
<div class="connected-button-group" role="group" aria-label="Size Selection">
  <div class="toggle-button">
    <input type="radio" checked name="product-size" class="input" id="size-sm" />
    <label class="label" for="size-sm">
      <span class="icon selected" aria-hidden="true">check</span>
      Small
    </label>
  </div>
  <div class="toggle-button">
    <input type="radio" name="product-size" class="input" id="size-lg" />
    <label class="label" for="size-lg">
      <span class="icon selected" aria-hidden="true">check</span>
      Large
    </label>
  </div>
</div>

See live Connected Button Group example

Extended FAB

The floating action button represents the primary, most important, or most common constructive action on a screen (such as Create, Compose, or New thread). Use an Extended FAB instead of a regular FAB when label text is needed alongside the icon to make the action clear, or when floating above long-scrolling content.

Supports <button> for actions or <a> for navigation links. Only one FAB or Extended FAB should be present per screen to preserve clear visual hierarchy.

<!-- Default primary container extended FAB -->
<button class="extended-fab">
  <span class="icon" aria-hidden="true">add</span>
  Create
</button>

<!-- Navigation link extended FAB -->
<a href="/compose" class="extended-fab primary medium">
  <span class="icon" aria-hidden="true">edit</span>
  Compose
</a>

See live Extended FAB example

FAB

Compact floating action button for the single most important action on a screen. Persists on screen as content scrolls (typically positioned in the lower trailing corner or navigation rail). Always provide an aria-label because FABs contain only an icon.

Per Material 3 guidelines, FABs promote positive, constructive actions (such as Create, Share, or Start a process). Avoid using FABs for minor, overflow, or destructive actions (like trash, archive, or volume controls). Do not disable FABs: if the action is unavailable, the FAB should not appear.

<!-- Default FAB for primary action -->
<button class="fab" aria-label="Add item">
  <span class="icon" aria-hidden="true">add</span>
</button>

<!-- Large primary FAB -->
<button class="fab primary large" aria-label="Compose message">
  <span class="icon" aria-hidden="true">edit</span>
</button>

See live FAB example

Switch

Switches toggle the state of a single standalone option or setting on and off. When toggled, the effect takes place immediately without requiring a separate "Save" action. Always pair switches with an adjacent label describing what the control does when active.

Selection Control Guidance (M3 Specification):

<!-- Standard switch with adjacent label -->
<label for="wifi-switch">Wi-Fi</label>
<input class="switch" id="wifi-switch" type="checkbox" role="switch" />

<!-- Switch with embedded icons -->
<input class="switch icon" type="checkbox" role="switch" checked aria-label="Dark mode" />

<!-- Disabled switch -->
<input class="switch" type="checkbox" role="switch" disabled aria-label="Airplane mode (unavailable)" />

See live Switch example

Implementation Notes

Icons

Buttons, toggle buttons, icon buttons, toggle icon buttons, FABs, and extended FABs accept Material Symbols, inline SVGs, and <img>. All formats must use the .icon class. Icons are filled by default; add .no-fill for outlined icons. Use .selected for icons that appear only while a toggle input is checked.

Container Style Queries (Zero-JS Declarative Configuration)

Web Expressive components have built-in Container Style Query listeners compiled directly into their CSS stylesheets. You do not need to implement or write container queries yourself.

The core problem they solve is declarative component configuration without runtime JavaScript: when you want child buttons, toggle buttons, or icon buttons to automatically adapt their size (extra-small to extra-large), shape (round or square), or width (narrow to wide) across responsive breakpoints or container widths, you simply declare registered custom properties on the parent container element:

/* Declarative container configuration: child components adapt with zero JavaScript */
.toolbar {
  @media (width >= 840px) {
    --size-items: large;
    --shape-items: square;
    --width-items: wide;
  }
}

Scoped to Immediate Children: These container properties affect only immediate child components within the container, preventing unwanted style leaking into deeper nested layouts.

Explicit Modifiers Win: Any modifier class added directly to a component (like <button class="button extra-large">) takes precedence over container rules.

Components that do not support a property simply ignore it. The registered properties and supported components are:

Custom Property Syntax & Values (Default) Supported Components
--size-items extra-small | small | medium | large | extra-large (default: small) .button, .toggle-button, .icon-button, .toggle-icon-button,
.fab (small, medium, large only),
.extended-fab (small, medium, large only)
--shape-items round | square (default: round) .button, .toggle-button, .icon-button, .toggle-icon-button
--width-items narrow | normal | wide (default: normal) .icon-button, .toggle-icon-button

For an in-depth conceptual walkthrough, see Responsive Components with Style Queries.