Skip to main content

Theme System

Kosh uses a detachable theme system that separates content from presentation. Themes are standalone repositories that can be installed, customized, and shared independently.

Theme Architecture

A Kosh theme is a directory containing templates and static assets:
themes/
└── <theme-name>/
    ├── templates/       # HTML templates (required)
    │   ├── layout.html  # Base layout
    │   ├── index.html   # Home page
    │   ├── post.html    # Single post (optional)
    │   ├── tags.html    # Tag index (optional)
    │   ├── 404.html     # Error page (optional)
    │   └── graph.html   # Knowledge graph (optional)
    ├── static/          # Static assets (optional)
    │   ├── css/
    │   ├── js/
    │   └── images/
    └── theme.yaml       # Theme metadata (optional)

Official Themes

Blog Theme

Repository: kosh-theme-blog Features:
  • Clean, minimal design
  • Reading time estimation
  • Pinned posts support
  • Tag system with index pages
  • Knowledge graph visualization
  • WASM-powered search
  • Dark/light mode toggle
  • Mobile-responsive
Installation:
git clone https://github.com/Kush-Singh-26/kosh-theme-blog themes/blog
Configuration:
kosh.yaml
theme: "blog"
themeDir: "themes"

Docs Theme

Repository: kosh-theme-docs Features:
  • Versioned documentation support
  • Sidebar navigation with hierarchy
  • Breadcrumb navigation
  • Previous/Next page links
  • Table of contents
  • Version selector dropdown
  • Outdated version warnings
  • Search scoped to current version
Installation:
git clone https://github.com/Kush-Singh-26/kosh-theme-docs themes/docs
Configuration:
kosh.yaml
theme: "docs"
themeDir: "themes"
versions:
  - name: "v4.0 (latest)"
    path: "v4.0"
    isLatest: true
  - name: "v3.0"
    path: "v3.0"
    isLatest: false

Installing Themes

From GitHub

# Clone into themes directory
git clone https://github.com/username/kosh-theme-name themes/theme-name

# Configure in kosh.yaml
theme: "theme-name"

Local Development

# Create theme structure
mkdir -p themes/my-theme/templates themes/my-theme/static

# Configure in kosh.yaml
theme: "my-theme"

Creating Custom Themes

Minimal Theme

The smallest viable theme requires two templates: 1. Base Layout (themes/my-theme/templates/layout.html)
<!DOCTYPE html>
<html lang="{{ .Config.Language }}">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>{{ .TabTitle }} | {{ .Config.Title }}</title>
  <meta name="description" content="{{ .Description }}">
  <link rel="stylesheet" href="{{ index .Assets "/static/css/style.css" }}">
</head>
<body>
  <header>
    <h1>{{ .Config.Title }}</h1>
    <nav>
      {{ range .Config.Menu }}
        <a href="{{ .URL }}">{{ .Name }}</a>
      {{ end }}
    </nav>
  </header>
  
  <main>
    {{ template "content" . }}
  </main>
  
  <footer>
    <p>&copy; 2026 {{ .Config.Author.Name }}</p>
  </footer>
</body>
</html>
2. Home Page (themes/my-theme/templates/index.html)
{{ define "content" }}
<div class="posts">
  {{ range .Posts }}
    <article>
      <h2><a href="{{ .Link }}">{{ .Title }}</a></h2>
      <p>{{ .Description }}</p>
      <time>{{ .DateObj.Format "January 2, 2006" }}</time>
    </article>
  {{ end }}
</div>
{{ end }}

Theme Metadata

themes/my-theme/theme.yaml
name: "My Theme"
author: "Your Name"
version: "1.0.0"
supportsVersioning: false  # Set true for docs themes
description: "A minimal Kosh theme"
homepage: "https://github.com/username/kosh-theme-name"
license: "MIT"
name
string
required
Theme display name.
supportsVersioning
boolean
default:false
Enable version selector and versioned navigation. Required for docs themes.
author
string
Theme author name or organization.
version
string
Theme version (semver recommended).
description
string
Brief theme description.
homepage
string
Theme repository or documentation URL.
license
string
License identifier (e.g., MIT, Apache-2.0).

Template Reference

Required Templates

layout.html
required
Base template with {{ template "content" . }} block.Purpose: Defines site-wide structure (header, footer, meta tags).
index.html
required
Home page template.Purpose: Renders post list with pagination.

