Setup and copy

Six blocks, in order. Each says where it goes, and the first two set the project up once. Copy them one at a time.

@anytv:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${ANYTV_M3_TOKEN}

Once per project. Commit this file — it names the token, it does not carry it. Keep the value in .env.local, which git ignores, as ANYTV_M3_TOKEN.

"registries": {
  "@m3": {
    "url": "https://api.github.com/repos/anyTV/m3-ui/contents/r/{name}.json",
    "params": { "ref": "components-v0.2.0" },
    "headers": {
      "Accept": "application/vnd.github.raw",
      "Authorization": "Bearer ${ANYTV_M3_TOKEN}",
      "X-GitHub-Api-Version": "2022-11-28"
    }
  }
}

Once per project. It is a top-level key, so add a comma after the block above it. The CLI fills in ${ANYTV_M3_TOKEN} from .env.local. The ref pins the released snapshot components-v0.2.0. Set it to main to take every change as it lands.

pnpm dlx shadcn-vue@latest search @m3
pnpm dlx shadcn-vue@latest add @m3/button

Run these from your project root, after the two files above — search lists all 41 items, and add takes any number of them.

pnpm dlx shadcn-vue@latest add @m3/agent-rules

Writes two files under .agents/. Nothing reads them on its own — the command prints the line to add to your AGENTS.md or CLAUDE.md.

<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:wght@400;500;700&amp;display=block">

Loads the typeface from Google. To self-host, swap these links for your own @font-face rules and keep the CSS block as it is.

/* m3-ui theme — import after the base M3 tokens
 * Source: #6750A4
 * Scheme: tonalSpot
 * Typeface: Roboto
 * Generated typography and one declaration per color role via light-dark(); the mode is
 * chosen by color-scheme, and the .light / .dark rules below let this block stand alone.
 */
:root {
  --md-ref-typeface-brand: 'Roboto', system-ui, sans-serif;
  --md-ref-typeface-plain: 'Roboto', system-ui, sans-serif;
  --md-ref-typeface-weight-regular: 400;
  --md-ref-typeface-weight-medium: 500;
  --md-ref-typeface-weight-bold: 700;
  font-optical-sizing: auto;
  font-stretch: normal;
  font-variation-settings: normal;
  --m3-brand-font-optical-sizing: auto;
  --m3-brand-font-stretch: normal;
  --m3-brand-font-variation-settings: normal;
  --m3-plain-font-optical-sizing: auto;
  --m3-plain-font-stretch: normal;
  --m3-plain-font-variation-settings: normal;
}

body {
  font-family: var(--md-ref-typeface-plain);
}

code,
kbd,
pre,
samp {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
    "Courier New", monospace;
  font-optical-sizing: auto;
  font-stretch: normal;
  font-variation-settings: normal;
}

