Help us shape the future of Go! Take the 2026 Go Developer Survey

theme

package
v0.17.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 23, 2026 License: AGPL-3.0 Imports: 48 Imported by: 0

Documentation

Overview

Package theme loads themes, resolves the template hierarchy and renders the public site.

contract.go holds the frozen wave-0 contract (types and interfaces only). The implementation arrives in wave 1.

Index

Constants

View Source
const (
	KindSingle   = "single"
	KindPage     = "page"
	KindHome     = "home"
	KindArchive  = "archive"
	KindTaxonomy = "taxonomy"
	KindSearch   = "search"
	Kind404      = "404"
	Kind500      = "500"
)

Built-in request kinds (spec §4.3). Modules add more with kernel.ThemeRegistry.AddKind (e.g. commerce: shop, product).

View Source
const (
	// Marker500 is present in every theme-engine 500 response body.
	Marker500 = "<!-- gorun:500 -->"
	// PreviewParam enables draft overrides for users with themes.manage.
	PreviewParam = "gp_preview_override"
	// HookRenderContext filters *Context before every render.
	HookRenderContext = "theme.render.context"
	// PermManage guards the appearance screens and API.
	PermManage = "themes.manage"
)
View Source
const (
	SettingTheme   = "site.theme"
	SettingTitle   = "site.title"
	SettingTagline = "site.tagline"
	SettingLocale  = "site.locale"
	SettingTZ      = "site.timezone"
	SettingFront   = "site.front_page"
)

Setting keys read by the theme engine.

View Source
const EnvThemesDir = "GORUN_THEMES"

EnvThemesDir names the environment variable that points at the themes directory.

View Source
const EnvThemesExtra = "GORUN_THEMES_EXTRA"

EnvThemesExtra names the environment variable listing extra theme directories (separated like PATH), searched after the core directory. Brand themes live there, outside the core catalog, on the servers that use them.

View Source
const HookMenuResolveURL = "menus.resolve_url"

HookMenuResolveURL is the filter (on *menus.Item) that fills URL for menu items of kind content, term and archive.

View Source
const (

	// PermIdentity matches the site-design workspace: settings-only users
	// may shape the identity without themes.manage.
	PermIdentity = "settings.manage"
)
View Source
const VariantNative = "native"

VariantNative is the fallback value of every slot.

Variables

View Source
var (
	// ErrNotFound is returned for unknown themes and templates.
	ErrNotFound = errors.New("theme: not found")
	// ErrInvalidName is returned for template names outside the naming scheme.
	ErrInvalidName = errors.New("theme: invalid template name")
	// ErrInvalidTheme is returned by Activate for a broken theme.
	ErrInvalidTheme = errors.New("theme: invalid theme")
	// ErrInvalidTemplate wraps a template parse error on save.
	ErrInvalidTemplate = errors.New("theme: invalid template")
)
View Source
var ErrIdentityLogoConflict = errors.New("theme: identity changed since logo review")
View Source
var ErrIdentityLogoDisabled = errors.New("theme: logo module disabled before draft selection")
View Source
var ErrIdentityLogoMedia = errors.New("theme: logo must reference an existing PNG")
View Source
var ErrNoTemplate = errors.New("theme: no template matches")

ErrNoTemplate is returned when no candidate template exists in the theme.

View Source
var IdentityKeys = []string{idAccent, idBackground, idText, idHeading, idBody, idLatinFont, idScheme, idRadius, idSpacing, idCustomCSS}

IdentityKeys are the theme.<slug>.* keys the identity page owns, in form order. Every value is a string; "" means "keep the theme's design".

View Source
var SiteIdentityKeys = []string{"site.title", "site.tagline", "site.logo", "site.logo_light", "site.logo_dark", "site.icon"}

SiteIdentityKeys are the global settings edited alongside the theme identity: name, tagline and the logo variants (media ids).

Functions

func BuildQuery

func BuildQuery(args map[string]any, canView bool) (content.Query, error)

