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

Split button

Pair one primary action with a menu of closely related alternatives.

No action yet. Menu collapsed.

Keyboard: Tab reaches both segments. Space or Enter activates the focused segment.

Controls

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

  • Small
  • Primary label Use one or two words
  • Leading icon
  • Icon only The label becomes the accessible name
  • Toggle primary Pressed state with aria-pressed
  • Tooltip Held shut while the menu is open
  • Selected item Radio items with a check
  • Shortcuts Keyboard commands as trailing text
  • Open menu
  • Disabled
  • Compact width
  • Right to left