Theming A Published Site
Theming a published site
A theme decides how a published site looks (see Publishing Your Notes As A Website). It is data,
never code that runs in EtherPK: a handful of templates, a stylesheet, a small search script,
and a theme.json describing them. The two themes that come with EtherPK are the reference;
copy one into your graph with Customise theme… and change it, or publish your own on the
web and point a publication at its theme.json by url.
Preview in the Theme editor renders your unsaved edits over a publication that uses the theme (or sample content) into a frame beside the files. Its scripts run there, sandboxed away from EtherPK, so search and the sidebar behave as they will on the site; a click on a link follows to that page, and the page picker frames any page directly. Update preview renders again, as does every save while the pane is open. Open in new tab is the same served page on its own, for a full-size look or the browser's phone emulation.
Files
theme.json
layouts/
page.html a document
home.html the front page (the home document, or the generated index)
journal.html a journal entry (falls back to page.html)
archive.html the journal archive (falls back to page.html)
404.html the "not published" page (falls back to page.html)
partials/
head.html logo.html header.html footer.html before-content.html after-content.html scripts.html
… and any partial the layouts include
assets/
theme.css search.js copied to the site under theme/
Every file is text. Templates are Mustache:
{{name}} inserts a value HTML-escaped, {{{name}}} inserts HTML as it is, {{#list}}…{{/list}}
repeats over a list or renders when a value is true, {{^list}}…{{/list}} renders when it is
empty or false, and {{> footer}} includes the partial partials/footer.html.
theme.json
{
"name": "my-theme",
"version": "1.0.0",
"contract": 1,
"description": "What it looks like, for the gallery.",
"kinds": ["docs"],
"includes": [
{ "name": "head", "description": "Extra markup inside <head>." },
{ "name": "footer", "description": "The site footer." },
{ "name": "styles", "description": "CSS appended after the stylesheet.", "kind": "css" }
],
"files": ["layouts/page.html", "partials/head.html", "partials/footer.html", "assets/theme.css"]
}
contractis the version of the view described below that the theme was written for. This EtherPK writes contract 1. A theme for a newer contract is refused with the reason; an older one still renders.includeslists the slots a user can fill from a page in their graph. Each is a partial name; the user's page replaces the partial of that name.kind: "css"marks the one slot that is appended to the stylesheet instead. The bundled themes declarehead,logo,header,before-content,after-content,footer,scriptsandstyles;logois the icon before the site title (partials/logo.html, the EtherPK icon as inline SVG).filesis needed when the theme is fetched from a url: every path the publisher should download, relative totheme.json.
The view
Every layout and partial is rendered over the same object.
site: title, url, hasUrl, kind (docs or blog), isDocs, isBlog, year,
generatedAt, hasJournals, hasPosts, hasMath, hasCustomCss, hasFeed, pages (a list
of page summaries, alphabetical, the home page excluded), journals (newest first), posts
(every dated document, newest first), search.url (search.json) and search.scriptUrl
(search-index.js, the same index as a script for a site opened from disk).
page: title (plain text; a scoped concept reads Physics Waves), titleHtml (the same
name as chained anchors, for the heading), slug, url, kind, isHome, isJournal,
isGeneratedIndex, isArchive, is404, date, hasDate, content (the rendered body),
toc (a list of { level, id, text }), hasToc, backlinks (a list of
{ title, titleHtml, url, kind, refs: [{ contextHtml }] }), hasBacklinks, excerpt,
hasMath, hasMermaid.
nav: the navigation tree for this page: a list of { label, labelHtml, href, external, concept, current, open, hasChildren, children }. Draw it with a recursive partial, as the
bundled nav-item.html does.
fragments: the same things pre-rendered, for a theme that would rather place them than
draw them: nav, toc, backlinks, index (the generated front-page list) and archive,
each a string of HTML with the classes below.
A page summary (in site.pages, site.journals, site.posts) is { title, titleHtml, url, kind, date, excerpt }.
Classes the content carries
The rendered body uses these, so a stylesheet can style them: a.wikilink and
a.wikilink-missing (a link to a page not on the site, pointing at 404.html); li.task,
li.task.done and span.task-tag with priority-1, priority-2, priority-3, doing,
waiting, cancelled, due-date, scheduled-date, completed-date; mark; figure.diagram
for a pre-rendered Mermaid diagram and pre.mermaid when one could not be drawn; span.math
and div.math-block for KaTeX output; pre > code.language-<name> with span.tok-* tokens
(tok-keyword, tok-string, tok-comment, tok-number, tok-typeName, tok-propertyName,
tok-tagName, tok-operator, and the rest of Lezer's class highlighter) for code.
What the site must include
theme/theme.cssandtheme/search.jsare the theme's own assets, copied fromassets/.- When
site.hasMath, linktheme/katex/katex.min.css; the publisher copies it and its fonts in. - When
site.hasCustomCss, linktheme/custom.css. - Search: the bundled
search.jsreadssearch.json({ documents: [{ url, title, kind, date, excerpt, text }] }) from a form with the classsite-search, falling back to a<script>ofsearch-index.jswhen the fetch is refused (a site opened from disk); keep that markup, with bothdata-search-indexanddata-search-script, or write your own script. - Small screens: the bundled docs theme turns its sidebar into a drawer. The same script runs a
button.nav-toggle(inshell-top.html, outside theheaderinclude so a replaced header keeps it), a.nav-backdrop, andbody.nav-openon the#site-nav; Escape and the backdrop close it, and focus moves in on open and back to the button on close. The stylesheet pins the button top right and slides the nav in under@media (max-width: 56rem). - Lists link a document by
title, nottitleHtml: a scoped concept's chained anchors cannot sit inside another anchor.titleHtmlis for the page heading.
Publishing a theme for others
Put the theme's folder on any static host that sends Access-Control-Allow-Origin headers (a
GitHub repository does, through raw.githubusercontent.com, as does GitHub Pages), list every
file in theme.json, and users add it under Settings → Publish → Add theme with the url of
theme.json, which copies it into the graph so the site keeps rendering if the url goes away.
A publication whose theme: is the url itself fetches the theme afresh on every publish.