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:
-
Zero-Runtime: Built with a standards-first approach using modern CSS (Container Queries,
@scope, CSS Layers, Relative Colors) and native HTML, saving maximum performance for your app's logic. - Production-Ready Theming: 50+ built-in light/dark schemes, a full set of documented system tokens, 35 abstract shapes, configurable components with dedicated APIs, and instant theme-swapping with friction-free overrides.
- Drop-in Modularity: Pick only what you need, from tokens to individual components, without adding bundle bloat or breaking existing workflows.
Quick Start (CDN)
- Create a new
index.htmlfile. - 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>
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 |
-
Path Parity: Both collections share identical filenames and variable names. Swapping between them requires only toggling
alpha/in the stylesheet path. - Collection Differences: The collections differ in their variant families and baseline scheme. Single-color, dual-color, and monochrome schemes are identical across both collections.
- Regular Updates: Both collections are regularly updated.
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
-
-tonal(Recommended Default): Balanced contrast, versatile surfaces, and consistent branding across light and dark modes. -
-neutral: Subtle, understated surface tinting ideal for productivity tools and content-heavy layouts. -
-vibrant: High-chroma accents with energetic contrast for dynamic, high-engagement experiences. -
-expressive: Bold color pairings and heightened chromatic contrast for memorable visual hierarchy.
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:
- Light Mode:
[data-mode="light"]or.light-mode - Dark Mode:
[data-mode="dark"]or.dark-mode
<!-- 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-modeattribute (or mode class), or setdata-mode="system"; the document naturally falls back to defaultcolor-scheme: light darkand 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
htmlselector (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-basefirst. 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:
- Surface – A role used for backgrounds and large, low-emphasis areas of the screen.
- Primary, Secondary, Tertiary – Accent color roles used to emphasize or de-emphasize foreground elements.
- Container – Roles used as a fill color for foreground elements like buttons. They should not be used for text or icons.
-
On – Roles starting with this term indicate a color for text or icons on top of its paired parent color (for
example,
on primaryis used for text and icons against theprimaryfill color). -
Variant – Roles ending with this term offer a lower emphasis alternative to its non-variant pair (for example,
outline variantis a less emphasized version of theoutlinecolor).
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:
-
Don't use the
outlinecolor for dividers since they have different contrast requirements. Instead, useoutline variant. -
Don't use the
outlinecolor for components that contain multiple elements, such as cards. Instead, useoutline variant. -
Don't use the
outline variantcolor to create visual hierarchy or define the visual boundary of targets. Instead, use theoutlinecolor or another color providing 3:1 contrast with the surface color.
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:
- Display (Large, Medium, Small) – As the largest text on the screen, display styles are reserved for short, important text or numerals. They work best on large screens and can accommodate expressive or brand-forward typefaces.
- Headline (Large, Medium, Small) – Best-suited for short, high-emphasis text on smaller screens, marking primary passages of text or key regions of content.
- Title (Large, Medium, Small) – Smaller than headline styles, titles are used for medium-emphasis text that remains relatively short (such as section dividers, dialog headers, or category labels).
- Body (Large, Medium, Small) – Used for longer passages of reading text throughout your interface. Body styles are optimized for sustained legibility at smaller sizes; avoid decorative or script fonts for body text.
- Label (Large, Medium, Small) – Utilitarian styles used for text inside interactive components (such as buttons, chips, and navigation items) or very small metadata, such as captions and timecodes.
When to Use Baseline vs. Emphasized Styles
Use baseline and emphasized type styles together to achieve expressive experiences:
- Baseline styles serve as the standard typographic default for all components, long-form content, and everyday layout elements.
- Emphasized styles were introduced in the M3 Expressive update. They have a higher weight than baseline styles and can be used for selection, actions, headlines, and other editorial treatments.
- Components: Use emphasized styles to communicate importance or active status, such as primary action buttons, Extended FABs, selected list items, selected menu items, unread messages, or badges.
- Weight & Context: Apply emphasized styles to draw attention to specific aspects of components or text that is already bolded for greater stylistic expression.
Implementation Note: Web Expressive packages each typescale into a single CSS
fontshorthand token (bundlingfont-weight,font-size/line-height, andfont-family). Always consume typescale variables via the shorthandfontproperty. Tracking is not included in these shorthand tokens; useletter-spacingseparately 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.normalare 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:
- Filled (High Emphasis, Default): Uses the primary color palette. The filled button has the most visual impact after the FAB and should be reserved for final or unblocking actions in a flow (such as Save, Confirm, or Done). Use sparingly—ideally one prominent primary action per screen.
- Tonal (Medium Emphasis): Uses the secondary container color palette. Useful for important actions that don't distract users from the main task, or for supporting secondary actions (such as Next in an onboarding flow).
- Elevated (Medium Emphasis): Features a secondary container fill and drop shadow (elevation 1). Use only when a button requires visual separation from a patterned or visually prominent background.
- Outlined (Medium Emphasis): Features a container stroke with no fill at rest. Ideal for actions that need attention but aren't the primary action (such as See all or Add to cart), or for providing escape actions (like Cancel).
- Text (Low Emphasis): Has no outline or container at rest. Best used for lowest-priority actions not essential to the user journey, such as actions inside dialogs, cards, or snackbars where a visible container would distract from nearby content.
- Export path:
components/button.css - Base class:
.button -
Modifiers:
-
Variants:
.filled(default),.tonal,.outline,.elevated,.text -
Sizes:
.extra-small(32px),.small(40px, default),.medium(56px),.large(96px),.extra-large(136px) - Shape:
.round(default),.square
-
Variants:
<!-- 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>
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).
- Export path:
components/toggle-button.css - Base class:
.toggle-button(wraps.inputand.label) -
Modifiers:
-
Variants:
.filled(default),.tonal,.outline,.elevated -
Sizes:
.extra-small,.small(default),.medium,.large,.extra-large - Shape:
.round(default),.square
-
Variants:
<!-- 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.
- Export path:
components/icon-button.css - Base class:
.icon-button -
Modifiers:
-
Variants:
.filled(default),.tonal,.outline,.standard -
Sizes:
.extra-small,.small(default),.medium,.large,.extra-large - Shape:
.round(default),.square - Width:
.narrow,.normal(default),.wide
-
Variants:
<!-- 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>
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.
- Export path:
components/toggle-icon-button.css - Base class:
.toggle-icon-button(wraps.inputand.label) -
Modifiers:
-
Variants:
.filled(default),.tonal,.outline,.standard -
Sizes:
.extra-small,.small(default),.medium,.large,.extra-large - Shape:
.round(default),.square - Width:
.narrow,.normal(default),.wide
-
Variants:
<!-- 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.
- Export path:
components/standard-button-group.css - Base class:
.standard-button-group -
Modifiers:
-
Sizes:
.extra-small,.small(default),.medium,.large,.extra-large - Shape:
.round(default),.square -
Width:
.narrow,.normal(default),.wide(applies to contained icon buttons)
-
Sizes:
<!-- 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.
- Export path:
components/connected-button-group.css - Base class:
.connected-button-group -
Modifiers:
-
Sizes:
.extra-small,.small(default),.medium,.large,.extra-large - Shape:
.round(default),.square
-
Sizes:
<!-- 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.
- Export path:
components/extended-fab.css - Base class:
.extended-fab -
Modifiers:
-
Variants:
.primary-container(default),.secondary-container,.tertiary-container,.primary,.secondary,.tertiary - Sizes:
.small(56px, default),.medium(80px),.large(96px)
-
Variants:
<!-- 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>
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.
- Export path:
components/fab.css - Base class:
.fab(supports<button>and<a>) -
Modifiers:
-
Variants:
.primary-container(default),.secondary-container,.tertiary-container,.primary,.secondary,.tertiary - Sizes:
.small(56px, default),.medium(80px),.large(96px)
-
Variants:
<!-- 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>
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):
- Use switches to toggle a single standalone setting or activate/deactivate a feature immediately.
- Do not use switches to choose between mutually exclusive opposing options (such as List view vs Map view)—use a Connected Button Group instead.
- Do not use switches in lists where multiple options must be confirmed with a "Save" or "Update" button—use native checkboxes instead.
- Export path:
components/switch.css - Base element:
<input class="switch" type="checkbox" role="switch" /> -
Modifiers:
- Icon:
.icon(displays embedded checkmark and cross indicators within the handle)
- Icon:
<!-- 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)" />
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.