Optional Templates

post.html
Single post template. Falls back to generic rendering if omitted.Available Data:
  • .Content - Rendered HTML
  • .Title, .Description, .DateObj
  • .TOC - Table of contents
  • .ReadingTime - Estimated minutes
  • .Tags - Post tags
tags.html
Tag index page template.Available Data:
  • .AllTags - Array of TagData with .Name, .Link, .Count
404.html
Error page template.Available Data:
  • .Config - Site configuration
  • .Assets - Asset map for CSS/JS
graph.html
Knowledge graph visualization template.Required: Include D3.js or similar for rendering graph data from /graph.json.

Template Data

All templates receive a PageData struct with these fields:
type PageData struct {
    Title        string           // Page title
    TabTitle     string           // Browser tab title
    Description  string           // Meta description
    BaseURL      string           // Site base URL
    Content      template.HTML    // Rendered markdown
    Meta         map[string]any   // Frontmatter data
    IsIndex      bool            // True for home page
    IsTagsIndex  bool            // True for tag index
    Posts        []PostMetadata   // Post list
    PinnedPosts  []PostMetadata   // Pinned posts
    AllTags      []TagData        // All tags with counts
    BuildVersion int64            // Build timestamp
    Permalink    string           // Canonical URL
    Image        string           // Social card image
    TOC          []TOCEntry       // Table of contents
    SiteTree     []*TreeNode      // Sidebar hierarchy (docs)
    Paginator    Paginator        // Pagination state
    Assets       map[string]string // Hashed asset paths
    Weight       int              // Post weight
    ReadingTime  int              // Estimated minutes
    
    // Navigation (docs theme)
    Breadcrumbs []Breadcrumb
    PrevPage    *NavPage
    NextPage    *NavPage
    
    // Versioning (docs theme)
    CurrentVersion string
    Versions       []VersionInfo
    IsOutdated     bool
    
    Config interface{}  // Access Config fields
}

Asset Management

Assets are processed with content hashing for cache busting:
<!-- Before processing -->
<link rel="stylesheet" href="/static/css/style.css">

<!-- After processing (with hash) -->
<link rel="stylesheet" href="{{ index .Assets "/static/css/style.css" }}">
<!-- Renders as: /static/css/style.a1b2c3d4.css -->
Supported Assets:
  • CSS files (minified)
  • JavaScript files (minified via esbuild)
  • Images (converted to WebP if compressImages: true)

Customizing Themes

Override Theme Files

Fork the theme and modify directly:
cd themes/blog
git checkout -b custom-modifications

# Edit templates/layout.html
# Edit static/css/style.css

git commit -am "Custom branding"

Extend with Custom CSS

Add custom styles without forking:
kosh.yaml
staticDir: "static"
static/css/custom.css
/* Override theme variables */
:root {
  --primary-color: #667eea;
  --background-color: #1a1a2e;
}
Include in templates:
<link rel="stylesheet" href="{{ index .Assets "/static/css/custom.css" }}">

Theme Configuration

Some themes expose configuration via kosh.yaml:
# Example: Docs theme with custom sidebar
theme: "docs"
themeDir: "themes"
menu:
  - name: "Docs"
    url: "/getting-started.html"
  - name: "API"
    url: "/api/index.html"
  - name: "GitHub"
    url: "https://github.com/username/repo"
    target: "_blank"

Theme Development Best Practices

Responsive Design

Use mobile-first CSS:
/* Mobile styles */
.container {
  padding: 1rem;
}

/* Tablet and up */
@media (min-width: 768px) {
  .container {
    padding: 2rem;
    max-width: 1200px;
    margin: 0 auto;
  }
}

Dark Mode Support

Use CSS custom properties:
:root {
  --bg-color: #ffffff;
  --text-color: #1a1a1a;
}

[data-theme="dark"] {
  --bg-color: #1a1a1a;
  --text-color: #ffffff;
}

body {
  background-color: var(--bg-color);
  color: var(--text-color);
}
JavaScript toggle:
const toggle = document.getElementById('theme-toggle');
toggle.addEventListener('click', () => {
  const current = document.documentElement.dataset.theme;
  document.documentElement.dataset.theme = current === 'dark' ? 'light' : 'dark';
  localStorage.setItem('theme', document.documentElement.dataset.theme);
});

