Source: https://monarchui.dev/migration/

Guides

# Migrating an existing Rook app

Measured starting point for every app: run `monarch check` and `monarch registry verify` on a checkout. Each step is shippable on its own; nothing requires a rewrite.

## Step 0: baseline (10 minutes)

`npx monarch check --product <id> --write-baseline monarch.baseline.json`, commit it, add the reusable workflow. From now on new violations fail CI; old ones are tracked.

## Step 1: tokens under the existing UI (one PR, low risk)

1. Install the packages and import the four CSS files (`getting-started.md` §2), including **`css/compat`**.
2. Delete the token blocks (`:root { --background … }` and `.dark { … }`) from `globals.css`. Keep your utilities and app CSS, and keep any app-specific variables Monarch doesn't define (grep `var(--` and check each one still resolves). Apps that use their brand colour for `--accent` need `--accent: var(--primary)`: the bridge maps `accent` to Rook Blue, as most Rook apps do.
3. Replace `tailwind.config.ts` colour/radius/keyframe sections with the preset. Keep `content` and any app-specific plugins.
4. Add `appearanceScript({ product })` to `<head>`, unless the app toggles the theme at runtime (see getting-started §3); then just set `<html data-mn-product="<id>">`.
5. Visual check in light and dark. Expected differences, all intentional: stronger input borders, accessible primary fills (see the product's row in `contrast-report.json`), consistent shadows. Note that the legacy `--input` now maps to Monarch's `border-input` (about 56% grey instead of 88%); the few places using it as a *background* (for example unchecked Switch tracks) get a darker track; switch them to `bg-muted` or the Monarch Switch. Also expected: `rounded-md`/`rounded-sm` are 8px/6px (were 10px/8px), and `destructive` is Rook Red rather than shadcn's generic red.
6. Gold and orange brands keep their fill as `--primary` because they need dark text on it; use `text-mn-accent-strong` for accent-coloured *text* in those apps. Every other brand's `--primary` is readable as text on every surface.

Conformance drops `tokens/local-token-definitions` to the few deliberate overrides and `deps/monarch-version` to "current". `registry verify` reports **partial** after this step (duplicate local components remain) and **adopted** after Step 2. In the first migration, 63 local token variables became 5, with 0 axe violations and at most 0.31% of pixels changed.

## Step 2: components (incremental)

Swap `components/ui/*` for Monarch imports, one primitive at a time, starting with Button, Field/Input, Dialog, DropdownMenu. Map:

| shadcn variant | Monarch |
| --- | --- |
| `default` | `variant="primary"` |
| `outline` | `variant="secondary"` (default) |
| `ghost` | `variant="ghost"` |
| `destructive` | `variant="danger"` |
| `size="icon"` | `<IconButton label=… icon=… />` |
| `<Label>` + `<Input>` + error `<p>` | `<Field><FieldLabel/><Input/><FieldError/></Field>` |

Delete the local file once nothing imports it; `components/duplicated-primitive` counts what is left.

## Step 3: colours and type (ratchet)

Replace palette classes with `mn-*` classes or semantic legacy names, guided by the report. Target: `color/no-raw-palette` = 0, `typography/no-arbitrary-type` = 0. Legitimate literals (OG images, brand logos, third-party colours) get a registry exception.

## Step 4: remove the bridge

When `legacy/bridge-variable` is 0, drop `css/compat`. Done.

## Rook platform notes

- Keep the shared Rook shell pieces (`RookRail`, the theme toggle, the Space switcher and the chat dock) as they are; they already use the shared cookie and `rail-expanded:` variant.
- Vite + Tailwind 3 apps follow the same steps.
- Tailwind 4 marketing sites: CSS files only, no preset.