BuildQuery turns a template query map into a content.Query. Keys: type, status, search, parent, locale, page, per_page (1–100, default 10), order ("published_at desc, title asc", "-published_at" or []content.Order), taxonomy (map tax → slug or slugs, or "tax:a,b"), fields (map key → value for eq, or content.Filter / {"op","value"}). Without canView the status is always "published".

func BuilderIdentityStylesheet added in v0.3.0

func BuilderIdentityStylesheet(identity brand.Identity) string

BuilderIdentityStylesheet returns the builder brand CSS body, or "".

func ContrastRatio added in v0.16.0

func ContrastRatio(a, b string) string

ContrastRatio formats the WCAG contrast between two hex colours ("" when either is not a colour).

func DeclaredVariants added in v0.16.0

func DeclaredVariants(info Info, slot string) []string

DeclaredVariants lists the variants info declares for slot, "native" first, in the slot's canonical order; nil for an unknown slot.

func ExtraThemesDirs

func ExtraThemesDirs() []string

ExtraThemesDirs returns the directories listed in $GORUN_THEMES_EXTRA.

func FormatDate

func FormatDate(v any, locale, tz string, layout ...string) string

FormatDate formats v (RFC3339 string or time) for locale in timezone tz. Layouts: "long" (default: "10 أكتوبر 2026" / "October 10, 2026"), "datetime" (long + 15:04), "short" (2006-01-02), "iso" (RFC3339), or a Go layout. Digits stay Latin. Unparseable strings are returned as-is.

func IdentityFontLabel added in v0.16.0

func IdentityFontLabel(value string) string

IdentityFontLabel returns the display name of an identity font value from either font list, or the value itself when unknown ("" for "").

func IdentityStylesheet added in v0.3.0

func IdentityStylesheet(get func(key string) string) string

IdentityStylesheet returns the cached site-identity CSS body for settings (empty when nothing is set). Used by /site/identity.css.

func Money

func Money(amount any, currency, locale string) (string, error)

Money formats an amount in minor units: Money(123450, "USD", "en") = "$1,234.50"; for Arabic the code follows the number ("1,234.50 USD"). Digits stay Latin.

func ResolveVariant added in v0.16.0

func ResolveVariant(info Info, slot, stored string) string

ResolveVariant returns stored when info declares it for slot, else "native".

func SettingKey

func SettingKey(slug, key string) string

SettingKey is the settings key of theme setting key of theme slug.

func SupportsIdentity added in v0.3.0

func SupportsIdentity(info Info) bool

SupportsIdentity is the exported form of supportsIdentity for public handlers (e.g. /site/identity.css).

func ThemesDir

func ThemesDir(cfg config.Config) string

ThemesDir returns the themes directory: $GORUN_THEMES, else ./themes (relative to the working directory) when it exists, else /app/themes (the container layout), then <DataDir>/themes when present, else <DataDir>/../themes.

func ValidAssetPath added in v0.15.0

func ValidAssetPath(p string) bool

ValidAssetPath accepts a relative file path under a theme's assets/ directory (no traversal, no absolute path, no query).

func ValidName

func ValidName(name string) bool

ValidName reports whether name follows the template naming scheme.

func VariantSlots added in v0.16.0

func VariantSlots() []string

VariantSlots returns the slot names in a stable order.

Types

type Alternate

type Alternate struct {
	Locale, Label, URL string
}

Alternate is a page's URL in one locale; Label is the locale's own name.

type AppearanceCustomCSS added in v0.15.0

type AppearanceCustomCSS struct {
	Present bool   `json:"present"`
	Bytes   int    `json:"bytes"`
	Source  string `json:"source"`
	Status  string `json:"status" enum:"none,applied_requires_browser_evaluation,invalid_not_applied"`
}

AppearanceCustomCSS says whether site custom CSS is applied after the theme styles. Its effect cannot be reported per value without a browser.

type AppearanceDeclared added in v0.15.0

type AppearanceDeclared struct {
	Colors map[string]AppearanceValue `json:"colors"`
	Fonts  map[string]AppearanceValue `json:"fonts"`
}

AppearanceDeclared is the theme's declaration layer only.

