- name
- export-component-to-other-repo
- description
- Port a component from nextjs-css-agent-components into a different repo without losing the design-system precision. Use when the user says "copy this component to <repo>", "lift Button into <repo>", "bring the Thin layout over", or otherwise wants a faithful transplant of one or more components from this codebase. Carries over the CSS Module, the theme tokens it depends on, the typography scale, the spacing/transition conventions, and the path-alias plumbing required for it to compile.
# Export a component to another repo
This skill exports a component (or a set of components) from `nextjs-css-agent-components` into a target repo while preserving every value that makes it look right: pixel measurements, type tokens, theme variables, transition timing, breakpoint, and z-index. The starter is a precision wireframe system — paraphrasing values is the failure mode this skill exists to prevent.
**Heads-up on styling:** SASS is **not** a dependency. All styling is vanilla CSS Modules (`.module.css`) using **native CSS nesting** (the `&` selector). Next.js compiles this via Lightning CSS with no preprocessor or PostCSS plugins. Don't introduce a SASS dependency in the target. (The package was previously named `nextjs-sass-starter`; ignore that name if you encounter it in old links or forks.)
## When to use
- "Copy `<Component>` into `<other-repo>`"
- "Bring the Thin layout / Button / Input / Navigation / Footer over to `<repo>`"
- "Port the modal system into `<repo>`"
- "Lift the typography scale into `<repo>`"
## When NOT to use
- The user wants a *redesign* of the component for the new repo. (Use a normal task; this skill preserves, it doesn't reinterpret.)
- The user wants to publish a shared package. (That is a different procedure — this skill copies source.)
- The target repo already uses Tailwind / styled-components / Emotion. Stop and ask the user how they want the styling reconciled before continuing.
## Pre-flight: profile the source
Before touching the target repo, build a manifest of what the component actually depends on. For each component you are exporting:
1. **Read the `.tsx` and `.module.css` together.** They are colocated in this repo and must move together.
2. **List the imports.** Anything from `@elements`, `@components`, `@patterns`, `@runtime`, `@common`, `@root`, `@modules` is a transitive dependency that has to either come along or be re-pointed. Note the **tier** of each — Tier-1 (`@elements/`) deps are usually free to copy, Tier-2 (`@components/`) deps pull in their own atoms, Tier-3 (`@patterns/`) deps are page-level and rarely worth porting on their own.
3. **Grep the CSS for `var(--...)`.** Every `--theme-*`, `--color-*`, `--type-scale-*`, `--font-family*`, `--theme-graph-*`, `--theme-box-shadow-*` token referenced is a global it depends on. Note all of them.
4. **Grep the CSS for `@media`, `transition:`, `animation:`, `@keyframes`, `100dvh`, and `&` (native nesting).** These are the precision-sensitive surfaces. If the target's CSS pipeline doesn't support native nesting, you'll have to flatten `&:hover { ... }` into `.foo:hover { ... }`.
5. **Note the spacing values used** (`24`, `48`, `64`, `4`, `8`, `12`, `16`, `32`) and any radii (`4px`, `8px`).
6. **Check for body/document assumptions** — does the component assume `body.theme-light` exists? Does it use `document.body.classList` (e.g. theme switcher)? Does it require a React context (modals, providers)?
Write the manifest down before editing the target. Do not skip this step — the failure mode of this skill is "I copied the TSX and the styles broke."
## What you must carry over (the precision payload)
These are non-negotiable. If the target repo doesn't already have them, copy them in.
### 1. The full theme token system (from `global.css`)
Even if you only want one component, you usually need to copy the whole `:root` palette + every `body.theme-*` block, because the component's `var(--theme-*)` references resolve through them. Specifically:
- The raw color palette under `html, body { ... }`:
- Black/white/gray: `--color-black-100`, `--color-black-100-{1,2,4}`, `--color-gray-{100,90,80,70,60,50,40,30,20,10}` (with `-3`, `-6`, `-2` opacity variants where defined), `--color-white`, `--color-white-{1,2,4}`.
- Status: `--color-red-60`, `--color-red-60-3`, `--color-green-60`, `--color-green-60-3`, `--color-gold-30`.
- Daybreak orange ramp: `--color-daybreak`, `--color-daybreak-{10..100}` plus opacity variants.
- Neon green ramp: `--color-neon-green-{10..100}` plus opacity variants.
- Blue ramp: `--color-blue-{10..100}` plus `-1`, `-2`, `-5` opacity variants.
- Black-daybreak: `--color-black-daybreak-100`, `--color-black-daybreak-100-4`.
- Graph tokens: `--theme-graph-positive`, `--theme-graph-positive-subdued`, `--theme-graph-netural` (yes, that spelling — preserve it), `--theme-graph-negative`, `--theme-graph-negative-subdued`.
- All five `body.theme-*` blocks: `theme-light`, `theme-dark`, `theme-daybreak`, `theme-blue`, `theme-neon-green`. Each redefines the semantic tokens (`--theme-background`, `--theme-background-overlay`, `--theme-background-box-{top,front,side}`, `--theme-foreground`, `--theme-foreground-secondary`, `--theme-text`, `--theme-border`, `--theme-border-box`, `--theme-border-subdued`, `--theme-button`, `--theme-button-text`, `--theme-primary`, `--theme-input-active`, `--theme-success`, `--theme-success-subdued`, `--theme-error`, `--theme-error-subdued`, `--theme-box-shadow-modal`, `--theme-box-shadow-button`, `--theme-box-shadow-button-hover`) and the matching `::-webkit-scrollbar` rules.
- The `<body>` in the target's document/html shell must get `className="theme-light"` (or whichever theme is the default) so the tokens resolve. In Next Pages Router, this lives in `pages/_document.tsx` (`<body className="theme-light">`). In App Router, set it on `<body>` in `app/layout.tsx`.
If the target only ever needs one theme, you can copy just that one `body.theme-*` block — but copy the full raw palette regardless, because the semantic tokens reference it.
### 2. The typography scale
Even a Button references `var(--type-scale-fixed-small)`. Copy the full set into the target's globals:
```css
--type-scale-1: 3.815rem;
--type-scale-2: 3.052rem;
--type-scale-3: 2.441rem;
--type-scale-4: 1.953rem;
--type-scale-5: 1.563rem;
--type-scale-6: 1.25rem;
--type-scale-7: 1rem;
--type-scale-fixed-large: 20px;
--type-scale-fixed-medium: 16px;
--type-scale-fixed-small: 14px;
--type-scale-fixed-tiny: 12px;
--type-scale-fixed-label: 10px;
```
Plus the responsive root font-size shift (this is what makes the modular `rem`-based scale shrink on mobile):
```css
html, body {
font-family: var(--font-family);
font-variant-numeric: tabular-nums;
font-size: 16px;
@media (max-width: 768px) { font-size: 12px; }
}
```
If you skip the `font-size: 12px` mobile override, every `--type-scale-1..7` value silently changes scale on mobile vs. the source repo.
### 3. The font stack (and ServerMono if the component uses mono)
```css
--font-family: -apple-system, BlinkMacSystemFont, helvetica neue, helvetica, sans-serif;
--font-family-mono: 'ServerMono', Consolas, monaco, monospace;
--font-family-serif: Georgia, Times New Roman, serif;
```
If the component (or anything under it) uses `var(--font-family-mono)`, copy the two `@font-face` declarations for `ServerMono` from the top of `global.css` verbatim — they reference S3-hosted woff2/woff/otf files at `intdev-global.s3.us-west-2.amazonaws.com`. Don't re-host or rename; the URLs are stable.
### 4. The CSS reset
The reset block in `global.css` (the giant comma-separated selector list ending in `box-sizing: border-box; vertical-align: baseline; margin: 0; padding: 0; border: 0;`, plus the `display: block` block for HTML5 elements). Components rely on `box-sizing: border-box` being the default. If the target has its own reset (e.g. Tailwind's preflight, `normalize.css`, `modern-normalize`), verify it sets `box-sizing: border-box` globally — if not, copy this reset.
### 5. The global animations (only if needed)
If the exported component (or its CSS) references `animation: blur ...`, `fade`, `slideUp`, `slideDown`, `slideLeft`, or `slideRight`, copy the matching `@keyframes` block from `animations.css` into the target's globals. All five share the 0% / 25% / 75% / 100% timing — preserve the keyframe percentages exactly.
### 6. Helpers from `common/utilities.ts`
If the TSX imports anything from `@common/utilities`, copy just those functions. Do not pull the whole file unless the component uses most of it. Inventory: `noop`, `pluralize`, `getOrdinalNumber`, `getDomainFromEmailWithoutAnySubdomain`, `onHandleThemeChange`, `formatDollars`, `calculatePositionWithGutter[ById]`, `leftPad`, `toDateISOString`, `elide`, `bytesToSize`, `isEmpty`, `createSlug`, `isUrl`, `debounce`, `timeAgo`, `classNames`, `generateNonce`, `filterUndefined`. `classNames` is the most commonly needed.
### 7. The modal system (only if the component opens or is a modal)
If the component calls `useModals()` or is registered via `modals.open(...)`, you must port:
- `runtime/modals/ModalContext.tsx` (the `ModalProvider`, `useModals` hook, `ModalContext`, types)
- `runtime/modals/GlobalModalManager.tsx` (the renderer)
- `runtime/modals/GlobalModalManager.module.css`
- The concrete modal component itself, from `patterns/modals/Modal*.tsx`
Wrap the target's app root in `<ModalProvider>` and place exactly one `<GlobalModalManager />` somewhere it will always be mounted (typically inside the page shell, after the main content).
## Step-by-step procedure
### Step 1 — confirm the target
Ask the user (or read from context) the absolute path of the target repo. Confirm:
- Framework (Next.js Pages, Next.js App, Vite + React, CRA, Remix, etc.).
- Existing styling approach (CSS Modules, Tailwind, styled-components, plain CSS).
- Whether path aliases (`@/`, `~/`, etc.) are configured in its `tsconfig.json`.
- Whether a global stylesheet exists where you can paste tokens.
- **Whether the target's CSS pipeline supports native CSS nesting** (the `&` selector). Next.js 13.4+, Vite 5+, and any pipeline using Lightning CSS or PostCSS ≥ 8.4 with `postcss-nesting` (or modern browsers via the unprefixed CSS Nesting Module) all support it. If unsure, write a one-line `.test:hover { color: red; }` nested in `.test { ... }` and check whether it applies.
If the target uses Tailwind or CSS-in-JS, **stop and ask** how the user wants to reconcile — pasting `.module.css` files into a Tailwind project is a decision, not a default. If the target does **not** support native nesting, you must flatten every `&:hover`, `&:focus`, `&:visited`, `&::before`, `& .child`, etc. into the explicit selector form before pasting.
### Step 2 — write the dependency manifest
Per the pre-flight section, list every transitive `.tsx`, `.module.css`, theme token, type-scale token, keyframe, and utility the export touches. Show this list to the user before copying so they can confirm scope.
### Step 3 — choose a destination layout in the target
Default mapping when the target has no existing convention:
```
<target>/components/<original-bucket>/ (mirror elements/, components/, patterns/, runtime/)
<target>/styles/tokens.css (the palette + theme blocks + type scale + fonts + reset)
<target>/styles/animations.css (only if keyframes are needed)
```
If the target already has its own convention (e.g. `src/ui/`, `app/_components/`), match it. The folder name is less important than the colocation rule: `Foo.tsx` and `Foo.module.css` must live in the same directory.
### Step 4 — copy globals first, components second
In this order:
1. Copy/merge the theme tokens, type scale, fonts, reset into the target's global stylesheet. Import it once at the app entry.
2. Copy the keyframes file if needed and import it once at the app entry.
3. Set the default theme class on `<body>` (`theme-light` unless the user says otherwise).
4. Copy the component `.tsx` + `.module.css` files. Preserve every `var(--theme-*)` reference and every nested `&` selector (or flatten them if the target doesn't support native nesting — see Step 1). Do not "simplify" `box-shadow: 0 0 0 1px var(--theme-border)` to `border: 1px solid` — the no-layout-shift behavior is intentional.
5. Copy any transitive component dependencies you identified.
6. Copy any utilities (`classNames`, etc.) the TSX imports.
### Step 5 — re-point imports
Replace the source aliases with whatever the target uses:
| Source | Replace with |
| --------------------- | ----------------------------------- |
| `@elements/<bucket>/Foo` | target equivalent (e.g. `@/components/ui/Foo` or relative `../Foo`) |
| `@elements/<bucket>/Foo.module.css` | same path with `.module.css` |
| `@components/Foo` | target equivalent for tier-2 molecules |
| `@patterns/<bucket>/Foo` | rarely exported; if so, mirror under the target's page-level folder |
Voir sur GitHub