Your First Theme

Themes in VeloCMS are npm packages that export React layout components and a theme.manifest.json. VeloCMS loads your BlogLayout and PostLayoutexports and wraps them with the tenant's own navigation chrome. You control the content area — header, footer, post body typography, sidebar presence, everything below the platform nav.

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. The platform monitors Core Web Vitals and themes that break LCP < 1000ms get flagged in the marketplace.

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 and submit

npm run build
velocms build   # produces velocms-theme-my-theme-1.0.0.tgz
velocms publish # submits as draft

Theme review checks visual correctness in a sandbox tenant across three screen sizes (mobile 375px, tablet 768px, desktop 1440px). Include a preview/thumbnail.png (400×300) and at least one full-page screenshot in preview/screenshot-1.png. Submissions without screenshots are soft-rejected automatically.

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. The marketplace is already well-stocked with minimal-dark and editorial-serif options — a photography portfolio theme or a restaurant menu theme with strong visual identity has a much better conversion rate. Niche beats generic.