type AppearanceOverride added in v0.15.0

type AppearanceOverride struct {
	Role     string  `json:"role" doc:"colors.<role> or fonts.<role>"`
	Source   string  `json:"source"`
	Value    string  `json:"value"`
	Declared *string `json:"declared" doc:"The theme's declared value it replaced; null when the theme declared none"`
}

AppearanceOverride records one effective value that a site setting supplied instead of the theme declaration.

type AppearanceProvider added in v0.14.0

type AppearanceProvider interface {
	Appearance(context.Context) AppearanceReport
}

AppearanceProvider is optional so existing Service implementations remain compatible. Appearance performs no writes or external requests.

type AppearanceReport added in v0.14.0

type AppearanceReport struct {
	Theme AppearanceTheme `json:"theme"`
	// Colors and Fonts are the effective values after site customisation
	// (identity settings) was applied to the theme's declarations; each
	// value names its source. Declared keeps the theme's own declarations.
	Colors      map[string]AppearanceValue `json:"colors" doc:"Effective values after site identity settings; source says which setting or theme token supplied each"`
	Fonts       map[string]AppearanceValue `json:"fonts" doc:"Effective values after site identity settings"`
	Declared    *AppearanceDeclared        `json:"declared" doc:"The theme's own declarations before site customisation; null when the theme cannot be read"`
	Overrides   []AppearanceOverride       `json:"overrides" doc:"Site settings that changed a declared value, with provenance"`
	Scheme      AppearanceValue            `json:"scheme" doc:"Site colour scheme setting (light, dark) or the theme default"`
	Radius      AppearanceValue            `json:"radius" doc:"Site corner-radius setting or the theme default"`
	CustomCSS   AppearanceCustomCSS        `json:"custom_css"`
	ObservedAt  time.Time                  `json:"observed_at"`
	Limitations []string                   `json:"limitations"`
}

AppearanceReport describes static declarations and validated identity settings, not the full CSS cascade. ObservedAt is the last filesystem read; settings are read for each report. InvalidateCache or restarting refreshes cached files. Only one theme snapshot is retained per service.

type AppearanceTheme added in v0.14.0

type AppearanceTheme struct {
	Slug    AppearanceValue `json:"slug"`
	Name    AppearanceValue `json:"name"`
	Version AppearanceValue `json:"version"`
}

AppearanceTheme intentionally excludes paths, settings schemas and secrets.

type AppearanceValue added in v0.14.0

type AppearanceValue struct {
	Value     *string `json:"value"`
	Source    string  `json:"source"`
	Available bool    `json:"available"`
	Reason    string  `json:"reason,omitempty"`
}

AppearanceValue identifies a public declaration, or explains why no value can safely be reported. Available does not mean browser-computed style.

type BuilderPreviewOptions

type BuilderPreviewOptions struct {
	Identity                         brand.Identity // Zero means inherit the public theme without an identity override.
	Menu                             *menus.Menu
}

type CommentNode

type CommentNode struct {
	ID, AuthorName, Body, CreatedAt string
	Replies                         []CommentNode
}

CommentNode is one approved comment.

type CommentsData

type CommentsData struct {
	Open      bool   // the item takes new comments
	ContentID string // posted as content_id
	Action    string // the form's action URL
	Back      string // posted as _back: the item's path (with its locale prefix)
	Count     int
	Items     []CommentNode // approved top-level comments, oldest first, with their replies
}

CommentsData is what a comments component renders for an item. It never holds a commenter's email.

type Context

type Context struct {
	Site       SiteInfo
	Request    *http.Request
	Kind       string
	Content    *content.Item
	Items      []content.Item
	Term       *taxonomy.Term
	Pagination Pagination
	Query      map[string]string
	User       *auth.User
	Theme      Info
	Extra      map[string]any
	Locale     string
	Dir        string
	Status     int
	// Alternates are the page in each locale it exists in (hreflang links and language
	// switchers); empty when it exists in one locale only.
	Alternates []Alternate
}

type IdentityDraft added in v0.16.0

