Themes
First-class storefront design. A tenant can hold many themes, swap between them, fork drafts, and ship custom CSS escape hatches.
{
"theme": {
"id": "308...",
"handle": "noir",
"name": "Noir",
"tokens": {
"--background": "#0a0a0a",
"--foreground": "#fafafa",
"--primary": "#fff",
"--primary-foreground": "#000",
"--radius": "0.5rem",
"--ring": "#fafafa"
},
"font_body": "Geist",
"font_heading": "Geist",
"font_url_imports": ["https://fonts.googleapis.com/..."],
"layout_density": "comfortable",
"custom_css": "/* escape hatch */",
"logo_url": "https://...",
"logo_dark_url": "https://...",
"favicon_url": "https://...",
"is_active": true,
"parent_theme_id": null
}
}
Tokens
tokens is a free-form JSONB map of CSS variable names to values.
The shadcn convention (--background, --foreground, --primary,
--radius, ...) is recommended but not enforced. The storefront
just emits a :root { ... } block from these.
One active theme at a time
A partial unique index enforces (account_id) WHERE is_active = TRUE.
Activating a theme atomically deactivates the previous active one.
curl -X POST "$API/themes/{theme_id}/activate.json" \
-H "Authorization: Bearer $NEVIOS_KEY"
Drafts (parent theme)
Fork the live theme into a draft, edit the draft, then activate-swap:
# Fork the active theme
curl -X POST "$API/themes.json" \
-H "Authorization: Bearer $NEVIOS_KEY" -H "Content-Type: application/json" \
-d '{
"handle": "noir-v2",
"name": "Noir v2 (draft)",
"parent_theme_id": 308...,
"tokens": { ... new values ... },
"is_active": false
}'
# Test in dashboard preview...
# When ready, activate
curl -X POST "$API/themes/{new_theme_id}/activate.json" \
-H "Authorization: Bearer $NEVIOS_KEY"
The previous theme stays in the database (not archived) — easy to roll back.
Custom CSS escape hatch
For one-off design tweaks beyond what tokens express:
{
"custom_css": ".product-card .price-tag { font-weight: 800; }"
}
Loaded after tokens. No sandboxing yet — staff-only authoring.
Endpoints
POST /admin/2026-01/{handle}/themes.json
GET /admin/2026-01/{handle}/themes.json
GET /admin/2026-01/{handle}/themes/active.json
PUT /admin/2026-01/{handle}/themes/{id}.json
POST /admin/2026-01/{handle}/themes/{id}/activate.json
DELETE /admin/2026-01/{handle}/themes/{id}.json
Cannot archive the currently-active theme — 409. Activate another
first.
Next: Email templates.