# Creating Themes

Build custom themes for your bok sites.

# Theme Structure

my-theme/
├── config.ts       # Required: theme configuration
├── layout/         # Required: template files
│   └── index.ts    # Required: default layout
├── assets/         # Optional: static files
└── content/        # Optional: example content

# Theme Config

Create config.ts:

export default {
  name: "my-theme",

  // File discovery mode
  files: {
    type: "walk",  // or "toc", "glob"
  },

  // Path defaults (relative to this file)
  paths: {
    assets: "assets",
    layout: "layout",
    defaultLayout: "index.ts",
  },

  // Dev server defaults
  serve: {
    reload: true,
    port: 5000,
    wsPort: 5001,
  },

  // Lifecycle hooks
  hooks: {
    async beforeSite(site, pages, opts) {},
    async afterSite(site, pages, opts) {},
    async beforePage(site, page, i, pages, opts) {},
    async afterPage(site, page, i, pages, opts) {},
  },

  // Default params (users can override)
  params: {
    title: "My Theme",
  },
};

# Layout Templates

Layouts are TypeScript files that export a function:

// layout/index.ts
export default function(site, page, pages) {
  return `
    <!DOCTYPE html>
    <html>
      <head>
        <title>${page.params.title || site.params.title}</title>
        <link rel="stylesheet" href="${site.rootUrl}/assets/css/style.css">
      </head>
      <body>
        <main>
          ${page.htmlContent}
        </main>
      </body>
    </html>
  `;
}

# Template Parameters

# site

The site configuration object:

site.rootUrl      // URL prefix
site.params       // Site params from config
site.paths        // Directory paths
site.uglyURLs     // URL style boolean

# page

The current page being rendered:

page.name         // Filename
page.path         // Full file path
page.link         // URL path (e.g., "/getting-started")
page.params       // Front matter data
page.htmlContent  // Rendered markdown HTML
page.tokens       // markdown-it AST tokens
page.toc          // Left sidebar HTML (if using TOC mode)
page.rightToc     // Right-side TOC HTML
page.prevPage     // Previous page URL
page.nextPage     // Next page URL

# pages

Array of all pages in the site.

# Composable Templates

Split templates into reusable parts:

// layout/base.ts
export default function({ title, rootUrl }, content) {
  return `
    <!DOCTYPE html>
    <html>
      <head>
        <title>${title}</title>
        <link rel="stylesheet" href="${rootUrl}/assets/css/style.css">
      </head>
      <body>
        ${content}
      </body>
    </html>
  `;
}

// layout/index.ts
import base from "./base.ts";

export default function(site, page, pages) {
  return base(
    { title: page.params.title, rootUrl: site.rootUrl },
    `<main>${page.htmlContent}</main>`
  );
}

# Assets

Place static files in assets/:

assets/
├── css/
│   └── style.css
├── js/
│   └── main.js
├── img/
│   └── logo.svg
└── fonts/
    └── ...

Reference in templates:

`<link rel="stylesheet" href="${site.rootUrl}/assets/css/style.css">`
`<script src="${site.rootUrl}/assets/js/main.js"></script>`

# Hooks

Use hooks for advanced processing:

hooks: {
  // Generate sitemap after build
  async afterSite(site, pages, opts) {
    const sitemap = pages
      .map(p => `<url><loc>https://example.com${p.link}</loc></url>`)
      .join("\n");

    await Bun.write(
      `${site.paths.output}/sitemap.xml`,
      `<?xml version="1.0"?><urlset>${sitemap}</urlset>`
    );
  },

  // Add build timestamp to each page
  async beforePage(site, page, i, pages, opts) {
    page.params.buildTime = new Date().toISOString();
  },
}

# Using Your Theme

Reference by path:

export default {
  extends: "/path/to/my-theme/config.ts",
  params: {
    title: "My Site",
  },
};

Or initialize with it:

bok init --mode extend --theme path --theme-path /path/to/my-theme/config.ts