Core concepts

The vault

Your vault is a plain folder of Markdown files. It’s the only content source karaoke-cms reads from. There’s no CMS database, no admin panel, and no upload step. You point the framework at the folder via KARAOKE_VAULT in .env.default, and it reads whatever’s there.

The vault also holds the framework’s navigation config file (_website/menus.yaml). This means your content, your structure, and your navigation config all live in the same place you write notes. Open it in Obsidian, a code editor, or both.

Private by default

No file on your site is public unless its frontmatter contains publish: true. Files without that field, or with publish: false, are loaded during development (so you can preview drafts) but are completely excluded from production builds.

---
title: "Draft idea"
publish: false
---

This is enforced at two levels: the Astro content filter excludes unpublished files from getStaticPaths, and a separate assert-privacy script scans the built dist/ folder after every build to verify no private content slipped through. If the check fails, the deploy stops.

Frontmatter as data

Everything meaningful about a note, beyond its body text, is expressed in frontmatter. The fields karaoke-cms uses are:

FieldRequiredPurpose
titleYesPage title and <h1>
publishYes (for public pages)Makes the page visible in production
descriptionNoSubtitle, meta description, OG tag
dateNoPublication date, used for sorting
authorNoAuthor name
tagsNoArray of tag strings for the tag index

There’s no database schema to configure. The fields are just YAML. An enrichment pipeline (karaoke-enrich) can add description, tags, reading_time, and related automatically via AI, but using it is optional.

Modules

Modules are optional features you turn on in karaoke.config.ts. Each module injects its own routes and content collections. Removing a module from the list removes everything it provided from the build.

modules: [
  blog({ mount: '/blog' }),
  docs({ mount: '/docs' }),
  seo(),
  tags(),
  search(),
],

Available modules:

ModuleWhat it adds
blog()Blog post list and individual post pages
docs()Documentation pages with sidebar navigation
seo()OG images, JSON-LD, and /robots.txt
tags()Tag index at /tags with per-tag RSS feeds
search()Full-text search at /search powered by Pagefind
comments()Giscus (GitHub Discussions) widget on posts and docs

You can have multiple docs sections by passing an array to docs():

docs([
  { id: 'docs', mount: '/docs' },
  { id: 'api-docs', mount: '/api-docs', folder: 'api-reference', label: 'API Reference' },
]),

Each section reads from its own vault subfolder and gets its own nav and routes.

Theme

The theme controls the visual layout of your site. It decides where the header, sidebar, and content area sit, what fonts and colors are used, and how pages are structured. karaoke-cms ships with themeDefault, which provides a two-column layout that adapts based on which modules are active.

theme: themeDefault(),

Modules and themes are intentionally separate. A module declares what CSS it needs through a typed contract. The theme implements those contracts. This means you can swap themes without changing your modules, and you can write a module without knowing which theme will render it.

You won’t need to touch the theme unless you’re building a custom one. For visual customization within the default theme, CSS variables in src/styles/global.css control all design tokens.