material-theme-builder
Material Theme Builder
Overview
Generate complete Material Design 3 color themes programmatically from any source color. This skill uses @material/material-color-utilities — the same color algorithm library that powers Google's Material Theme Builder — to produce accessible light/dark palettes, tonal ranges, and design tokens ready for CSS, JSON, or any framework.
Keywords: Material Theme Builder, color generation, dynamic color, theme export, @material/material-color-utilities, HCT, tonal palette, source color, brand color, CSS tokens, JSON export
When to Use
- "Generate an M3 theme from my brand color"
- "Create M3 color tokens from #FF9800"
- "Export M3 palette as CSS custom properties"
- Creating a new M3 project and need a complete color token set
- Converting a hex brand color into a full M3 palette
- Generating both light and dark theme tokens programmatically
How It Works
Material Color Utilities uses the HCT color space (Hue, Chroma, Tone) — a perceptually uniform color model built on CAM16 and L* — to generate five tonal palettes from a single source color:
- Primary — derived from the source color's hue
- Secondary — desaturated variant of the source hue
- Tertiary — complementary hue for contrast
- Neutral — very low chroma for surfaces and backgrounds
- Neutral Variant — slightly chromatic neutral for outlines
Each palette contains tones 0–100. Specific tones are mapped to semantic color roles (e.g., primary = tone 40 in light, tone 80 in dark).
Install
npm install @material/material-color-utilities
Generate a Theme
Use the generate-theme.mjs script in this skill's directory:
node generate-theme.mjs "#FF9800" # CSS output (default tonal-spot)
node generate-theme.mjs "#FF9800" --json # JSON output
node generate-theme.mjs "#FF9800" --scheme expressive # Expressive scheme variant
Available Scheme Variants
The --scheme flag selects from 9 dynamic color strategies defined in material-color-utilities:
| Scheme | Description |
|---|---|
tonal-spot |
Default — balanced, versatile (used by Android Material You) |
content |
Colors derived with fidelity to the source, good for photo-based themes |
expressive |
Intentionally detached from source for bold, playful palettes |
fidelity |
High fidelity to source hue, chroma-capped for accessibility |
fruit-salad |
Vibrant, playful secondary and tertiary from offset hues |
monochrome |
Achromatic — all palettes have zero chroma |
neutral |
Near-achromatic — very low chroma for subtle, muted themes |
rainbow |
Wide hue spread across primary, secondary, and tertiary |
vibrant |
Saturated, colorful variant of tonal-spot |
Quick Start (Node.js)
import {
argbFromHex,
hexFromArgb,
themeFromSourceColor,
} from "@material/material-color-utilities";
// Generate from any hex source color
const theme = themeFromSourceColor(argbFromHex("#FF9800"));
// Extract light scheme
const light = theme.schemes.light.toJSON();
for (const [role, argb] of Object.entries(light)) {
console.log(`${role}: ${hexFromArgb(argb)}`);
}
Full Theme with Surface Containers
The base themeFromSourceColor provides core roles. Surface container tones are derived from the neutral palette at specific tones:
import {
argbFromHex,
hexFromArgb,
themeFromSourceColor,
} from "@material/material-color-utilities";
const theme = themeFromSourceColor(argbFromHex("#FF9800"));
const neutral = theme.palettes.neutral;
const primary = theme.palettes.primary;
const secondary = theme.palettes.secondary;
const tertiary = theme.palettes.tertiary;
// Light surface containers (tone values per M3 spec)
const lightSurfaces = {
surface: neutral.tone(98),
surfaceDim: neutral.tone(87),
surfaceBright: neutral.tone(98),
surfaceContainerLowest: neutral.tone(100),
surfaceContainerLow: neutral.tone(96),
surfaceContainer: neutral.tone(94),
surfaceContainerHigh: neutral.tone(92),
surfaceContainerHighest: neutral.tone(90),
};
// Dark surface containers
const darkSurfaces = {
surface: neutral.tone(6),
surfaceDim: neutral.tone(6),
surfaceBright: neutral.tone(24),
surfaceContainerLowest: neutral.tone(4),
surfaceContainerLow: neutral.tone(10),
surfaceContainer: neutral.tone(12),
surfaceContainerHigh: neutral.tone(17),
surfaceContainerHighest: neutral.tone(22),
};
// Fixed colors (consistent across light and dark)
const fixed = {
primaryFixed: primary.tone(90),
onPrimaryFixed: primary.tone(10),
primaryFixedDim: primary.tone(80),
onPrimaryFixedVariant: primary.tone(30),
secondaryFixed: secondary.tone(90),
onSecondaryFixed: secondary.tone(10),
secondaryFixedDim: secondary.tone(80),
onSecondaryFixedVariant: secondary.tone(30),
tertiaryFixed: tertiary.tone(90),
onTertiaryFixed: tertiary.tone(10),
tertiaryFixedDim: tertiary.tone(80),
onTertiaryFixedVariant: tertiary.tone(30),
};
Export Formats
CSS Custom Properties
function schemeToCss(scheme, selector = ":root") {
const entries = Object.entries(scheme.toJSON());
const props = entries
.map(([key, argb]) => {
const token = key.replace(/([A-Z])/g, "-$1").toLowerCase();
return ` --md-sys-color-${token}: ${hexFromArgb(argb)};`;
})
.join("\n");
return `${selector} {\n${props}\n}`;
}
console.log(schemeToCss(theme.schemes.light));
console.log(schemeToCss(theme.schemes.dark, '[data-theme="dark"]'));
JSON
function schemeToJson(scheme) {
const result = {};
for (const [key, argb] of Object.entries(scheme.toJSON())) {
result[key] = hexFromArgb(argb);
}
return result;
}
const output = {
source: "#FF9800",
light: schemeToJson(theme.schemes.light),
dark: schemeToJson(theme.schemes.dark),
};
console.log(JSON.stringify(output, null, 2));
Apply to DOM
import { applyTheme } from "@material/material-color-utilities";
const systemDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
applyTheme(theme, { target: document.body, dark: systemDark });
Custom Colors (Extended Palette)
Add brand-specific colors that harmonize with the generated theme:
const theme = themeFromSourceColor(argbFromHex("#FF9800"), [
{
name: "brand-green",
value: argbFromHex("#4CAF50"),
blend: true, // harmonize with the source color
},
{
name: "warning",
value: argbFromHex("#FFC107"),
blend: false, // keep exact hue
},
]);
// Access custom colors
for (const custom of theme.customColors) {
console.log(`${custom.color.name}:`);
console.log(` light: ${hexFromArgb(custom.light.color)}`);
console.log(` dark: ${hexFromArgb(custom.dark.color)}`);
}
Platform Libraries
| Platform | Package |
|---|---|
| TypeScript | @material/material-color-utilities (npm) |
| Dart | material_color_utilities (pub.dev) |
| Java/Kotlin | Built into MDC-Android |
| Swift | Source in material-color-utilities repo |
| C++ | Source in material-color-utilities repo |
Checklist
- Source color chosen (brand primary or user-provided hex)
-
@material/material-color-utilitiesinstalled - Light and dark schemes generated via
themeFromSourceColor - Surface container tokens derived from neutral palette tones
- Fixed accent colors derived from primary/secondary/tertiary palette tones
- Tokens exported in the target format (CSS, JSON, or framework config)
- Custom/extended colors added if needed (with harmonization)
- Both light and dark themes tested for WCAG contrast compliance
Resources
- Material Theme Builder (web): https://material-foundation.github.io/material-theme-builder/
- Material Theme Builder (repo): https://github.com/material-foundation/material-theme-builder
- Material Color Utilities: https://github.com/material-foundation/material-color-utilities
- Official M3 CSS tokens (baseline): https://github.com/material-foundation/material-tokens
- npm: https://www.npmjs.com/package/@material/material-color-utilities
- M3 design tokens overview: https://m3.material.io/foundations/design-tokens/overview
- M3 color system: https://m3.material.io/styles/color/overview
- HCT color space: https://material.io/blog/science-of-color-design
More from shelbeely/shelbeely-agent-skills
material-design-3-guide
Master guide for Material Design 3 — covering the full specification from Material You foundations through M3 Expressive. Explains when to use each Material Design 3 skill subset (color, motion, typography, shape, layout, components, icons). Use this when starting a Material Design 3 project, when you need to understand which M3 skill to apply, or when the user asks about Material Design 3 in general.
28material-design-3-components
Comprehensive guide to Material Design 3 components — from baseline Material You through M3 Expressive. Covers action, containment, communication, navigation, selection, and text input components with specifications, states, and implementation guidelines. Use this when building or styling UI components following Material Design 3 guidelines.
27material-design-3-typography
Applies Material Design 3 Expressive typography principles including variable fonts, type scales, and hierarchy. Use this when working on text styling, type hierarchy, readable interfaces, or when the user asks to apply Material Design 3 typography guidelines.
21material-design-3-layout
Applies Material Design 3 Expressive layout, spacing, and size hierarchy principles including grid systems, responsive design, and elevation. Use this when working on page layouts, spacing, grids, responsive design, or when the user asks to apply Material Design 3 layout guidelines.
20material-design-3-motion
Applies Material Design 3 Expressive motion and animation principles to create natural, intuitive, and engaging user experiences. Use this when implementing animations, transitions, micro-interactions, or when the user asks to apply Material Design 3 motion guidelines.
17material-design-3-color
Applies Material Design 3 Expressive dynamic color and theming principles to user interfaces. Use this when working on color palettes, themes, dynamic color systems, accessibility, or when the user asks to apply Material Design 3 color guidelines to a design or application.
16