CHAPTER 03

Theme Development

Agency-owned surface: React layout, section components, and defineSection contracts. Site owners stay out of theme/**.

01

Entry

theme/index.tsx
ExportRequiredPurpose
LayoutyesWraps every page
sectionspreferredArray of defineSection contracts
stylesheetnoCSS copied into dist/
export { Layout };
export const sections = [hero, features, faq, cta];
export const stylesheet = "styles.css";
02

defineSection

Contract Checklist
export const hero = defineSection({
  type: "hero",
  description: "Opening band with headline and optional CTA",
  guidance: "Use once near the top. Keep heading under ~12 words.",
  props: z.object({
    heading: z.string().min(1),
    subheading: z.string().optional(),
    ctaLabel: z.string().optional(),
    ctaHref: z.string().optional(),
  }),
  component: Hero,
  example: {
    type: "hero",
    heading: "Ship the site. Skip the retainer.",
    ctaLabel: "See the work",
    ctaHref: "/work",
  },
});
FieldNotes
typeStable string — renaming breaks content
descriptionOne line for AGENTS.md
propsZod schema excluding type
componentReact renderer
exampleMust include matching type
guidanceOptional longer owner instructions
03

Images

Content-Driven
import { image, imageProps } from "aftercare";

props: z.object({
  heading: z.string(),
  image: image(),
})

// in component:
<img {...imageProps(image)} />

Content uses /images/team.webp; file lives at public/images/team.webp. Doctor checks local paths and alt text.

04

Sync

Agent Surface
npx aftercare watch
npx aftercare sync
PathPurpose
AGENTS.mdSite-owner agent guide
agent/catalog.toonCompact section catalog
agent/schemas/*.jsonPer-section JSON Schema
.cursor/skills/aftercare-*Cursor skills + scripts

Do not hand-edit generated agent files. Change contracts, then re-run sync/watch.

Do

  • Keep watch running while building the theme
  • Export sections via defineSection
  • Use image() for content-driven assets
  • Run doctor before handoff

Don't

  • Rename section types casually
  • Hand-edit AGENTS.md or agent/
  • Rely on browser-only APIs in SSR components
  • Skip sync after contract changes