Skip to content
Wiki— browse docs
On this page

Step 4: Pick a theme

One shared catalog of looks, your template's own signature, and a swatch you can repaint by hand.

You've built a wiki you think looks great. The person installing it wants parchment and a serif. The next one wants a black terminal. You could ship your own theme picker — but every template that did would have a different list, and switching templates would lose the choice. So vvd ships one catalog, in the SDK, that every wiki template offers:

import { WIKI_THEME_PARAM } from "@vvd/sdk"

A theme in it is a palette plus two hints — nothing more:

{ id: "parchment", name: "Parchment", values: {
  background: "#f4eedd", primary: "#8a5a2b", secondary: "#fffdf4",
  foreground: "#2c2418", line: "rgba(60,45,20,0.2)", font: "serif",
} }

Because it's only a palette, any theme works on any template — your template contributes layout, the preset contributes colour. WIKI_THEME_PARAM.presets is real data, so the swatch row below is pulled straight from the actual catalog, not a mock-up of one.

Edit the tool below. Change a hex value in TEMPLATE_DEFAULT and watch the big card repaint:

Your template's own signature
src/app.tsx Lines 4 to 11 highlighted.

Edit and the example re-runs. Tab indents; press Escape to leave the editor.

Running · your edits, live
Starting the example…

You should see: the big card repaint the instant you change bg, ink or accent — and the row of small circles is the real catalog: Midnight, Verdant, Paper and the rest, each one pulled from WIKI_THEME_PARAM.presets exactly as it ships.

Note:

Preset ids are forever

Presets get added to the catalog, never renamed or removed — a published site from a year ago still stores its choice by id, and it has to keep resolving.

Reading the owner's actual pick

TEMPLATE_DEFAULT above is what renders when nobody's chosen anything. In the real wiki, an owner's pick comes from useAppCustomization() — live only, so it's off-limits in this sandbox (Step 2's rule) — resolved with a pure function you can also run on the published side:

import { resolveCustomizationChoice, useAppCustomization } from "@vvd/sdk"

const { values } = useAppCustomization()
const themeId = resolveCustomizationChoice(WIKI_THEME_PARAM, values)
const preset = WIKI_THEME_PARAM.presets.find((p) => p.id === themeId)

Two ids mean "ask something else" rather than "here's a palette": "default" means your template's own look (what you just edited above), and "world" means the reader's own world theme — derived live from useHostTheme(), and worth knowing it goes quiet on a published page (a sandboxed third-party wiki doesn't get the theme capability). Reference covers both, plus loading a picked Google font.

Where the picker is declared

You never build the theme UI, but you do declare it — and the scaffold already ships the declaration, one line on defineApp in src/app.tsx:

src/app.tsx
const THEME_PARAM = WIKI_THEME_PARAM

export default defineApp({
  // …
  // The STANDARD Customize schema (mirrored into vvd.json by `vvd save`, which
  // is what the catalog reads): declaring it is what gives this wiki the
  // platform's Customize pill on every surface it mounts.
  customization: [THEME_PARAM, WIKI_HEADING_FONT_PARAM, WIKI_BODY_FONT_PARAM],
})

That array is the whole wiring: the platform's Customize panel reads it and renders the swatch picker (and the two font pickers) itself, then hands the owner's choices back to you as the values you read above.

Recap

  • WIKI_THEME_PARAM is one shared catalog every wiki template offers — a theme is a palette plus two hints, so any theme works on any template.
  • "default" means your template's own signature (colours and fonts); "world" means the reader's own world theme.
  • Your manifest declares the picker as data; the platform renders it. Your app ships no theme UI of its own.

Next steps