:root {
  color-scheme: light dark;
  --md-sys-color-background: light-dark(#fdf7ff, #141218);
  --md-sys-color-on-background: light-dark(#1d1b20, #e6e0e9);
  --md-sys-color-surface: light-dark(#fdf7ff, #141218);
  --md-sys-color-surface-dim: light-dark(#ded8e0, #141218);
  --md-sys-color-surface-bright: light-dark(#fdf7ff, #3b383e);
  --md-sys-color-surface-container-lowest: light-dark(#ffffff, #0f0d13);
  --md-sys-color-surface-container-low: light-dark(#f8f2fa, #1d1b20);
  --md-sys-color-surface-container: light-dark(#f2ecf4, #211f24);
  --md-sys-color-surface-container-high: light-dark(#ece6ee, #2b292f);
  --md-sys-color-surface-container-highest: light-dark(#e6e0e9, #36343a);
  --md-sys-color-on-surface: light-dark(#1d1b20, #e6e0e9);
  --md-sys-color-surface-variant: light-dark(#e7e0eb, #49454e);
  --md-sys-color-on-surface-variant: light-dark(#49454e, #cac4cf);
  --md-sys-color-outline: light-dark(#7a757f, #948f99);
  --md-sys-color-outline-variant: light-dark(#cac4cf, #49454e);
  --md-sys-color-inverse-surface: light-dark(#322f35, #e6e0e9);
  --md-sys-color-inverse-on-surface: light-dark(#f5eff7, #322f35);
  --md-sys-color-shadow: #000000;
  --md-sys-color-scrim: #000000;
  --md-sys-color-surface-tint: light-dark(#65558f, #cfbdfe);
  --md-sys-color-primary: light-dark(#65558f, #cfbdfe);
  --md-sys-color-primary-dim: light-dark(#594982, #c7b4f5);
  --md-sys-color-on-primary: light-dark(#ffffff, #36275d);
  --md-sys-color-primary-container: light-dark(#e9ddff, #4d3d75);
  --md-sys-color-on-primary-container: light-dark(#4d3d75, #e9ddff);
  --md-sys-color-primary-fixed: #e9ddff;
  --md-sys-color-primary-fixed-dim: #cfbdfe;
  --md-sys-color-on-primary-fixed: #201047;
  --md-sys-color-on-primary-fixed-variant: #4d3d75;
  --md-sys-color-inverse-primary: light-dark(#cfbdfe, #65558f);
  --md-sys-color-secondary: light-dark(#625b71, #cbc2db);
  --md-sys-color-secondary-dim: light-dark(#564f64, #e8def8);
  --md-sys-color-on-secondary: light-dark(#ffffff, #332d41);
  --md-sys-color-secondary-container: light-dark(#e8def8, #4a4458);
  --md-sys-color-on-secondary-container: light-dark(#4a4458, #e8def8);
  --md-sys-color-secondary-fixed: #e8def8;
  --md-sys-color-secondary-fixed-dim: #cbc2db;
  --md-sys-color-on-secondary-fixed: #1e192b;
  --md-sys-color-on-secondary-fixed-variant: #4a4458;
  --md-sys-color-tertiary: light-dark(#7e5260, #efb8c8);
  --md-sys-color-tertiary-dim: light-dark(#704654, #fec5d6);
  --md-sys-color-on-tertiary: light-dark(#ffffff, #4a2532);
  --md-sys-color-tertiary-container: light-dark(#ffd9e3, #633b48);
  --md-sys-color-on-tertiary-container: light-dark(#633b48, #ffd9e3);
  --md-sys-color-tertiary-fixed: #ffd9e3;
  --md-sys-color-tertiary-fixed-dim: #efb8c8;
  --md-sys-color-on-tertiary-fixed: #31101d;
  --md-sys-color-on-tertiary-fixed-variant: #633b48;
  --md-sys-color-error: light-dark(#ba1a1a, #ffb4ab);
  --md-sys-color-error-dim: light-dark(#a80710, #ff554a);
  --md-sys-color-on-error: light-dark(#ffffff, #690005);
  --md-sys-color-error-container: light-dark(#ffdad6, #93000a);
  --md-sys-color-on-error-container: light-dark(#93000a, #ffdad6);
}

.light {
  color-scheme: light;
}

.dark {
  color-scheme: dark;
}

No runtime color generator needed. For build-time or runtime theming, use generateM3Theme and toCssVariables from @anytv/m3-tokens.

Setup for your agent

One prompt: the whole setup, and this theme. Paste it into your agent.

Set up the anyTV Material 3 component library in this project, using the theme configured
below. Work from the project root and do these in order.

1. Create or edit `.npmrc`:

```ini
@anytv:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${ANYTV_M3_TOKEN}
```

Commit `.npmrc` — it names the token, it does not carry it.

2. The token value lives in `.env.local`, which git ignores, under the name
`ANYTV_M3_TOKEN`. Ask me for the value rather than obtaining one, and keep it out of every
tracked file. Two tools need it: the shadcn-vue CLI reads `.env.local`, and pnpm reads the
same name from the shell environment.

3. Add this as a top-level key in `components.json`, beside `"aliases"`. Run
`pnpm dlx shadcn-vue@latest init` first if `components.json` is absent. `{name}` and
`${ANYTV_M3_TOKEN}` are placeholders the CLI expands, so keep both literal.

```json
"registries": {
  "@m3": {
    "url": "https://api.github.com/repos/anyTV/m3-ui/contents/r/{name}.json",
    "params": { "ref": "components-v0.2.0" },
    "headers": {
      "Accept": "application/vnd.github.raw",
      "Authorization": "Bearer ${ANYTV_M3_TOKEN}",
      "X-GitHub-Api-Version": "2022-11-28"
    }
  }
}
```

4. Run `pnpm add @anytv/m3-tokens`.

5. In the Tailwind entry stylesheet, import the theme after Tailwind itself and set the
source colour configured below:

```css
@import 'tailwindcss';
@import '@anytv/m3-tokens/css/theme.css';

:root {
  --m3-source-color: #6750A4;
}
```

6. In the same file, after those imports, paste this generated theme block exactly as it
stands. Every color role and the typeface in it are already resolved from the configuration
below, so nothing here is a placeholder to fill in:

```css
/* m3-ui theme — import after the base M3 tokens
 * Source: #6750A4
 * Scheme: tonalSpot
 * Typeface: Roboto
 * Generated typography and one declaration per color role via light-dark(); the mode is
 * chosen by color-scheme, and the .light / .dark rules below let this block stand alone.
 */
:root {
  --md-ref-typeface-brand: 'Roboto', system-ui, sans-serif;
  --md-ref-typeface-plain: 'Roboto', system-ui, sans-serif;
  --md-ref-typeface-weight-regular: 400;
  --md-ref-typeface-weight-medium: 500;
  --md-ref-typeface-weight-bold: 700;
  font-optical-sizing: auto;
  font-stretch: normal;
  font-variation-settings: normal;
  --m3-brand-font-optical-sizing: auto;
  --m3-brand-font-stretch: normal;
  --m3-brand-font-variation-settings: normal;
  --m3-plain-font-optical-sizing: auto;
  --m3-plain-font-stretch: normal;
  --m3-plain-font-variation-settings: normal;
}

body {
  font-family: var(--md-ref-typeface-plain);
}

code,
kbd,
pre,
samp {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
    "Courier New", monospace;
  font-optical-sizing: auto;
  font-stretch: normal;
  font-variation-settings: normal;
}

:root {
  color-scheme: light dark;
  --md-sys-color-background: light-dark(#fdf7ff, #141218);
  --md-sys-color-on-background: light-dark(#1d1b20, #e6e0e9);
  --md-sys-color-surface: light-dark(#fdf7ff, #141218);
  --md-sys-color-surface-dim: light-dark(#ded8e0, #141218);
  --md-sys-color-surface-bright: light-dark(#fdf7ff, #3b383e);
  --md-sys-color-surface-container-lowest: light-dark(#ffffff, #0f0d13);
  --md-sys-color-surface-container-low: light-dark(#f8f2fa, #1d1b20);
  --md-sys-color-surface-container: light-dark(#f2ecf4, #211f24);
  --md-sys-color-surface-container-high: light-dark(#ece6ee, #2b292f);
  --md-sys-color-surface-container-highest: light-dark(#e6e0e9, #36343a);
  --md-sys-color-on-surface: light-dark(#1d1b20, #e6e0e9);
  --md-sys-color-surface-variant: light-dark(#e7e0eb, #49454e);
  --md-sys-color-on-surface-variant: light-dark(#49454e, #cac4cf);
  --md-sys-color-outline: light-dark(#7a757f, #948f99);
  --md-sys-color-outline-variant: light-dark(#cac4cf, #49454e);
  --md-sys-color-inverse-surface: light-dark(#322f35, #e6e0e9);
  --md-sys-color-inverse-on-surface: light-dark(#f5eff7, #322f35);
  --md-sys-color-shadow: #000000;
  --md-sys-color-scrim: #000000;
  --md-sys-color-surface-tint: light-dark(#65558f, #cfbdfe);
  --md-sys-color-primary: light-dark(#65558f, #cfbdfe);
  --md-sys-color-primary-dim: light-dark(#594982, #c7b4f5);
  --md-sys-color-on-primary: light-dark(#ffffff, #36275d);
  --md-sys-color-primary-container: light-dark(#e9ddff, #4d3d75);
  --md-sys-color-on-primary-container: light-dark(#4d3d75, #e9ddff);
  --md-sys-color-primary-fixed: #e9ddff;
  --md-sys-color-primary-fixed-dim: #cfbdfe;
  --md-sys-color-on-primary-fixed: #201047;
  --md-sys-color-on-primary-fixed-variant: #4d3d75;
  --md-sys-color-inverse-primary: light-dark(#cfbdfe, #65558f);
  --md-sys-color-secondary: light-dark(#625b71, #cbc2db);
  --md-sys-color-secondary-dim: light-dark(#564f64, #e8def8);
  --md-sys-color-on-secondary: light-dark(#ffffff, #332d41);
  --md-sys-color-secondary-container: light-dark(#e8def8, #4a4458);
  --md-sys-color-on-secondary-container: light-dark(#4a4458, #e8def8);
  --md-sys-color-secondary-fixed: #e8def8;
  --md-sys-color-secondary-fixed-dim: #cbc2db;
  --md-sys-color-on-secondary-fixed: #1e192b;
  --md-sys-color-on-secondary-fixed-variant: #4a4458;
  --md-sys-color-tertiary: light-dark(#7e5260, #efb8c8);
  --md-sys-color-tertiary-dim: light-dark(#704654, #fec5d6);
  --md-sys-color-on-tertiary: light-dark(#ffffff, #4a2532);
  --md-sys-color-tertiary-container: light-dark(#ffd9e3, #633b48);
  --md-sys-color-on-tertiary-container: light-dark(#633b48, #ffd9e3);
  --md-sys-color-tertiary-fixed: #ffd9e3;
  --md-sys-color-tertiary-fixed-dim: #efb8c8;
  --md-sys-color-on-tertiary-fixed: #31101d;
  --md-sys-color-on-tertiary-fixed-variant: #633b48;
  --md-sys-color-error: light-dark(#ba1a1a, #ffb4ab);
  --md-sys-color-error-dim: light-dark(#a80710, #ff554a);
  --md-sys-color-on-error: light-dark(#ffffff, #690005);
  --md-sys-color-error-container: light-dark(#ffdad6, #93000a);
  --md-sys-color-on-error-container: light-dark(#93000a, #ffdad6);
}

.light {
  color-scheme: light;
}

.dark {
  color-scheme: dark;
}

```

7. Inside `<head>` in `index.html`, paste these font links exactly as they stand:

```html
<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:wght@400;500;700&amp;display=block">
```

8. Run `pnpm dlx shadcn-vue@latest add @m3/agent-rules`. It writes `.agents/m3-ui.md` and
`.agents/m3-ui-components.md` and prints one pointer line; put that line in this project’s
`AGENTS.md` or `CLAUDE.md` so every later session reaches them.

9. Read `.agents/m3-ui.md` and `.agents/m3-ui-components.md` in full, and follow them for
every component you add from here, one at a time:
`pnpm dlx shadcn-vue@latest add @m3/<name>`.

Done when `pnpm dlx shadcn-vue@latest search @m3` lists the registry’s items. A 404 from it
means the token value has not reached the CLI — an empty header is dropped rather than sent
blank. If an install fails with `ERR_PNPM_IGNORED_BUILDS`, allow `esbuild` and `vue-demi`
under `allowBuilds` in `pnpm-workspace.yaml`.

It writes the files, runs the installs, and asks you for the token value. The color and typeface you picked here are already in it.

Reset theme?

This restores the source color, color scheme, appearance, relative colors, and typography to their defaults.

M3 UI Playground

Component playground

Circular progress indicator

Inspect one process indicator from 24 to 240 dp, including expressive waves and button integration.

Controls

Adjust one property at a time. The preview updates immediately.

  • Progress
  • Indeterminate
  • Size Official range: 24–240 dp
  • Thickness Capped at a quarter of the size: 12 dp here
  • Track Secondary-container track