Writing your own design
When nothing in the registry fits and the adapters can't get you close — how to hand-roll a new Cartwright design from scratch. Practical 30-minute walkthrough.
When you've outgrown the imports and want a fully custom design, copy an existing one as a starting point and edit. The whole workflow is 30 minutes if you already have a layout in mind.
Quickest path: fork an existing design
# 1. Copy the closest match
cp -r designs/studio designs/my-studio
# 2. Edit the slug + name in the new design.md
sed -i '' 's/slug: studio/slug: my-studio/' designs/my-studio/design.md
sed -i '' 's/name: Studio.*/name: My Studio Variant/' designs/my-studio/design.md
# 3. Register it in the two registry files
# (Codegen does this automatically for imports, but hand-copied folders
# need a manual update — three lines total.)Then add to designs/index.ts:
import { myStudioDesign } from "./my-studio";
const DESIGNS: Record<string, DesignPack> = {
// ... existing entries
"my-studio": myStudioDesign,
};And to designs/options.ts:
{
slug: "my-studio",
name: "My Studio Variant",
description: "Studio with my brand colors.",
mode: "website",
premium: false,
},Also rename the exported const in designs/my-studio/index.ts:
export const myStudioDesign: DesignPack = {
slug: "my-studio",
// ...
};That's the full path. Restart pnpm dev, your new design shows up in /admin/designs immediately.
When forking, you usually only edit two files
design.md— change palette, fonts, headline/tagline copyindex.ts— match the slug/name/description to the design.md (this is the registration block that the registry reads at compile-time)
The homepage.tsx and any sections/ folder you can leave alone if the layout still works. Most custom designs are just palette + copy swaps on an existing layout.
When you need a real custom layout
If the existing homepage components don't compose what you want, write your own:
// designs/my-design/homepage.tsx
import type { DesignHomepageProps } from "../types";
export default function MyDesignHomepage({
settings,
featured = [],
categories = [],
}: DesignHomepageProps) {
return (
<div className="bg-mydesign-cream text-mydesign-ink">
{/* Build whatever layout you want. The design.md sections array
becomes informational-only when you use a custom homepage. */}
</div>
);
}Then in design.md, mark the section as opaque so codegen doesn't try to re-emit:
sections:
- type: opaque
component: MyDesignHomepageThis pattern is how all three new webshop variants (minimal, editorial, bold) are structured. Their layouts break too far from the generic section atoms to compose declaratively — so they're each a single React component referenced from design.md.
Pulling shared section atoms
If you write a custom homepage, you can still reuse the Studio section atoms:
import {
StudioHero,
StudioFeatureGrid,
StudioCtaFooter,
} from "@/designs/studio/sections/_index";
export default function MyDesignHomepage(props) {
return (
<div>
<StudioHero
headline="Custom headline"
tagline="Custom tagline"
ctaLabel="Custom CTA"
ctaHref="/contact"
/>
<StudioFeatureGrid
title="Features"
features={[
{ title: "Feature 1", body: "..." },
{ title: "Feature 2", body: "..." },
]}
/>
<StudioCtaFooter
title="Get started"
ctaLabel="Sign up"
ctaHref="/contact"
/>
</div>
);
}The atoms live in designs/studio/sections/ but they're palette-token-driven so they'll pick up your design's cw-* / mydesign-* tokens automatically (if your design uses the same prefix). If you use a different prefix, you may need to either:
- Use the Studio prefix (
cw) for compatibility — many designs do this - Write your own atoms that read your design's prefix
Designing the palette
Six core colors per design:
| Field | Use it for |
|---|---|
accent | CTAs, links, headline highlights |
accentDeep | Hover state of accent (typically 10-15% darker) |
cream | Page background (typically very light or very dark) |
sand | Card/panel background (one step away from cream) |
ink | Body text (high-contrast against cream) |
muted | Secondary text (gray-ish, ~50% contrast against cream) |
Common pitfall: making cream and sand too close so cards disappear into the background. Aim for 5-10% lightness difference.
Use Coolors or Tailwind's color palette for inspiration.
Adding extra tokens
For design-specific colors that don't fit the 6-token palette (Studio has terracotta + oker, Bold has electric-yellow paper + black ink), add them to extraTokens:
extraTokens:
color-mydesign-highlight: "#ff6b35"
color-mydesign-warm-gray: "#8b7d6b"
radius-mydesign-card: "20px"
shadow-mydesign-soft: "0 2px 12px rgba(0,0,0,0.08)"Each becomes a CSS variable --<key> available everywhere in your homepage component.
Testing locally
pnpm dev
# Open localhost:3000/admin/designs
# Pick your new design from the grid
# Open localhost:3000 in a new tab → see the resultFor palette tweaks, just edit designs/my-design/design.md and re-import via CLI:
tsx scripts/design-import.ts designs/my-design/design.md --forceThe --force flag overwrites the existing scaffold. Reload the page.
Sharing your design
Once you're happy:
- Commit
designs/my-design/to your repo - Export the canonical design.md for sharing — from the admin, click Download design.md in
/admin/designs(or hit the export API directly):curl -H "Cookie: <your-admin-session>" \ https://your-shop.com/api/admin/designs/my-design/export > my-design.md - Share the
.mdfile with others — they drop it in/admin/designsand get your exact design
The community design marketplace is live: browse every built-in design, copy the prompt that builds it, or submit your own via a GitHub PR.
Related
- design.md spec — full schema reference
- Picking a design — once you've installed yours
- Overview — the orthogonality story
Import a design from Claude Design, v0, or Loveable
How to turn raw React + Tailwind output from Claude Design, Vercel v0, or Loveable into a working Cartwright DesignPack using the best-effort claude-design adapter.
Build a design with an IDE agent
Use Claude Code, Codex, or any AI coding agent to build a bespoke, premium Cartwright design in real code — the "build your own" path. Ships with a skill that teaches the agent the house rules.