Firestarter is a pre-built design system, CMS, and frontend for low to mid-tier client projects. It bridges the gap between bespoke builds and Squarespace/Framer sites for clients who deserve a real CMS and higher design quality.
The system ships a complete set of flexible components and page layouts designed and built to a consistent, production-ready standard. Duplicate the repo, customize design tokens (colors, type, spacing), add any unique components or page templates, and deploy.
Figma Design System → Storybook → Craft CMS (headless) → Next.js Frontend
| Layer | Tool | Purpose |
|---|---|---|
| Design | Figma | Design system source of truth |
| Components | Storybook | Component library with Figma tokens |
| CMS | Craft CMS 5 | Headless content management |
| Frontend | Next.js 16 | App Router, Server/Client Components |
/firestarter
├── /site # Next.js + Storybook
│ ├── .storybook/ # Storybook configuration
│ ├── src/
│ │ ├── app/ # Next.js App Router
│ │ ├── components/ # Components with co-located stories
│ │ ├── stories/ # Storybook default stories
│ │ ├── tokens/ # Design tokens from Figma
│ │ └── lib/ # Utilities and API helpers
│
├── /cms # Craft CMS (headless)
│ ├── config/ # Craft configuration
│ ├── templates/ # Twig templates (minimal)
│ └── web/ # Public directory
│
└── /docs # Project documentation
Design specs and implementation plans live in docs/design-specs/ and docs/implementation-plans/. Each changelog entry links to the relevant spec and plan for deeper context.
- Node.js (see
.nvmrcfor version) - DDEV (for local Craft development)
This repo uses a pnpm workspace — the root pnpm-workspace.yaml defines site as a workspace package. A single install from the root handles everything:
# Set correct Node version
nvm use
# Activate the pinned pnpm version
corepack enable
# Installs all dependencies (root + site workspace)
pnpm installThis installs Lefthook and Prettier at the root, site dependencies in /site, and automatically configures pre-commit hooks for formatting, type checking, and linting.
See cms/README.md for Craft setup instructions (DDEV, env file, install, license key).
The root provides proxy commands for the frontend workspace:
# Start Next.js at localhost:3000
pnpm dev
# Start Storybook at localhost:6006
pnpm storybook
# Rebuild design tokens
pnpm tokensThese proxy to the site workspace via pnpm --filter site. See site/README.md for full frontend setup, component structure, design tokens, and testing.
The frontend fetches content from Craft CMS via GraphQL. The integration includes:
- GraphQL data fetching —
site/src/lib/graphql/client.tswraps fetch with caching - Live preview — Editors see draft changes in Craft's preview iframe
- Cache revalidation — Webhook from Craft invalidates Next.js cache on publish
When an editor publishes content in Craft, a webhook POSTs to Next.js to purge the cached data so the next visitor sees fresh content.
1. Add environment variables to cms/.env:
REVALIDATION_SECRET=your-random-string
REVALIDATION_URL=http://host.docker.internal:3000/api/revalidate # local dev2. Configure the webhook in Craft CP → Settings → Webhooks:
| Field | Value |
|---|---|
| Name | Revalidate Next.js |
| Sender Class | craft\elements\Entry |
| Event Name | afterSave |
| URL | $REVALIDATION_URL |
| Method | POST |
| Headers | Content-Type: application/json |
3. Set the webhook payload (Twig template):
{% set entryUri = event.sender.uri %}
{% set revalidationSecret = getenv('REVALIDATION_SECRET') %}
{{
{
secret: revalidationSecret,
uri: entryUri
}|json_encode|raw
}}You can verify the webhook is working in Craft CP → Utilities → Webhooks, which shows request/response history. See docs/learnings/2026-01-27-on-demand-revalidation.md for details on how revalidation works under the hood.
Copy the example file and fill in values:
cd site
cp .env.example .env.local| Variable | Purpose |
|---|---|
CRAFT_URL |
Craft CMS URL (e.g., http://cms.ddev.site) |
REVALIDATION_SECRET |
Shared secret for webhook validation |
The CMS also needs REVALIDATION_SECRET in cms/.env (same value).
- Use HTTP (not HTTPS) for
CRAFT_URLlocally — Node rejects self-signed certs - To test Craft's live preview feature, you need HTTPS (the CP iframe won't load HTTP content). Run
pnpm dev --experimental-httpsand updatePRIMARY_SITE_URL=https://localhost:3000incms/.env. Revert to HTTP when done — webhooks won't work over HTTPS locally. - See
docs/learnings/2026-01-27-local-vs-production-config.mdfor details
Lefthook runs automated checks before each commit:
- Prettier — Auto-formats staged files and re-stages them
- TypeScript — Full project type check (
tsc --noEmit) - ESLint — Lints staged JS/TS files only
Jobs run in order. Prettier runs on all supported file types; TypeScript and ESLint only run on JS/TS files in /site.
To format the entire repo manually:
# Format all files
pnpm format
# Check without writing
pnpm format:checkTo lint the frontend manually:
# Check for errors
pnpm lint
# Auto-fix (includes import sorting)
pnpm lint --fixTo bypass hooks temporarily (use sparingly):
git commit --no-verify -m "message"- Client safety — CMS structured so clients can manage content without breaking layouts
- Off-the-shelf components — Most projects use existing components, few custom additions
- Clear separation — Frontend and CMS are distinct, self-contained directories
- Clone and go — New projects clone the repo; no complex setup or shared dependencies