# Migrating an entrepta project to v2

> For a coding agent working in a project that already uses entrepta 1.x, or that copied pieces of it by hand. Do the steps in order, and check the app at 375px, in dark and light, after each one.

## 1. Update the files

The CLI copies source, so nothing updates on its own. Commit first: `--overwrite` replaces local edits. Rewrite the tokens, then each component the project uses (look in `entrepta.json` and the components folder).

```bash
# tokens and cn: add --themes=all if you use the ThemeSwitcher
npx @entrepta/cli@latest init --theme=entrepta --overwrite

# every component you already have
npx @entrepta/cli@latest add button card dialog --overwrite
```

## 2. Font sizes

Sizes come from ten scale steps. Arbitrary pixel sizes and Tailwind default steps go. A step sets size and leading, never the family, so keep the font-* class.

| before | after |
| --- | --- |
| text-[10px] | text-mono-xs |
| text-[11px], text-xs | text-mono-sm for labels, text-mono-xs for group headings |
| text-[12px] | text-mono-sm |
| text-[13px] or text-[14px] in mono | text-mono-md |
| text-[13px] in sans | text-body-md |
| text-sm on a large button | text-body-lg |
| text-2xl on a Card or Dialog title | text-heading-lg |

## 3. The .t-* classes

They are gone. Each becomes a family and a step.

| before | after |
| --- | --- |
| .t-display-xl | font-serif text-display-xl |
| .t-heading-md | font-serif text-heading-md (now 18px, was 20px) |
| .t-body-md | font-sans text-body-md |
| .t-mono-sm | font-mono text-mono-sm |
| .t-mono-xs | font-mono text-mono-xs (now 10px, was 11px) |
| .t-muted, .t-secondary | text-[var(--fg-muted)], text-[var(--fg-secondary)] |
| .t-brand | text-[var(--fg-brand-text)] below 24px, text-[var(--fg-brand)] above |

## 4. lucide to Phosphor

Install `@phosphor-icons/react` and remove `lucide-react`. A file without `"use client"` imports from `@phosphor-icons/react/dist/ssr`. Phosphor takes `size`, not width and strokeWidth.

| lucide | phosphor |
| --- | --- |
| Loader2 | CircleNotchIcon, with animate-spin |
| X | XIcon |
| Check | CheckIcon |
| ChevronRight | CaretRightIcon |
| Circle | CircleIcon weight="fill" |
| Search | MagnifyingGlassIcon |
| Copy | CopyIcon |
| AlertTriangle | WarningIcon |
| Sun, Moon | SunIcon, MoonIcon |

## 5. Brand inks and surfaces

Search the project's own code for these. The components already use the new tokens.

| before | after |
| --- | --- |
| text-[var(--bg-canvas)] on a brand fill | text-[var(--fg-on-brand)] |
| text-[var(--zinc-50)] on a brand fill | text-[var(--fg-on-brand)] |
| text-[var(--fg-brand)] on text below 24px | text-[var(--fg-brand-text)] |
| text-[var(--fg-brand-hover)] on the brand tint | text-[var(--fg-brand-text)] |
| --fg-brand-on-tint | --fg-brand-text |
| bg-[var(--bg-surface)] on a card | bg-[var(--bg-card)] |
| bg-[var(--bg-surface)] on a dialog, menu or code block | bg-[var(--bg-overlay)] with the sheen class |
| bg-[var(--bg-surface)] on an input | bg-[var(--bg-field)] |

## 6. Card

The parts are the same. The look is new: near black with a glow in the corner, a border that lights up on hover, and a header that wraps. New: size (sm, md, xl), and as and icon on CardLabel. The terminal variant sets its own text color, so it reads in light mode.

## 7. Themes

