A theme is a folder with a description, some Python and some templates. Start from a copy of a bundled theme, or from the small example below.
Start from a bundled theme
-
Find where KB4IT keeps its themes:
bash kb4it -L DEBUG themes 2>&1 | grep THEME_SEARCH_GLOBAL -
Copy one into your own themes folder under a new name, replacing
<path>with the folder the first command printed:bash mkdir -p ~/.kb4it/opt/resources/themes cp -r <path>/techdoc ~/.kb4it/opt/resources/themes/mytheme -
In
mytheme/theme.json, set"id": "mytheme"and a newname. - Set
"theme": "mytheme"in therepo.jsonof a knowledge base and build it with--force.
kb4it themes now lists it with scope=local. Change the templates and CSS step by step and rebuild.
What a theme contains
mytheme/
├── theme.json # id, name, description, version, KB4IT it needs
├── logic/theme.py # class Theme: builds the pages
├── templates/*.tpl # Mako templates
├── framework/ # CSS, JavaScript, fonts, images
└── apps/default/ # optional sample knowledge base for kb4it create
A small working theme
This theme, plain, makes a home page that lists every document and wraps each document in a simple layout. Create these files in ~/.kb4it/opt/resources/themes/plain/.
theme.json:
{
"id": "plain",
"name": "Plain",
"author": "Ann Lopez",
"email": "ann@example.com",
"website": "",
"description": "One page per document and a list of them",
"version": "0.1.0",
"kb4it": ">=0.7.10",
"metadata_pages": false
}
"metadata_pages": false skips the pages per property and per value, so the theme needs no templates for them.
logic/theme.py:
"""Plain theme: a home page listing every document, and one page per document."""
import os
from kb4it.core.util import html_id_for
from kb4it.services.builder import Builder
class Theme(Builder):
def build(self):
"""Build the pages that do not come from a document: here, the home page."""
var = self.get_theme_var()
var["docs"] = [
{"url": html_id_for(doc), "title": self.srvdtb.get_values(doc, "Title")[0]}
for doc in self.srvdtb.get_documents()
if not self.srvdtb.is_system(doc)
]
self.distribute_md("index", self.template("PAGE_INDEX").render(var=var))
self.srvdtb.add_document("index.md")
self.srvdtb.add_document_key("index.md", "Title", var["repo"]["title"])
self.srvdtb.add_document_key("index.md", "SystemPage", "Yes")
def build_page(self, path_md):
"""Wrap each compiled page in the layout of HTML_BODY.tpl."""
path_html = html_id_for(path_md)
with open(path_html, encoding="utf-8") as fh:
body = fh.read()
var = self.get_theme_var()
var["body"] = body
var["title"] = self.srvdtb.get_values(os.path.basename(path_md), "Title")[0]
with open(path_html, "w", encoding="utf-8") as fh:
fh.write(self.template("HTML_BODY").render(var=var))
templates/HTML_BODY.tpl, the layout of every page:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>${var['title']} · ${var['repo']['title']}</title>
<link rel="stylesheet" href="resources/themes/plain/framework/plain.css">
</head>
<body>
<header><a href="index.html">${var['repo']['title']}</a></header>
<main>${var['body']}</main>
</body>
</html>
templates/PAGE_INDEX.tpl, the home page:
<h1>${var['repo']['title']}</h1>
<ul>
% for doc in var['docs']:
<li><a href="${doc['url']}">${doc['title']}</a></li>
% endfor
</ul>
framework/plain.css:
body { max-width: 46rem; margin: 2rem auto; font-family: sans-serif; line-height: 1.6; }
header a { font-weight: bold; text-decoration: none; }
KB4IT copies the theme folder to target/resources/themes/plain/; that is why the layout links the CSS from there.
How the pieces fit
| Part | Role |
|---|---|
build() |
Builds the pages that do not come from a document: home, lists, calendars |
build_page(path_md) |
Receives each compiled document and writes its final HTML |
build_page_key(key, values), build_page_key_value(kvpath) |
Build the pages per property and per value, unless metadata_pages is false |
post_activities(), post_deploy_activities() |
Run after compilation and after the copy to target/ |
site_signature() |
Returns a value that changes when every page must be rebuilt, for example when the navigation changes |
self.srvdtb |
The documents and their properties: get_documents(), get_values(doc, key) |
self.distribute_md(name, html) |
Adds a page the theme rendered |
self.template(name) |
Loads a template from templates/, or from KB4IT's shared ones |
Templates use Mako: ${var['repo']['title']} prints a value, and % for ... % endfor repeats a block. var['repo'] holds every key of repo.json.
HTML_BODY and PAGE_INDEX are always required, and PAGE_KEY and PAGE_KEY_VALUE too unless metadata_pages is false. A missing one stops the build with TEMPLATE_MISSING.
Options in theme.json
| Key | Meaning |
|---|---|
kb4it |
The KB4IT versions the theme works with, such as ">=0.7.10" or ">=0.7.10, <0.9" |
metadata_pages |
false skips the pages per property and per value |
deploy_dirs |
Only these folders of the theme are copied to the website, for example ["framework"] |
Add your own checks
A file logic/verify.py with a function verify(repo, docs) runs during kb4it verify. docs maps each file name to its properties. Return a list of problem messages, empty when all is well.
Share it
- Put the theme in
source/resources/themes/<name>/of a knowledge base to ship it with the documents. - Or point
"theme_path"inrepo.jsonat its folder.