A CLI based on the Material Design dynamic color system. Given a source color, it generates theme data and palette tokens that can be used directly in front-end projects, design tokens, script integrations, and bulk exports.
Runtime requirements: Node.js 22+ and ESM.
For the Simplified Chinese version, see README.zh-CN.md.
npm i -g @sandlada/material-theme-clinpm install
npm start -- "#0f774a"npm start runs the build first and then launches the CLI entry point. For local development and verification, this is the most direct workflow.
- Pass a color directly to the CLI. By default, it prints CSS theme and palette tokens to the terminal:
material-theme-cli "#0f774a"If you want a random theme, you can also use random-color:
material-theme-cli random-color- If you want to write the output to a file, switch to file output and specify a path:
material-theme-cli "#0f774a" --format css --output file --path ./theme.css- If the color is already stored in a text file, use
--inputto read it:
# color.txt
#0f774a
material-theme-cli --input ./color.txt --format json --output file --path ./theme.jsonThe recommended flow is simple: choose the input color, then the output format, then decide whether you want a preview or a file.
- Choose the input method.
You can pass [color] directly, or use --input <file-path> to read from a file. If both are provided, --input takes precedence.
- Choose the output method.
The default is terminal output. To generate a file, use --output file and set the destination with --path. If --path is omitted, the CLI writes to ./output.<format> in the current working directory.
- Choose the format and theme parameters.
If you just want a quick preview, keep the defaults. If you are integrating with a design system or multi-platform theme, adjust --variant, --contrast-level, --spec-version, and --platform, override palettes with --primary, --secondary, and similar options, or control palette output with --no-palette, --palette-tones, and token selectors such as palette-primary-50.
- Use a whitelist or blacklist when you need to filter tokens.
--token is a whitelist, and --exclude is a blacklist. The CLI normalizes names to kebab-case before matching, and the two options are mutually exclusive.
material-theme-cli [color] \
[--input <file-path>] \
[--format <css|json|xml|yaml|js|ts|csv>] \
[--output <console|file>] \
[--path <output-file-path>] \
[--make-js <output-js-file-path>] \
[--no-palette] \
[--palette-only] \
[--palette-tones <tone-list>] \
[--variant <0-8|MONOCHROME|NEUTRAL|TONAL_SPOT|TONALSPOT|VIBRANT|EXPRESSIVE|FIDELITY|CONTENT|RAINBOW|FRUIT_SALAD|FRUITSALAD>] \
[--contrast-level <-1|0|1>] \
[--spec-version <2021|2025>] \
[--platform <phone|watch>] \
[--primary <color>] \
[--secondary <color>] \
[--tertiary <color>] \
[--error <color>] \
[--neutral <color>] \
[--neutral-variant <color>] \
[--token <token-name...>] \
[--exclude <token-name...>] \
[--help]Default behavior:
--format css--output console- palette output enabled by default
--palette-tones 0..100--variant TONAL_SPOT--contrast-level 0--spec-version 2025--platform phone
The CLI accepts the following color syntaxes:
| Syntax | Example | Description |
|---|---|---|
| Hex | #0f774a |
Supports #RGB, #RRGGBB, #RGBA, and #RRGGBBAA. Pure hexadecimal values must keep the leading #, or they may be parsed as integers. |
| RGB | rgb(15, 119, 74) |
Channels can be integers in the range 0-255, or percentages from 0%-100%. |
| RGBA | rgba(15, 119, 74, 1) |
Only fully opaque alpha is accepted, meaning 1 or 100%. |
| LAB | lab(44.3, -15.2, 18.6) |
Converted directly from LAB values. |
| HCT | hct(270, 75, 50) |
Useful when you want to provide Material color model values directly. |
| ARGB fn | argb(0xff0f774a) |
Accepts a single ARGB integer. |
| ARGB int | 0xff0f774a or 4278851722 |
Supports decimal and 0x-prefixed integers. |
| Random | random-color |
Generates an opaque random color. |
If the input is empty or unsupported, the CLI exits with an error.
random-color can be used in the positional argument, in --input file contents, and in the palette override options --primary, --secondary, --tertiary, --error, --neutral, and --neutral-variant.
| Option | Default | Description |
|---|---|---|
[color] |
none | Positional argument for passing the color value directly. |
--input <file-path> |
none | Reads the source color text from a file, trimming leading and trailing whitespace. The path is resolved relative to the current working directory. |
--format <format> |
css |
Output format. Supported values: css, json, xml, yaml, js, ts, and csv. |
--output <target> |
console |
console prints to the terminal; file writes to disk. |
--path <output-file-path> |
./output.<format> |
Used when --output file is selected. The path is resolved relative to the current working directory. |
--make-js <output-js-file-path> |
none | Generates a reusable ESM wrapper script. It captures the current CLI configuration and calls dist/index.js at runtime. If the input uses random-color, the generated script re-randomizes at runtime. This is different from --format js. |
--help |
none | Displays help information. |
| Option | Default | Description |
|---|---|---|
--variant <variant> |
TONAL_SPOT |
Dynamic scheme variant. Available values include: 0 = MONOCHROME, 1 = NEUTRAL, 2 = TONAL_SPOT / TONALSPOT, 3 = VIBRANT, 4 = EXPRESSIVE, 5 = FIDELITY, 6 = CONTENT, 7 = RAINBOW, 8 = FRUIT_SALAD / FRUITSALAD. |
--contrast-level <contrast-level> |
1 |
Contrast level. Only -1, 0, and 1 are accepted. |
--spec-version <spec-version> |
2025 |
Design specification version. Only 2021 and 2025 are accepted. |
--platform <platform> |
phone |
Target platform. Only phone and watch are accepted. |
--primary <color> |
none | Overrides the primary palette. |
--secondary <color> |
none | Overrides the secondary palette. |
--tertiary <color> |
none | Overrides the tertiary palette. |
--error <color> |
none | Overrides the error palette. |
--neutral <color> |
none | Overrides the neutral palette. |
--neutral-variant <color> |
none | Overrides the neutral-variant palette. |
--no-palette |
enabled | Disables palette token output. Theme tokens still render normally. |
--palette-only |
off | Emits palette tokens only and skips theme token output. |
--palette-tones <tone-list> |
0..100 |
Limits palette output to the selected tones. Accepts comma- or space-separated integers from 0 to 100, such as 0, 1. |
These palette override options accept the same syntax as the source color: Hex, RGB, RGBA, LAB, HCT, ARGB function, and ARGB integer.
| Option | Default | Description |
|---|---|---|
--token <token-name...> |
none | Whitelist. Keeps only these tokens. Supports multiple values. |
--exclude <token-name...> |
none | Blacklist. Removes these tokens. Supports multiple values. |
Notes:
--tokenand--excludeare mutually exclusive.- Names are normalized to kebab-case before matching, so
primaryContainer,PRIMARY_CONTAINER, andprimary-containerare treated as the same token. - Palette selectors use the
palette-prefix. Usepalette-primaryorpalette-neutral-variantto keep an entire family, orpalette-primary-50to keep a single tone. - Palette selectors can be combined with
--palette-onlyand--palette-tonesto narrow output to specific palette tokens. - Unknown names are reported as warnings and ignored.
material-theme-cli "#0f774a"material-theme-cli "#0f774a" --format css --output file --path ./theme.cssmaterial-theme-cli "#0f774a" --format json --output file --path ./theme.jsonmaterial-theme-cli --input ./color.txt --format yaml --output file --path ./theme.yamljs and ts produce the same serialized content, both generating module source like export const MdSysColor = { ... }.
material-theme-cli "#0f774a" --format ts --output file --path ./theme.tsmaterial-theme-cli "#0f774a" --token primary surface-tint on-primarymaterial-theme-cli "#0f774a" --exclude surface-tint outline shadowmaterial-theme-cli "#0f774a" --primary "#1d4ed8" --secondary "#14b8a6" --neutral "#111827"material-theme-cli "#0f774a" --variant FRUIT_SALAD --contrast-level 0 --spec-version 2025 --platform watchmaterial-theme-cli "#0f774a" --make-js ./scripts/theme-generator.js --format css --output file --path ./theme.css--make-js generates a runtime script that captures the current parameters. The script depends on the built artifact dist/index.js, so run it only after the repository has been built.
material-theme-cli "#0f774a" --no-palettematerial-theme-cli "#0f774a" --palette-tones "0, 1"material-theme-cli "#0f774a" --palette-only --token palette-primary-50The CLI first generates a pair of lightObject and darkObject values, then serializes them into the chosen format. To keep output stable, keys are normalized to kebab-case and sorted alphabetically.
css: emits:rootand--md-sys-color-*custom properties usinglight-dark(light, dark)values.json/yaml: emits a theme object with top-levellight,dark, andschemenodes.xml: emits aresourcesnode, with color names usingmd_sys_color_*_lightandmd_sys_color_*_dark.js/ts: emits module source likeexport const MdSysColor = { ... }, whereLight,Dark, andSchemesuffixes represent the light, dark, and combined values.csv: uses the fixed headerscheme,token-name,color-value, and expands each token into three rows forlight,dark, andscheme.
Palette output is included by default.
css: adds--md-sys-palette-*custom properties with fixed hex values and nolight-dark()wrapper.json/yaml: adds a top-levelpalettenode withmd-sys-palette-*keys.xml: addsmd_sys_palette_*color entries underresources.js/ts: adds anexport const MdSysPalette = { ... }object with fixed palette token values.csv: addspaletterows for each palette token tone.
If the normalized keys in light and dark do not match, serialization fails. This prevents incomplete theme files from being generated.
This project is released under the MIT License. See LICENSE for details. Third-party dependencies are governed by their own license terms.