type IdentityDraft struct {
	Snapshot IdentitySnapshot `json:"snapshot"`
	SavedAt  string           `json:"saved_at"`
	Author   string           `json:"author"`
	// Base holds the live site values when the draft was saved, so a draft
	// never restores a title or logo that another apply changed meanwhile.
	Base map[string]string `json:"base,omitempty"`
	// Rebased lists the site keys replaced by their live value on load.
	Rebased []string `json:"-"`
}

IdentityDraft is the pending, unapplied identity of a theme.

type IdentityLogoSelectionStore added in v0.17.0

type IdentityLogoSelectionStore interface {
	IdentityLogoReview(context.Context, string) (revision string, slotBaselines map[string]string, err error)
	SaveIdentityLogoSlot(ctx context.Context, slug, baseline, slot, mediaID string) error
}

IdentityLogoSelectionStore captures all target baselines with the full review revision, then compares one target inside the final draft transaction.

type IdentityLogoStore added in v0.16.0

type IdentityLogoStore interface {
	IdentityLogoRevision(context.Context, string) (string, error)
}

IdentityLogoStore is the bounded interface used by logo generation bridges. It deliberately exposes neither ApplyIdentity nor whole-snapshot saving.

type IdentityPreviewOptions added in v0.16.0

type IdentityPreviewOptions struct {
	View     string // "home" or "single" (newest published post; home when none)
	Locale   string
	Snapshot IdentitySnapshot
}

IdentityPreviewOptions describes one identity preview render.

type IdentityRevision added in v0.16.0

type IdentityRevision struct {
	ID       string           `json:"id"`
	At       string           `json:"at"`
	Author   string           `json:"author"`
	Kind     string           `json:"kind"` // baseline, apply, revert, import
	Snapshot IdentitySnapshot `json:"snapshot"`
}

IdentityRevision is one applied identity state.

type IdentitySnapshot added in v0.16.0

type IdentitySnapshot struct {
	Theme  string            `json:"theme"`
	Values map[string]string `json:"values"`
	Site   map[string]string `json:"site"`
	// Links are the theme's header link settings (cta_label, cta_url) when
	// the theme declares them; they are written with the identity but are
	// ordinary theme settings, not design tokens.
	Links map[string]string `json:"links,omitempty"`
}

IdentitySnapshot is one complete identity state for a theme: its theme.<slug>.identity_* values plus the site keys. Drafts, revisions and previews all carry one.

func ImportBuilderIdentity added in v0.16.0

func ImportBuilderIdentity(slug string, b brand.Identity) IdentitySnapshot

ImportBuilderIdentity maps the builder's brand identity onto theme identity values, one way: colours carry over, the bundled Zad font maps to itself, other builder fonts and the page width stay with the builder layer (which still renders beneath the site identity).

func (IdentitySnapshot) Get added in v0.16.0

func (s IdentitySnapshot) Get(key string) string

Get returns the theme identity value for key ("" when absent).

type IdentityValidationError added in v0.16.0

type IdentityValidationError struct{ Fields map[string]string }

IdentityValidationError lists invalid fields (identity key or site key → i18n message key).

func (*IdentityValidationError) Error added in v0.16.0

func (e *IdentityValidationError) Error() string

type Info

type Info struct {
	Slug      string          `json:"slug"`
	Name      string          `json:"name"`
	Version   string          `json:"version"`
	Parent    string          `json:"parent"`
	Locations []string        `json:"locations"`
	Settings  []content.Field `json:"settings"`
	Supports  []string        `json:"supports"`
	// Logos are optional theme-shipped marks by variant (primary, dark,
	// light, icon): asset paths relative to the theme's assets/ directory,
	// served at /themes/<slug>/assets/<path>. Site settings take precedence.
	Logos map[string]string `json:"logos,omitempty"`
	Dir   string            `json:"dir"`
}

type InfoLookup

type InfoLookup interface {
	Lookup(slug string) (Info, error)
}

InfoLookup is implemented by the theme service: Lookup reads one installed theme's theme.json (ErrNotFound for unknown or invalid slugs).

