Build for Dunea
Quickstart: your first theme
Make a theme from tokens, fonts and a style guide, validate it, and submit it for review.
A theme is a single JSON file: design tokens, two or three fonts, and an art-direction guide that the Dunea AI follows when it designs an app. There is no server and no code. In this guide you build Harbour, a calm, nautical theme for local businesses.
You need Node.js 18 or later and a Dunea account.
Scaffold the theme
npx -p @dunea/plugin-sdk dunea-plugin init harbour --theme
cd harbourThis creates dunea.theme.json and a README.md.
Set the tokens
Open dunea.theme.json and set the listing details and tokens:
{
"schemaVersion": 1,
"kind": "theme",
"id": "harbour",
"version": "0.1.0",
"name": "Harbour",
"tagline": "Calm navy and sand, with a crisp serif, for local businesses by the water.",
"description": "Harbour pairs deep navy and warm sand with a crisp Fraunces serif and a quiet Inter text face. Generous spacing, square-ish corners and a single coral accent. Made for cafes, boat hire, guesthouses, surf schools and other local businesses that want to feel calm and trustworthy.",
"icon": "https://example.com/harbour/icon.png",
"screenshots": ["https://example.com/harbour/home.png"],
"developer": {
"name": "Your Studio",
"supportEmail": "hello@example.com",
"privacyUrl": "https://example.com/privacy"
},
"pricing": { "model": "free" },
"categories": ["local-business", "minimal"],
"tokens": {
"colors": {
"background": "#fbf8f3",
"surface": "#f1ebe1",
"text": "#14213d",
"muted": "#5c6478",
"primary": "#14213d",
"primaryText": "#fbf8f3",
"accent": "#e76f51",
"border": "#e2d9cb"
},
"fonts": { "heading": "Fraunces", "body": "Inter" },
"radius": 4,
"density": "airy",
"typeScale": 1.333
},
"guide": "...",
"sections": []
}- Every colour is a
#rrggbbhex. Check thattextonbackgroundandprimaryTextonprimaryare easy to read. - Fonts must come from the allow-list.
- Leave out
darkColorsfor a single-mode theme, or add the same eight keys for a dark palette.
Write the guide
The guide is markdown, 200 to 12,000 characters, and it is what makes a theme feel designed. Write concrete instructions the AI can follow. In JSON, newlines are \n:
"guide": "## Mood\nA quiet harbour town on a clear morning: calm, honest, unhurried. Never loud, never salesy.\n\n## Layout\n- Wide margins and one idea per section. Separate sections with space and a single 1px rule in the border colour, never with coloured bands.\n- Left-align text. Keep paragraphs under 65 characters per line.\n\n## Typography\n- Headings in Fraunces at weight 600, tight leading; body in Inter at 16-17px, line-height 1.6.\n- Small labels in Inter, uppercase, tracked 0.12em, muted colour.\n\n## Colour\nNavy and sand carry the page. Coral is the accent: one button, link underline or numeral per screen, never a background.\n\n## Imagery\nNatural light, water, rope, wood and real people at work. No stock handshakes, no gradients, no illustrations of waves.\n\n## Motion\nGentle fades and 8px rises over 500ms. No bounces, no parallax.\n\n## Copy tone\nShort, plain sentences. Concrete details (opening hours, distances, prices). Buttons say what happens: \"Book a table\", \"See the timetable\".\n\n## Don't\n- No drop shadows or rounded pills.\n- Never more than one accent element per viewport."Put the most important direction first: if the AI's brief runs long, the end of the guide is what gets trimmed. See Writing a great guide.
Add section recipes (optional)
Recipes describe how to build a particular section in your style. The AI fetches a recipe before it builds that section.
"sections": [
{
"name": "Hero",
"recipe": "Full-width section on the background colour. Left 6 columns: a small uppercase label, a two-line Fraunces heading at the 5xl step, one sentence of body copy in muted text, and one primary button. Right 6 columns: a single landscape photo with 4px corners. On mobile the photo comes first."
},
{
"name": "Opening hours",
"recipe": "A two-column definition list on the surface colour: day names on the left in uppercase tracked labels, times on the right in the body face. 1px border-colour rules between rows. Today's row has a coral dot before the day name."
}
]Validate
npx -p @dunea/plugin-sdk dunea-plugin validateOK dunea.theme.json: theme "harbour" v0.1.0
guide: 1083 chars, sections: Hero, Opening hoursnpx -p @dunea/plugin-sdk dunea-plugin check also runs the review checklist, including the contrast checks (DUN-T001, DUN-T002).
Publish and submit
Create a developer token in the Dunea developer portal, then:
export DUNEA_DEV_TOKEN=...
npx -p @dunea/plugin-sdk dunea-plugin publish --submitThis creates the listing, uploads 0.1.0 and submits it for review. If it prints a client secret, store it safely; themes do not receive webhooks, so you will not use it.
Track the review with:
npx -p @dunea/plugin-sdk dunea-plugin status harbourNext steps
- How the AI uses each token, and how to write recipes: Themes guide
- Every field and its limits: Manifest reference
- Charging for a theme: Pricing and payouts
- Whole sites instead of a look: Templates
Quickstart: your first plugin
Build a plugin that posts paid orders to Slack: scaffold, handle shop.order.paid, validate, publish and submit for review.
Templates
Publish a whole Dunea site other people start from: defineTemplate, what the snapshot copies (and never copies), the remix flow and pricing.