Your First Theme

The third-party theme developer program isn't open yet.

This page describes how the themes the VeloCMS team builds are structured. VeloCMS is a hosted platform, so a theme built from this page can't be installed on a VeloCMS site, and submitting or selling one as an outside developer isn't something you can do today. There's no date for that to change. See Marketplace Policies for what's live now.

A VeloCMS theme is a set of React layout components plus a theme.manifest.json. The platform renders a theme's BlogLayout and PostLayout inside the tenant's own navigation chrome, so a theme controls the content area — header, footer, post body typography, sidebar presence, everything below the platform nav. Every theme on VeloCMS today is built by the VeloCMS team and ships with the platform. This walkthrough shows how one is structured, using the public scaffolder; the result runs on your machine only.

Step 1 — Scaffold

npx create-velocms-theme my-theme
cd my-theme
npm install

The scaffolder asks for a name, slug, type (layout / niche / pack), and an optional niche category (photographer, restaurant, etc.). It creates src/BlogIndex.tsx, src/PostLayout.tsx, a theme.manifest.json, and a Tailwind preset override file.

Step 2 — Define your color tokens

Open src/styles.css and set your custom properties. VeloCMS theme tokens use OKLCH for perceptual uniformity — pick a primary hue and derive variants from it rather than hard-coding hex values.

:root {
  --theme-font-heading: "Playfair Display", Georgia, serif;
  --theme-font-body: "Inter", system-ui, sans-serif;
  --theme-color-bg: oklch(99% 0.002 100);
  --theme-color-text: oklch(15% 0.01 100);
  --theme-color-accent: oklch(55% 0.22 30); /* warm red */
  --theme-color-muted: oklch(95% 0.005 100);
}

Step 3 — Build the BlogIndex component

Your BlogIndex receives posts, settings, and tenant as props. VeloCMS injects them at render time. Keep the markup semantic — one <h1> for the blog name, <article> per post preview, <time datetime="..."> on dates. Treat LCP < 1000ms as a hard constraint, not a suggestion — it is the budget the platform measures against.

Step 4 — Build the PostLayout component

PostLayout receives a post object and a pre-rendered content HTML string. Style the prose area — font size, line height, heading levels, blockquotes, code blocks. Avoid !important overrides; the platform injects a CSS reset that your styles should layer on top of gracefully.

Step 5 — Update the manifest

Check that theme.manifest.json matches your package.json name and exports the correct component paths. The exports.components map tells VeloCMS which file to load for each slot.

{
  "$schema": "https://velocms.org/schemas/theme-v1.json",
  "name": "velocms-theme-my-theme",
  "displayName": "My Theme",
  "version": "1.0.0",
  "type": "layout",
  "exports": {
    "components": {
      "BlogLayout": "./dist/BlogIndex.js",
      "PostLayout": "./dist/PostLayout.js"
    }
  },
  "pricing": { "model": "free" }
}

Step 6 — Build

npm run build
velocms build   # validates the manifest, produces velocms-theme-my-theme-1.0.0.tgz

The scaffold reserves a preview/ folder for a thumbnail.png (400×300) and at least one full-page screenshot such as screenshot-1.png. A theme without preview images is hard for anyone to evaluate at a glance.

What makes a great theme

Lighthouse score above 95, WCAG AA contrast on all text, semantic HTML, and something that looks different from the built-in presets. A photography portfolio or a restaurant menu with real visual identity teaches you more about the token system than another minimal-dark clone. Niche beats generic.

What happens to a finished theme

Nothing yet. VeloCMS is a hosted platform, third-party theme development isn't open, and there is no upload, review, or sales flow for outside themes. If you have a theme direction you think VeloCMS should ship, email [email protected], and see Marketplace Policies for how third-party code fits into the platform.