type InheritedSource

type InheritedSource struct {
	Name   string `json:"name"` // e.g. templates/single.html
	Before string `json:"before"`
	After  string `json:"after"` // "" when no theme in the new chain provides it
}

InheritedSource is one inherited file and where it comes from before/after.

type Linker

type Linker struct {
	// contains filtered or unexported fields
}

Linker computes public paths (permalinks). It is the single source of URLs for the site router, the url template func and menu items:

page (hierarchical)   /parent/child         (the front page is /)
other content types   /{type}/{slug}
term                  /{taxonomy}/{term}
type archive          /{archive_slug or type}/

Paths are returned unescaped (Arabic slugs stay UTF-8); html/template percent-encodes them in attributes.

func NewLinker

func NewLinker(d *kernel.Deps) *Linker

NewLinker returns a Linker reading services lazily from d.

func (*Linker) Archive

func (l *Linker) Archive(ctx context.Context, typ string) string

Archive returns the archive path of content type typ.

func (*Linker) FrontPageID

func (l *Linker) FrontPageID(ctx context.Context) string

FrontPageID returns the static front page's content id (setting site.front_page), or "".

func (*Linker) Item

func (l *Linker) Item(ctx context.Context, it *content.Item) string

Item returns the public path of it.

func (*Linker) ItemWithFront

func (l *Linker) ItemWithFront(ctx context.Context, it *content.Item, frontID string) string

ItemWithFront is Item with the front page id already known (renders look it up once). An item in another locale than the site's lives under /<locale>/, and a translation of the front page is that locale's /.

func (*Linker) MenuItem

func (l *Linker) MenuItem(ctx context.Context, it *menus.Item)