The ThemeSwitcher needs every theme in your CSS: run init with --themes=all. Light mode brands moved slightly (entrepta light is now #6656FF) so every ink clears AA, and light --fg-muted is #68686F. Add suppressHydrationWarning to your <html> if you use ThemeScript or ModeScript.

## 8. Components that changed

- **Tabs.** The × is a real button next to the tab, on the active tab only. Route tabs use TabNav and TabNavLink, and variant="window" draws the title bar. Tabs depend on motion.
- **Dropdown, Toast, Tooltip, CommandPalette.** All sit on the overlay surface. A highlighted row takes the brand tint instead of an edge bar, keyboard hints are Kbd, and a toast shows its status as an icon tile. The Toaster adds a close button.
- **StatusBar.** position="static" puts it in your layout instead of five override classes.
- **CodeBlock.** A failed copy says copy failed instead of claiming it copied.
- **RollingNumber, Reveal, TypeIn, Spotlight.** Bring the motion package when you add them. Nothing else needs it.

## 9. New in v2: 17 components

If the project built its own version of one of these, replace it with `npx @entrepta/cli@latest add <name>` and delete the local copy.

- **Kbd** (Primitives): A key or a shortcut, as a chip or plain, that turns brand in a highlighted row. Replaces hand-styled key chips next to shortcuts. [docs](https://entrepta.vercel.app/docs/components/kbd.md)
- **Textarea** (Forms): Multi-line text field in sans, with an error state. Replaces a textarea styled by hand. [docs](https://entrepta.vercel.app/docs/components/textarea.md)
- **Checkbox** (Forms): Checkbox over a native input, with a label, a description and a mixed state. Replaces native checkboxes with accent-color, or a custom box. [docs](https://entrepta.vercel.app/docs/components/checkbox.md)
- **Switch** (Forms): On/off switch over a native checkbox, with an optional label. Replaces a custom toggle built from a div. [docs](https://entrepta.vercel.app/docs/components/switch.md)
- **Field** (Forms): Label, control and error or hint, wired together for screen readers. Replaces label, hint and error markup wired to aria-describedby by hand. [docs](https://entrepta.vercel.app/docs/components/field.md)
- **FilterPill** (Forms): Toggle pill for one filter value, with an optional count and icon. Replaces filter buttons that keep their state in the URL. [docs](https://entrepta.vercel.app/docs/components/filter-pill.md)
- **Sidebar** (Layout): 56px icon rail with a diamond that travels to the active item. Replaces an icon rail with its own active indicator. [docs](https://entrepta.vercel.app/docs/components/sidebar.md)
- **PageOutline** (Layout): Sticky outline of the page's sections with a scrollspy, from 1100px up. Replaces a table of contents with a scrollspy. [docs](https://entrepta.vercel.app/docs/components/page-outline.md)
- **SectHead** (Content): The $ command rule that opens a section, with meta on the right. Replaces a $ command rule above a section. [docs](https://entrepta.vercel.app/docs/components/sect-head.md)
- **Doc parts** (Content): Long-form page pieces: doc labels, sections, serif heading, prose, Em, Strong. Replaces the eyebrow, section and display heading pieces of a long page. [docs](https://entrepta.vercel.app/docs/components/doc-parts.md)
- **ChromeMessage** (Feedback): Full-page status screen for 404s and errors, as a terminal command. Replaces 404 and error screens. [docs](https://entrepta.vercel.app/docs/components/chrome-message.md)
- **PageLoading** (Feedback): Loading screen in CSS alone, that says more only when the wait runs long. Replaces a loading.tsx with dots or lines. [docs](https://entrepta.vercel.app/docs/components/page-loading.md)
- **Reveal** (Motion): Entrance that rises and fades in once on screen, with a capped list stagger. Replaces fade-in-on-scroll wrappers. [docs](https://entrepta.vercel.app/docs/components/reveal.md)
- **TypeIn** (Motion): Text that assembles piece by piece, with the full sentence always in the DOM. Replaces a typewriter heading. [docs](https://entrepta.vercel.app/docs/components/type-in.md)
- **RollingNumber** (Motion): Odometer counter that rolls into place and turns once on hover. Replaces an odometer counter. [docs](https://entrepta.vercel.app/docs/components/rolling-number.md)
- **Spotlight** (Motion): Brand glow that trails the cursor across a card on a spring. Replaces a glow that follows the cursor. [docs](https://entrepta.vercel.app/docs/components/spotlight.md)
- **ArrowLink** (Motion): Text link with a traveling arrow and a brand rule that wipes in. Replaces a link with a moving arrow. [docs](https://entrepta.vercel.app/docs/components/arrow-link.md)

## Rules to keep while migrating

## Tokens

- Every color is a CSS variable from `app/globals.css`. Never write a hex in a component.
- Surfaces: `--bg-canvas` for the page, `--bg-card` for cards, `--bg-surface` for what sits above a card (menus, tooltips, code), `--bg-overlay` for dialogs.
- Text: `--fg-primary`, `--fg-secondary`, `--fg-muted`. Borders: `--border-subtle`, `--border-strong`.
- Brand: `--fg-brand` for fills, borders, glyphs and text 24px and up. `--fg-brand-text` for brand-colored text below 24px. `--fg-on-brand` for text on a brand fill.
- Brand accents mix from the brand, so they follow the theme: `--border-brand`, `--border-brand-strong`, `--bg-surface-brand`, `--fg-brand-glow`. For a new one, use `color-mix(in srgb, var(--fg-brand) N%, transparent)`.
- Status: `--status-success`, `--status-warning`, `--status-error`, `--status-info` for fills and dots, `--status-*-fg` for text, `--status-*-soft` for tints.
- In Tailwind, write tokens as `bg-[var(--bg-card)]` and `text-[var(--fg-muted)]`.

## Type

- Font sizes come from ten steps and nothing else: `text-display-xl`, `text-display-lg`, `text-display-md`, `text-heading-lg`, `text-heading-md`, `text-body-lg`, `text-body-md`, `text-mono-md`, `text-mono-sm`, `text-mono-xs`.
- A step sets size and leading, never the family. Pair it with `font-serif` (titles, proper nouns), `font-mono` (UI, labels, metadata) or `font-sans` (long prose).
- No arbitrary pixel sizes and no Tailwind default size steps.
- Compose classes with `cn()` from the utils file. It knows the scale, so a step and a color class survive together.

## Rules

- Do: Use a CSS variable for every color. Don't: Hardcode a hex in a component. It stops following the theme.
- Do: Derive brand accents from --fg-brand with color-mix(). Don't: Hardcode the violet. It stops reacting when someone picks another theme.
- Do: Put --fg-on-brand on a brand fill. Don't: Reach for a fixed near-white. It fails contrast in 7 of the 12 theme and mode pairs.
- Do: Color brand text below 24px with --fg-brand-text. Don't: Use --fg-brand for small text. It fails AA in 4 of the 12 pairs.
- Do: Put cards on --bg-card, menus on --bg-surface, dialogs on --bg-overlay. Don't: Fill a grid of cards with --bg-surface. It reads as a field of gray.
- Do: Size text with the ten scale steps, such as text-mono-sm. Don't: Write text-[13px] or Tailwind's text-sm. A test fails on both.
- Do: Pick the family at the call site: font-serif, font-sans or font-mono. Don't: Expect a scale step to set the font. It sets size and leading only.
- Do: Let a row of two halves wrap, and keep each half on one line. Don't: Assume it fits. A card clips with no ellipsis, so overflow looks like missing data.
- Do: Hide glyphs such as ◆, $ and // with aria-hidden. Don't: Let a screen reader say black diamond suit before every label.
- Do: Give bare buttons the .focus-ring class. Don't: Rely on outline. The reset removes it from every button.
- Do: Import Phosphor from @phosphor-icons/react/dist/ssr in files without use client. Don't: Import the package root there. next build fails with an error that names no file.
- Do: Pass your router's link with asChild or linkComponent, and mark the current item with active. Don't: Import next/link in a copied component. It stops working outside Next.js.
