Skip to content

Themes

A theme changes how the site looks, not what it says. The engine renders the same semantic HTML for every theme; a theme overrides it at three levels.

Level File Changes
1. Tokens tokens.css (required) colours, fonts, type scale, spacing
2. Components theme.css the look of a component, through its stable class
3. Templates templates/ the markup of a component, one file at a time

Choosing a theme

Set SITE_THEME to the theme's directory name. The engine ships one theme, base: black, white and greys on system fonts. An instance keeps its own themes in directories listed in SITE_THEME_DIRS (comma-separated); those are searched before the engine's. An instance theme named base replaces the engine's, so it must set every required token.

themes/<name>/
  theme.toml      name, version, engine version, font stylesheets
  tokens.css      level 1
  theme.css       level 2, optional
  templates/      level 3, optional; same paths as the engine's templates
  static/         fonts, textures, icons; reach them from theme.css by relative URL

theme.toml:

name = "Dusk"
version = "1.0.0"
engine = "0.1"        # the engine release the theme is built for
fonts = ["https://fonts.googleapis.com/css2?family=Gabarito&display=swap"]

Every page loads, in order: the font stylesheets, base/tokens.css, the theme's tokens.css, the engine's base.css, the theme's theme.css, then the content type colours from the admin. A theme's tokens.css therefore sets only the tokens it changes.

Tokens

The token list is the engine's public API. Renaming or removing a token is a breaking change.

Group Tokens
Surfaces --color-bg, --color-surface, --color-surface-2, --color-surface-band
Text --color-text, --color-text-strong, --color-text-muted
Lines --color-border, --color-border-soft
Links --color-link, --color-link-hover
Brand --color-brand, --color-brand-contrast (text on the brand colour)
Type fallback --color-type-default, --color-type-default-tint
Fonts --font-body, --font-display, --font-label
Type scale --text-base, --text-label, --text-h1, --text-h1-detail, --text-h2
Spacing --space-1 … --space-9, --max-width, --gutter
Shape --radius, --radius-lg, --line-strong

The engine reads tokens only from top-level :root rules. A dark scheme sets tokens again inside @media (prefers-color-scheme: dark) { :root { … } }.

--color-bg, --color-text, --color-brand, --color-brand-contrast, --color-type-default and --color-type-default-tint must be #rrggbb, so that their contrast can be checked. A tokens.css that does not parse, or sets one of these to anything else, stops the start.

Content type colours

Content type colours are not tokens: each type's colour and tint are set in the admin and emitted as [data-type="<key>"] { --type-color; --type-tint }. The tint mixes 12% of the colour into --color-bg, so it follows the dark scheme; a tint set in the admin replaces it in the light scheme only. The admin checks each colour against the light background. When the theme sets dark tokens, the engine lightens each colour that would not read on the dark --color-bg or its dark tint, and sets that as --type-color in the dark scheme. The type's admin page shows the derived colour. Components read only --type-color and --type-tint, so a door, a card, a menu item and a what's-new line all take the colour of their type.

Component classes

Stable, BEM-style. Target these from theme.css.

Component Classes
Page frame .site-header, .site-logo, .site-logo__mark, .site-logo__name, .site-logo__handle, .site-nav, .site-footer, .site-footer__inner, .site-footer__credit, .footer-links
Layout .container, .page, .page__title, .page__lead, .section-title, .label, .empty, .notice
Buttons and forms .btn, .btn--primary, .btn--type, .input, .form, .form__field, .form__errors, .newsletter, .newsletter__title, .newsletter__row
Home .hero, .hero__name, .hero__name--quiet, .hero__stats, .hero__lead, .hero__portrait, .hero__aside, .hero__actions, .home-doors, .doors, .door, .door__label, .door__title, .door__meta, .door__image, .door__more, .home-bottom, .home-bottom__aside, .next-up__label, .next-up__title, .follow, .follow__title
Lists .cards, .card, .card__image, .card__title, .card__summary, .card__meta, .type-label, .stats, .filters, .pagination, .gallery, .gallery__item, .feed, .feed__item, .feed__type, .feed__title, .feed__meta, .feed__change, .chip, .timeline
Detail pages .breadcrumb, .detail-hero, .detail-hero__title, .detail-hero__summary, .detail-hero__cover, .detail-layout, .detail-layout__main, .detail-layout__aside, .panel, .panel--typed, .panel__title, .panel__label, .related-item, .author-box
Parts .part-text, .part-figure, .part-audio, .video-facade, .video-facade__play, .link-card, .link-card__icon
Frames .framed, .framed__inner; set --frame-color to recolour

Overriding a template

Copy the engine's template to the same path under the theme's templates/, for example templates/lapidarium/components/door.html. Components are small files so that an override replaces one piece, not a page.

Checks

Every command that runs Django's system checks (check, migrate, runserver, and so the container start) checks the active theme:

Id Level When
lapidarium.E001 error a required token is defined by neither the base nor the theme
lapidarium.E002 error text, brand or type fallback colour has contrast below 4.5:1 against its background, in either scheme; or the dark --color-bg is darker than the light one, yet too light for white type colours to reach 4.5:1 on it or its tint
lapidarium.W001 warning theme.toml declares no engine version
lapidarium.W002 warning the theme is built for another engine release
lapidarium.W003 warning the theme overrides a template the engine no longer has

Content type colours are checked when a type is saved in the admin, against the active theme's --color-bg.

Preview

Staff see every component on the site's own content at <ADMIN_URL>theme-preview/, linked from the admin sidebar, together with the check results.