MenuItem sets it.URL (and its children's) for items that reference a record: kind content (RefID = content id) and term (RefID = term id) always get the permalink, overwriting the menus module's fallback path ("/"+slug is wrong for every type but top-level pages); kind archive (RefID = type) is filled only when empty, so a stored URL wins.

func (*Linker) SiteLocale

func (l *Linker) SiteLocale(ctx context.Context) string

SiteLocale is the public site locale (setting site.locale): its URLs carry no locale prefix.

func (*Linker) Term

func (l *Linker) Term(t *taxonomy.Term) string

Term returns the public path of t.

type Module

type Module struct {
	// contains filtered or unexported fields
}

Module is the theme kernel.Module: it publishes theme.Service, the menus.resolve_url filter, the Appearance admin screens and the themes/templates API operations. Templates are parsed lazily on first render (after every module has added its funcs).

func New

func New(opts ...Option) *Module

New returns the theme module.

func (*Module) Catalogs

func (*Module) Catalogs() fs.FS

Catalogs implements kernel.CatalogProvider.

func (*Module) Migrations

func (*Module) Migrations() fs.FS

Migrations implements kernel.Module (overrides live on disk).

func (*Module) Name

func (*Module) Name() string

Name implements kernel.Module.

func (*Module) Register

func (m *Module) Register(d *kernel.Deps) error

Register publishes the service and wires hooks, admin and API.

type Option

type Option func(*Module)

Option configures the module.

func WithLogger

func WithLogger(l *slog.Logger) Option

WithLogger sets the logger for render errors (default: Deps.Log).

func WithThemesDir

func WithThemesDir(dir string) Option

WithThemesDir sets the themes directory (default: ThemesDir(cfg)).

type Override

type Override struct {
	Name      string `json:"name"`
	Draft     bool   `json:"draft"`
	UpdatedAt string `json:"updated_at"`
}

type Pagination

type Pagination struct {
	Page    int  `json:"page"`
	PerPage int  `json:"per_page"`
	Total   int  `json:"total"`
	Pages   int  `json:"pages"`
	HasPrev bool `json:"has_prev"`
	HasNext bool `json:"has_next"`
}

func NewPagination

func NewPagination(page, perPage, total int) Pagination

NewPagination computes page metadata; page and perPage are clamped to ≥ 1.

type PatternAware added in v0.11.0

type PatternAware interface {
	SetPatternLayer(p PatternLayer)
	InvalidateCache()
}

PatternAware is implemented by the theme service so the pattern module can attach a layer and flush compiled template caches when the active pattern changes.

type PatternLayer added in v0.11.0

type PatternLayer interface {
	// Active returns the filesystem directory and slug of the active pattern.
	// ok is false when no pattern is active or the package cannot be loaded.
	Active(ctx context.Context) (dir, slug string, ok bool)
}

PatternLayer is an optional layout package layered above the active theme and below site overrides. Patterns own opinionated page structure; themes remain identity systems (type, color, spacing, chrome). Wired by the pattern module after theme boots.

type ReparentReport

type ReparentReport struct {
	Child      string            `json:"child"`
	OldParent  string            `json:"old_parent"`
	NewParent  string            `json:"new_parent"`
	Changed    []InheritedSource `json:"changed"`   // inherited files whose source theme changes
	Missing    []string          `json:"missing"`   // part/component references the new chain cannot resolve
	Unchanged  int               `json:"unchanged"` // inherited files with the same source
	Applied    bool              `json:"applied"`   // theme.json was rewritten
	BackupPath string            `json:"backup,omitempty"`
}

ReparentReport describes the effect of moving Child from OldParent to NewParent.

func Reparent

func Reparent(roots []string, child, parent string, apply bool, now time.Time) (ReparentReport, error)

Reparent reports (and with apply, performs) moving child onto parent.

type Service

type Service interface {
	Active(ctx context.Context) Info
	List(ctx context.Context) ([]Info, error)
	Activate(ctx context.Context, slug string) error
	Resolve(req kernel.KindRequest) (templateName string, err error)
	Render(ctx context.Context, w http.ResponseWriter, req kernel.KindRequest, tc *Context) error // renders with layout; on template error → 500 page + log
	Templates(ctx context.Context) ([]TemplateFile, error)
	ReadTemplate(ctx context.Context, name string) (string, error)
	TemplateDetails(ctx context.Context, name string) (TemplateDTO, error)
	SaveOverride(ctx context.Context, name, body string, draft bool) error
	Overrides(ctx context.Context) ([]Override, error)
	PublishOverride(ctx context.Context, name string) error
	DeleteOverride(ctx context.Context, name string) error
}

type SiteInfo

type SiteInfo struct {
	Title   string `json:"title"`
	Tagline string `json:"tagline"`
	BaseURL string `json:"base_url"`
	Locale  string `json:"locale"`
	Dir     string `json:"dir"`
	Theme   string `json:"theme"`
}

type TOC

type TOC struct {
	Body  template.HTML
	Items []TOCItem
}

TOC is a body whose h2 and h3 headings all carry an id, with those headings in order.

type TOCItem

type TOCItem struct {
	ID    string
	Text  string
	Level int
}

TOCItem is one h2/h3 of a body, for an "on this page" list.

type TemplateDTO

type TemplateDTO struct {
	Name   string  `json:"name"`
	Source string  `json:"source"`
	Body   string  `json:"body"`
	Draft  *string `json:"draft"`
}

TemplateDTO is one template's sources in API responses; Draft is null when no draft exists.

type TemplateFile

type TemplateFile struct {
	Name   string `json:"name"`
	Path   string `json:"path"`
	Source string `json:"source"`
}

TemplateFile is a template visible to the theme; Source is "theme", "parent", "override", "draft", or "pattern".

type TemplateListDTO

type TemplateListDTO struct {
	Templates []TemplateFile `json:"templates"`
	Overrides []Override     `json:"overrides"`
}

type ThemeDTO

type ThemeDTO struct {
	Info
	Active bool `json:"active"`
}

ThemeDTO is a theme in API responses.

Directories

Path Synopsis
Package fake holds in-memory implementations of the services the theme engine and the site front controller consume (content, taxonomy, media, menus, redirects, seo), for tests.
Package fake holds in-memory implementations of the services the theme engine and the site front controller consume (content, taxonomy, media, menus, redirects, seo), for tests.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL