Use when an existing design system component needs to change. Triggers on "add a variant", "update the button", "the modal now has X", "change the style of", "add loading state", or when a Figma link or description shows a new state for a component already in components/ui/. Updates both the component and its documentation so engineers know about the change.
An existing component in @mnee/ui needs to change — a new variant, a new state, a style change, or a new prop. This skill updates the component file and its documentation so everything stays in sync.
Design ownership philosophy: MNEE UI is a designer-owned system. Every update must reinforce — never relax — the principle that engineers provide data and wire handlers. They do not choose colors, layout, or structure. If a requested change would expose layout or style decisions to engineers, push back and find a data-only prop instead.
From the user's request, determine:
loading to Button)icon to Badge)If a Figma URL is provided: First invoke the figma-import skill (extraction + design system audit phases). The notes from that skill replace the need to manually call get_design_context and get_screenshot here — they will have already been done, and the extraction notes and component mapping table are ready for use in Phase 4 below.
If ambiguous, ask before proceeding.
Read both files in parallel:
components/ui/<name>.tsxapp/docs/components/<name>/page.tsxCheck for callers that might break:
grep -r "<ComponentName" /Users/fostan/mnee-ui/app --include="*.tsx" -l
Audit the doc page: do the existing code snippets match what the live demos render? If there are local helper functions defined in the doc page, flag them — they should be promoted to real components.
Also assess the component's current API shape:
Before writing any code, evaluate the new prop through this lens:
Engineers pass data and handlers. The component decides everything else.
| Engineer provides | Design system decides |
|---|---|
amount="$1,204" | How amount is styled (size, weight, color) |
status="success" | Which badge color maps to success |
loading={isLoading} | What the skeleton looks like |
onEdit={() => router.push(...)} | Whether that renders a ghost or primary button |
title="Stripe Payments" | Typography, spacing, layout |
If a requested change would add any of these, do not add them. Propose a data-only alternative instead:
className on internal sections (e.g. headerClassName, bodyClassName)color, backgroundColor, textColor as free-form stringscustomIcon as a raw JSX slot with no constraintsstyle prop pass-throughs to internal elementsIf the component is a compound component (exports sub-components like CardHeader, CardTitle) and the change being requested adds new layout or complexity, evaluate whether the component should be migrated to a discriminated union before adding the new prop:
See the add-component skill for the full discriminated union template.
If the component already uses discriminated unions, a new variant means a new type branch — not a new optional prop bolted onto an existing type:
// WRONG — bolting onto existing type
type CardProps = {
variant: "balance" | "module" | "summary"; // ← summary shares balance's type
amount?: string; // optional because summary doesn't need it
summaryItems?: Item[]; // optional because balance doesn't need it
}
// RIGHT — new branch with its own required props
type SummaryCardProps = {
variant: "summary";
items: Item[]; // required, clearly typed
className?: string;
}
export type CardProps = BalanceCardProps | ModuleCardProps | SummaryCardProps;
components/ui/<name>.tsxAdding a variant or size (simple component):
Record<VariantType, string> objectAdding a new variant (discriminated union):
ComponentProps unionAdding a prop:
size: use Omit<React.InputHTMLAttributes<HTMLInputElement>, "size">"use client" if hooks or event handlers are introduced for the first timeStyle change:
cn() for class mergingAnimated show/hide:
Behavioral changes:
document.body.style.overflow in useEffect with cleanupapp/docs/components/<name>/page.tsxUpdate when:
Design ownership callout: If the doc page does not already have the amber callout below the title, add it now (immediately before the first ComponentPreview):
{/* Design ownership callout */}
<div className="mb-8 rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-800">
<span className="font-semibold">Design system component.</span>{" "}
Fully styled by the UX team — no color or style changes needed. Choose a variant, provide your content, and wire your handler or route.
</div>
Critical: The code snippet shown to engineers must exactly match what the live demo renders. If you update a demo, update its snippet. They must always be identical.
New variant section pattern:
<h2 className="text-lg font-semibold text-gray-900 mb-3">Loading state</h2>
<ComponentPreview code={snippets.loading} className="mb-8">
<Button loading>Processing</Button>
</ComponentPreview>
And in snippets: