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:
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:
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>© 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 }}
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"
Enable version selector and versioned navigation. Required for docs themes.
Theme author name or organization.
Theme version (semver recommended).
Theme repository or documentation URL.
License identifier (e.g., MIT, Apache-2.0).
Template Reference
Required Templates
Base template with {{ template "content" . }} block.Purpose: Defines site-wide structure (header, footer, meta tags).
Home page template.Purpose: Renders post list with pagination.
Optional Templates
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
Tag index page template.Available Data:
.AllTags - Array of TagData with .Name, .Link, .Count
Error page template.Available Data:
.Config - Site configuration
.Assets - Asset map for CSS/JS
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:
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>
- 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
{{ 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
{{ 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" }}
{{ .DateObj.Format "January 2, 2006" }}
{{ .DateObj.Format "2006-01-02" }}
{{ .DateObj.Format "Jan 2, 2006 at 3:04pm" }}
Sharing Themes
Publishing to GitHub
- Create repository:
kosh-theme-<name>
- Add README with installation instructions
- Include
theme.yaml with metadata
- 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
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 }}