Skip to main content

Build your own theme

Dev workflow

How kotao theme dev runs your theme locally — and what to do when it doesn't.

kotao theme dev spawns Astro dev against the storefront runtime, but with your theme swapped in as the active one. Hot reload on save, ctrl-c to stop.

What it actually does

When you run kotao theme dev inside a theme project, the CLI:

  1. Validates the project (theme.json + kotao.theme.toml present, slugs match, semver valid).
  2. Finds the runtimeapps/web/sites-theme-runtime/ somewhere up the directory tree, or --runtime=<path> if you pass it explicitly.
  3. Swaps the runtime’s active theme — rewrites <runtime>/src/active-theme.ts to import your theme by absolute path.
  4. Spawns astro dev from inside the runtime, stdio inherited.
  5. Restores the runtime’s active-theme.ts on ctrl-c or process exit so the runtime checkout isn’t left dirty.

Step 5 is load-bearing: a leaked dirty active-theme.ts would cause the runtime’s CI to either commit your local path (which doesn’t resolve on the build cluster) or fail typecheck. The CLI registers SIGINT and SIGTERM handlers that run the restore even when astro is killed.

Flags

FlagDefaultNotes
--runtime=<path>auto-detectWhere sites-theme-runtime/ lives. Required when not in a monorepo checkout.
--port=<n>4321Astro dev port.
--host=<addr>127.0.0.1Loopback by default; --host=0.0.0.0 to expose on the LAN.

KOTAO_THEME_RUNTIME=<path> is an alternative to --runtime if you’d rather not pass it every invocation.

Hot reload

Astro’s dev server watches your project files and rebuilds on save. The reload covers:

  • .astro files (sections, layouts) → instant
  • .css files → instant
  • .ts / .js files (helpers, the theme entry) → fast HMR
  • theme.json → requires a manual page refresh

A clean reload means your renderer logged no errors and the section schemas accepted the rendered data. A red overlay in the browser means one of those failed; the message tells you which.

When theme dev errors out

”Couldn’t find a sites-theme-runtime checkout”

You’re outside the surreal-kotao monorepo. Phase 4C ships the in-repo path; pass --runtime=<path> to a checkout you’ve cloned. Phase 4D will package a runtime as @kotao/sites-theme-runtime on npm so this isn’t required.

”Couldn’t find <theme>/src/theme.ts

Every theme must export its Theme object as the default from src/theme.ts. The scaffold (kotao theme init) writes this for you; if you started from someone else’s theme that omitted it, add one matching the SDK README’s shape.

Astro renders a section as [object Object] or empty

Your renderer received data that doesn’t match the expected shape. Two common causes:

  • The data prop typing isn’t picking up the right type. Make sure Props extends SectionRendererProps<"<section-type>"> matches the section type exactly.
  • You’re indexing into data.someField that’s optional in the schema and undefined for this section instance. Add an {x && <…>} guard.

Dirty active-theme.ts after a crash

If theme dev was force-killed (kill -9, OS reboot mid-dev), the cleanup handlers didn’t run. Inside the runtime checkout:

git checkout src/active-theme.ts

The committed version always imports the linen first-party theme — restoring to that puts the runtime back into its default state.

kotao theme dev doesn’t render real workspace content

Today, theme dev renders the scaffolded fixture data — useful for iterating on visuals but not for verifying behavior against your actual content. The plan covers a --with-workspace=<slug> flag that mints a preview token and feeds real RenderDocuments into the runtime; that’s deferred to a follow-up.

In the meantime, kotao theme push is the way to verify against real content: bump the version, push, and apply the new theme to a staging site.