Accessibility

  • Use semantic HTML (<nav>, <article>, <aside>)
  • Provide alt text for images
  • Ensure sufficient color contrast (WCAG AA: 4.5:1)
  • Support keyboard navigation
  • Use ARIA labels for icon buttons
<button aria-label="Toggle dark mode" id="theme-toggle">
  <svg aria-hidden="true">...</svg>
</button>

Performance

  • Minimize CSS (Kosh does this automatically)
  • Use system fonts or subset web fonts
  • Lazy load images: <img loading="lazy">
  • Defer non-critical JavaScript
<script src="{{ index .Assets "/static/js/app.js" }}" defer></script>

Versioning (Docs Theme)

The docs theme supports versioned documentation:

Version Selector

templates/layout.html
{{ if .Versions }}
<select id="version-selector">
  {{ range .Versions }}
    <option value="{{ .URL }}" {{ if .IsCurrent }}selected{{ end }}>
      {{ .Name }}{{ if .IsLatest }} (Latest){{ end }}
    </option>
  {{ end }}
</select>
{{ end }}

Outdated Banner

templates/post.html
{{ if .IsOutdated }}
<div class="outdated-warning">
  ⚠️ This is an old version.
  <a href="{{ .Permalink }}">View latest</a>
</div>
{{ end }}

Version URL Preservation

The version selector preserves the current page path:
const selector = document.getElementById('version-selector');
selector.addEventListener('change', (e) => {
  window.location.href = e.target.value;
});
Backend logic (handled by Kosh):
  • /getting-started.html → switch to v3.0 → /v3.0/getting-started.html
  • If target page doesn’t exist, falls back to version index

Template Functions

Kosh provides Go template functions:

Built-in Functions

<!-- String manipulation -->
{{ .Title | lower }}
{{ .Title | upper }}
{{ .Title | title }}

<!-- Conditionals -->
{{ if eq .Weight 10 }}
{{ if ne .Version "" }}
{{ if gt .ReadingTime 5 }}

<!-- Range -->
{{ range .Posts }}
  <li>{{ .Title }}</li>
{{ end }}

<!-- Index (map/array access) -->
{{ index .Assets "/static/css/style.css" }}
{{ index .Meta "custom_field" }}

Date Formatting

{{ .DateObj.Format "January 2, 2006" }}
{{ .DateObj.Format "2006-01-02" }}
{{ .DateObj.Format "Jan 2, 2006 at 3:04pm" }}

Sharing Themes

Publishing to GitHub

  1. Create repository: kosh-theme-<name>
  2. Add README with installation instructions
  3. Include theme.yaml with metadata
  4. Tag releases: git tag v1.0.0

Example README

# Kosh Theme: My Theme

A minimal, fast theme for Kosh static sites.

## Installation

```bash
git clone https://github.com/username/kosh-theme-mytheme themes/mytheme

Configuration

theme: "mytheme"

Features

  • Dark mode toggle
  • Reading time estimation
  • Tag system
  • Mobile responsive

License

MIT

<Accordion title="Theme Development Checklist">

- [ ] `layout.html` with `{{ template "content" . }}` block
- [ ] `index.html` for home page
- [ ] `theme.yaml` with metadata
- [ ] Responsive CSS (mobile-first)
- [ ] Dark mode support (optional)
- [ ] Asset hashing via `{{ index .Assets ... }}`
- [ ] Accessible markup (semantic HTML, ARIA)
- [ ] Minified CSS/JS (handled by Kosh)
- [ ] README with installation instructions
- [ ] License file (MIT recommended)
- [ ] Version tag (e.g., `v1.0.0`)

</Accordion>

## Troubleshooting

### Theme Not Found

❌ Theme not found: themes/mytheme/

**Solution:** Verify theme directory exists:
```bash
ls -la themes/mytheme

Missing Templates Directory

❌ templates/ directory required in theme
Solution: Create templates directory:
mkdir -p themes/mytheme/templates

Asset Not Found

❌ Asset not found: /static/css/style.css
Solution: Check asset path in staticDir:
staticDir: "themes/mytheme/static"

Template Syntax Error

❌ template: layout.html:10: unexpected "}" in operand
Solution: Check Go template syntax:
<!-- Wrong -->
{{ .Title }}

<!-- Correct -->
{{ .Title }}

Build docs developers (and LLMs) love