render

package
v1.1.17 Latest Latest
Warning

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

Go to latest
Published: Aug 23, 2026 License: MPL-2.0 Imports: 23 Imported by: 0

Documentation

Overview

Package render executes the host application's Go templates with the CMS's template funcs (cmsText, cmsRegion, cmsHead, cmsScripts) bound to a specific page's content. The host owns all page markup and styling; the CMS only fills the editable holes the templates declare.

Index

Constants

View Source
const DefaultPostsPerPage = 10

DefaultPostsPerPage is how many posts a paginated listing shows when neither the host's Config.PostsPerPage nor the template says otherwise.

View Source
const FuncNamePrefix = "cms"

FuncNamePrefix is the namespace the CMS reserves for its own template functions. Host functions may not start with it, so a host can add however many of its own as it likes without ever colliding with a func a later CMS release introduces.

View Source
const InertScriptType = "text/cms-code"

InertScriptType is the type an edit render gives every <script> inside a custom-code block. No browser knows it, so the parser stores the element and runs nothing — which is the point: an editing page must not run the code it is editing, and a logged-in editor merely looking at the page should still see the block rather than a hole where it goes. The editor script swaps these back for live scripts while the page is being viewed, and empties the block when edit mode starts.

View Source
const KindShared = "shared"

KindShared is the Kind of a region declared with {{cmsShared "key"}}: content stored once for the whole site rather than per page. Regions never returns one — a shared region is not the page's to save — and SharedRegions returns nothing else.

View Source
const NoticeRegion = "notice"

NoticeRegion is the shared region the notice bar's words live in. {{cmsShared "notice"}} reaches the same content — the bar simply renders it with its own markup around it.

View Source
const PagerCSS = `.cms-pager{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;` +
	`gap:.35em;margin:2.5em 0;font-size:.9375em;line-height:1}` +
	`.cms-pager ul{list-style:none;margin:0;padding:0}` +
	`.cms-pager-list{display:flex;flex-wrap:wrap;align-items:center;gap:.25em}` +

	`.cms-pager-link,.cms-pager-step{display:inline-block;box-sizing:border-box;min-width:2.5em;` +
	`padding:.6em .85em;border:1px solid transparent;border-radius:.4em;` +
	`text-align:center;text-decoration:none;color:inherit}` +

	`a.cms-pager-link:hover,a.cms-pager-step:hover{border-color:rgba(128,128,128,.35);` +
	`background:rgba(128,128,128,.12)}` +
	`a.cms-pager-link:hover,a.cms-pager-step:hover{` +
	`border-color:color-mix(in srgb,currentColor 30%,transparent);` +
	`background:color-mix(in srgb,currentColor 10%,transparent)}` +
	`.cms-pager-current{font-weight:700;border-color:rgba(128,128,128,.55)}` +
	`.cms-pager-current{border-color:color-mix(in srgb,currentColor 55%,transparent)}` +

	`.cms-pager-step{min-width:0;font-weight:500}` +
	`.cms-pager-off{opacity:.35}` +
	`.cms-pager-gap{display:inline-block;padding:.6em .2em;opacity:.5}`

PagerCSS is the functional minimum for cmsPagination markup: one centered row of evenly sized targets, with the current page and the dead end buttons told apart from live links.

It states no colors of its own. Everything is currentColor mixed with transparency, so the bar inherits the host's palette and stays legible on a dark background as readily as a light one — the CMS knows neither. The current page is outlined rather than filled for the same reason: a fill needs a second color to contrast against. Plain classes throughout, so a host stylesheet can take the whole thing over.

View Source
const SharedRegionPrefix = "site:"

SharedRegionPrefix namespaces shared regions in the marker attributes an edit render emits: {{cmsShared "footer"}} becomes data-cms-region="site:footer". The prefix is what tells the editor to save that region to the site rather than to the page it is standing on, and it keeps a page region named "footer" distinct from the shared one.

Variables

View Source
var NoticeStyles = []NoticeStyle{
	{Key: "dark", Label: "Dark"},
	{Key: "accent", Label: "Accent (blue)"},
	{Key: "warning", Label: "Warning (amber)"},
	{Key: "alert", Label: "Alert (red)"},
	{Key: "light", Label: "Light"},
}

NoticeStyles are the schemes the site-settings dialog offers, in the order it offers them. The first is the default: an unset or unknown key resolves to it, so stored settings can never render a bar with no colours at all.

Functions

func BuildMenus

func BuildMenus(items []content.MenuItem, currentSlug, locale, defaultLocale string, includeDrafts bool) map[string][]MenuEntry

BuildMenus turns stored menu items into render-ready entries grouped by menu key. Page-linked items resolve their URL from the page's current slug; items pointing at unpublished or private pages are dropped unless includeDrafts (editors see draft and private pages, so they see their menu items). Label-only top-level items are dropdown parents holding their Children; on public renders a dropdown with nothing visible in it is dropped, while editors keep it so they can fill it. Menu entries carry the locale's labels and locale-prefixed page URLs.

func CheckTemplate

func CheckTemplate(name, src string) error

CheckTemplate parses src as a page template, with the cms template funcs stubbed, and returns the parse error if there is one. It needs no database, request, or rendered page, which is what makes it usable from a test.

Page templates are parsed when the server first renders them, so a malformed one compiles perfectly well and fails only in front of a visitor. Host applications can point this at their own templates to find that at build time instead; the scaffold uses it on the templates `cms init` writes.

A host that registers its own template functions (Config.TemplateFuncs) must use CheckTemplateFuncs instead, or every template calling one of them reports a spurious "function not defined".

func CheckTemplateFuncs

func CheckTemplateFuncs(name, src string, host template.FuncMap) error

CheckTemplateFuncs is CheckTemplate with the host's own template functions declared alongside the cms ones. Only the names and signatures matter here — the funcs are never called — so a build-time check can pass the same map the server does.

func EditorAppliedClasses added in v1.1.11

func EditorAppliedClasses() []string

EditorAppliedClasses are the Tailwind classes the in-place editor can put into content on its own — chosen from a dialog rather than typed, and carried in by no snippet, so nothing that scans stored content sees them until after the first save that happens to use one.

That gap is the whole reason this list exists. A class the editor offers but nothing compiles is the worst kind of bug to meet: the setting appears to work, the class really does change on the element, and the page does not move. The list is folded into the generated content stylesheet's corpus so a fresh install is covered, and the docs' safelists are checked against it so the two cannot drift.

Keep it in step with the editor script: its entries are the column tool's (editor/src/columns.js), which reshapes a block into a row of columns and rewrites the track count and spans as columns are added, removed, and resized, and the toolbar's text-size ladder (editor/src/richtext.js). Widening what either offers means widening this.

The counts here are the sm: forms, which is what every stock snippet uses. A host whose own snippets set their tracks at another breakpoint — or at none — gets the same treatment, since the tool writes back at whatever prefix it read, and has to safelist that prefix itself.

func EditorScriptPath

func EditorScriptPath() string

EditorScriptPath is the public route the in-place editor script is served from. It carries a digest of the editor bundle, so shipping a new one changes the address and no browser can go on running the copy it cached. See editor.Version.

func LocalePrefix

func LocalePrefix(locale, defaultLocale string) string

LocalePrefix is the URL path prefix for a locale: "" for the default locale, "/fr" for a non-default "fr". Localized page URLs are prefix + "/" + slug (the prefix alone for the homepage).

func SharedRegionName

func SharedRegionName(marker string) (string, bool)

SharedRegionName strips the marker prefix from a region name, reporting whether it was there — the server side of SharedRegionPrefix.

func ValidBackgroundColor

func ValidBackgroundColor(s string) string

ValidBackgroundColor returns the value if it is a safe #rrggbb color, or "" otherwise.

func ValidBackgroundPosition

func ValidBackgroundPosition(s string) string

ValidBackgroundPosition returns the value if it is a pair of percentages in range and not the centered default, or "" otherwise. "" renders centered.

func ValidBackgroundURL

func ValidBackgroundURL(s string) string

ValidBackgroundURL returns the value if it is safe to embed in a CSS url('…') inside an HTML attribute: an http(s) or site-relative URL containing none of the characters that could break out of either context. Returns "" otherwise.

func ValidNoticeStyle added in v1.1.13

func ValidNoticeStyle(key string) string

ValidNoticeStyle resolves a stored style key to one that exists, falling back to the first scheme. Unknown keys are normalized rather than refused, the way an unknown section setting is: a key that stopped existing should restyle the bar, not break the page.

func ValidSectionHeight

func ValidSectionHeight(s string) string

ValidSectionHeight returns the value if it is one of the fixed viewport-height options ("50", "75", "100"), or "" otherwise ("auto" and anything unknown mean no minimum height).

func ValidSectionVAlign

func ValidSectionVAlign(s string) string

ValidSectionVAlign returns the value if it is a non-default vertical alignment ("center" or "bottom"), or "" otherwise ("top" is the default flow and needs no styles).

func ValidateFuncNames

func ValidateFuncNames(m template.FuncMap) error

ValidateFuncNames checks a host's template functions for names the CMS cannot accept: anything inside the reserved cms* namespace, and anything that is not a legal template identifier (templates call funcs by name, so an unusable name is a silent no-op rather than an error the host would otherwise see).

Types

type CodeLookup added in v1.1.9

type CodeLookup func(key string) (html string, ok bool)

CodeLookup resolves a custom-code block's key to the markup stored for it, reporting false when the library has no such entry (or it is empty). Input.CodeSnippets carries one; snippets.CodeStore.Lookup builds the usual implementation.

type EditInfo

type EditInfo struct {
	PageID     int64
	Slug       string // "" identifies the home page (not deletable)
	AdminPath  string
	CSRFToken  string
	Locale     string
	Status     string // "draft" or "published"
	Visibility string // "public" or "private" — who may view the page
	// HasUnpublished is true when a published page's draft content
	// differs from what is live — the editor shows "Unpublished changes"
	// and keeps Publish available.
	HasUnpublished bool
	MediaEnabled   bool
	// IsAdmin unlocks admin-only editor chrome (the page CSS & JS
	// panel); the server enforces the restriction regardless.
	IsAdmin bool
	// IsSuperadmin lets the new-page dialog offer unlisted page
	// templates and puts the development/production switch in the site
	// settings dialog; the server holds both to the same rule regardless.
	IsSuperadmin bool
	// CanPages, CanBlogs, and CanNews unlock the editor's cross-page
	// chrome: new page, menu editing, and site settings need pages;
	// creating a post needs its feed. The server enforces every one of
	// these regardless — the flags only decide what the UI offers.
	CanPages bool
	CanBlogs bool
	CanNews  bool
	// PostsEnabled shows the tool rail's "New post" button (blog & news
	// configured on the host).
	PostsEnabled bool
	// Locales is the site's configured locale list ([0] = default); more
	// than one entry shows the edit bar's locale switcher.
	Locales []string
	// Post, set when the page backs a post, enables the edit bar's
	// post-settings gear (date, summary, thumbnail, header image).
	Post     *PostInfo
	Styles   []EditorStyle  // entries for the editor's Styles menu
	Sections *SectionStyles // options for section settings
}

EditInfo turns a render into an editable one: regions are wrapped in marker elements and the in-place editor script is injected before </body>. Pass nil for a plain public render.

type EditorStyle

type EditorStyle struct {
	Label string `json:"label"`
	Class string `json:"class"`
	// Block applies the style to the whole surrounding block, converted
	// to this element ("p", "h2", ...). Empty applies the style inline
	// to the selected text.
	Block string `json:"block,omitempty"`
	// Group nests the entry in a submenu with this title. Entries
	// sharing a Group are folded together; the submenu sits where its
	// first member appears, among any ungrouped top-level entries.
	// Empty keeps the entry at the top level.
	Group string `json:"group,omitempty"`
}

EditorStyle is one entry in the in-place editor's "Styles" menu. Styles apply CSS classes — never inline styles — so the host site's stylesheet stays the single source of design truth and a redesign can restyle existing content. Class may hold several space-separated classes.

func DefaultEditorStyles

func DefaultEditorStyles() []EditorStyle

DefaultEditorStyles is the Tailwind-first default Styles menu, used when the host does not configure its own. Every class here must be safelisted in the site's Tailwind build (see the README) — editor content lives in the database, which Tailwind's source scanner never sees.

type FeedPage

type FeedPage struct {
	Feed  string     // "blog" or "news"
	Posts []PostInfo // this page's posts, newest first
	Pager
}

FeedPage is one page of a paginated post listing — what {{cmsFeed "blog"}} returns. Posts holds that page's posts and the embedded Pager says where the page sits in the feed, so a template can draw its own pagination from the numbers ({{$feed.Page}}, {{range $feed.Links}}) or hand the whole value to {{cmsPagination}}.

func (*FeedPage) HasPages

func (f *FeedPage) HasPages() bool

HasPages shadows the embedded Pager's so it stays safe on a nil *FeedPage, which is what {{cmsFeed}} yields for an unknown feed.

type Input

type Input struct {
	Page   *content.Page
	Blocks []content.Block
	// Shared holds the site's shared-region blocks — what {{cmsShared}}
	// renders. They are the same on every page, so they arrive alongside
	// the page's own rather than being looked up per region.
	Shared []content.Block
	Locale string
	Menus  map[string][]MenuEntry
	// Edit produces the editable variant of the page; see EditInfo. Nil
	// for a plain public render.
	Edit *EditInfo
	// Post is set when the page backs a blog or news post; it becomes the
	// template dot's .Post.
	Post *PostInfo
	// Posts feeds {{cmsPosts}} and {{cmsFeed}} on listing templates. Nil
	// makes both funcs return nothing.
	Posts PostLister
	// PostsPerPage sizes a {{cmsFeed}} page. Zero uses
	// DefaultPostsPerPage; a template may override it per listing.
	PostsPerPage int
	// PageNumber is the listing page this request asked for (?page=N).
	// Zero and below mean the first page, and so does a number past the
	// end — {{cmsFeed}} clamps it to a page that exists.
	PageNumber int
	// PageURL builds the URL of listing page n, for {{cmsFeed}}'s
	// prev/next and numbered links. Nil falls back to a bare "?page=n",
	// which is right for a render with no request behind it.
	PageURL func(n int) string
	// Locales is the site's configured locale list ([0] = default), for
	// {{cmsLocales}} and hreflang links. Nil or single-entry disables
	// both.
	Locales []string
	// BaseURL ("scheme://host", no trailing slash) makes hreflang
	// alternate links absolute; empty omits them.
	BaseURL string
	// Site is the stored site-wide settings ({{cmsBrand}}, {{cmsSiteName}}, nav
	// alignment). The zero value means "all defaults".
	Site content.SiteSettings
	// AdminPath is the URL prefix the admin area is mounted at, used to
	// build the optional "Log in" nav link (Site.LoginInNav). Empty
	// disables the link even when the setting is on.
	AdminPath string
	// CodeSnippets resolves the custom-code blocks a page references —
	// the admin-written markup-and-JavaScript kept out of page content
	// so the HTML sanitizer never has to carry it. Nil leaves the
	// placeholders alone, and so, in effect, does an edit render: it
	// fills them, but with every <script> parked under InertScriptType,
	// so nothing executes and the editor still saves back the
	// placeholder it was given.
	CodeSnippets CodeLookup
	// Funcs replaces the host template functions declared at construction
	// (NewWithFuncs) for this render, entry by entry: a name it does not
	// carry keeps the declared implementation. This is how a host binds
	// its functions to the request — a database query wants the request's
	// context, and a locale-aware one wants Locale — without the renderer
	// having to know what a request is.
	//
	// Only names the constructor declared can actually be called: the
	// templates were parsed against that set, so a name appearing here
	// for the first time is unreachable. Reserved cms* names are ignored.
	Funcs template.FuncMap
}

Input is everything one page render needs. Page, Blocks, and Locale are required; the rest is optional.

type LocaleLink struct {
	Code   string // e.g. "en", "fr"
	URL    string // this page in that locale, e.g. "/fr/about"
	Active bool   // the locale of the current render
}

LocaleLink is one entry {{cmsLocales}} returns: the current page's URL in each configured locale, for host-rendered language switchers.

type MenuEntry struct {
	ID       int64 // menu item id; the editor's edit-mode marker
	Label    string
	URL      string // empty for a dropdown parent (label-only)
	NewTab   bool
	Active   bool        // this entry links to the page being rendered
	External bool        // absolute http(s) URL rather than a site page
	Children []MenuEntry // one level of dropdown items
}

MenuEntry is one rendered navigation item, as templates receive it from {{cmsMenu "main"}} and as {{cmsNav "main"}} renders it. The data-only cmsMenu form supplies entries and the template owns the markup; cmsNav emits the CMS's own nav markup (which is what the in-place editor can edit in place).

type NoticeStyle added in v1.1.13

type NoticeStyle struct {
	Key   string `json:"key"`
	Label string `json:"label"`
}

NoticeStyle is one of the notice bar's curated colour schemes. The key is what settings store and what becomes the bar's cms-notice-{key} class; the CSS ships with the CMS (noticeCSS) rather than coming from the host's Tailwind build, so a bar looks right on a site that has never heard of it. A host restyles the bar by overriding .cms-notice, the same bargain {{cmsNav}} offers.

type PageData

type PageData struct {
	Title string
	// Description is the page's description, which on a post is its
	// summary — the words its listing card and feed entry show.
	Description string
	// MetaDescription is what cmsHead publishes to search engines: the
	// page's own meta description when it has one, otherwise
	// Description. Templates need it only to do something else with the
	// same words; the meta tag itself comes from cmsHead.
	MetaDescription string
	Slug            string
	Locale          string
	// Post is set when the page is a blog or news post's backing page —
	// the post template reads its date, author, and images from here.
	// Nil on ordinary pages.
	Post *PostInfo
}

PageData is the dot value passed to a page template.

type PageLink struct {
	Number   int
	URL      string
	Current  bool
	Ellipsis bool
}

PageLink is one entry in the numbered part of a pagination bar. An Ellipsis entry stands for the pages skipped in a long list: it has no number and no URL.

type PageTemplate

type PageTemplate struct {
	File     string `json:"file"`
	Label    string `json:"label"`
	Unlisted bool   `json:"unlisted,omitempty"`
}

PageTemplate is one template the host application offers for pages. File is a path within the host's TemplateFS (e.g. "templates/pages/home.gohtml"); Label is what content editors see when choosing a page type.

Unlisted keeps a template out of the new-page chooser for everyone but superadmins: for one-off templates — a home page, a staff directory — that only ever back a single page, where offering them on every "new page" would invite a second. Pages already using an unlisted template render and edit exactly as before; only creation is limited.

type Pager

type Pager struct {
	Page    int // 1-based, always within [1, TotalPages]
	PerPage int
	// Total is the list's full length and TotalPages how many pages that
	// makes. TotalPages is at least 1, so an empty list is one empty page
	// rather than a list with no pages at all.
	Total      int
	TotalPages int
	// PrevURL and NextURL are the adjacent pages, empty at either end.
	PrevURL string
	NextURL string
	// Links is the numbered bar: every page in a short list, and in a
	// long one the ends, the pages around the current one, and ellipses
	// for the gaps.
	Links []PageLink
	// PrevLabel and NextLabel caption the end buttons. NewPager writes
	// English; a caller with a translator to hand overwrites them (the
	// admin does, since its UI is bilingual).
	PrevLabel string
	NextLabel string
}

Pager is where one page of a list sits in the whole of it, and how to reach the others — the state any paginated list needs, whatever it is listing. FeedPage embeds one for {{cmsFeed}} and the admin builds them for its own tables, so every paginated list in the CMS draws the same bar from the same code.

func NewPager

func NewPager(page, perPage, total int, url func(int) string) *Pager

NewPager works out the numbering for a list of total items shown perPage at a time. page is the one asked for, clamped into range — a number past the end lands on the last real page rather than on an empty one. url builds the link to a page number; nil yields a bare "?page=n".

The count has to be known first, so the usual order is: count, NewPager, then fetch Offset()..PerPage. That way an out-of-range page is corrected before the window is read rather than after.

func (*Pager) HTML

func (f *Pager) HTML() template.HTML

HTML is the ready-made bar: previous, the numbered links, next. It renders nothing for a list that fits on one page. Like cmsNav it is plain classes over minimal markup (styled by PagerCSS, which {{cmsHead}} inlines on the public site and the admin layout inlines for its own tables), so a stylesheet can restyle it entirely; callers wanting different markup range over the Pager's fields instead.

{{cmsPagination}} is this method, and the admin's lists call it directly, so both bars are the same bar.

func (*Pager) HasPages

func (p *Pager) HasPages() bool

HasPages reports whether the list runs to more than one page — what a template asks before drawing pagination at all. Safe on a nil Pager.

func (*Pager) Offset

func (p *Pager) Offset() int

Offset is how many items to skip to reach this page — the OFFSET of the query that fills it.

type PostImages

type PostImages func(md *media.Media, prefer string) *media.Image

PostImages resolves a post's library image into the renditions a template can use. prefer names the rendition wanted as the default src. media.Manager.ImageFor is the implementation; nil (no media library configured) leaves posts with whatever URLs they stored.

type PostInfo

type PostInfo struct {
	ID          int64  // post id, for the editor's settings API
	Feed        string // "blog" or "news"
	Title       string
	Summary     string
	URL         string // site-relative, e.g. "/blog/launch-day"
	PublishedAt time.Time
	// Author is the byline to print: empty when the post has none, and
	// equally empty when the post is set to publish without one, so
	// {{with .Author}} is all a template needs for either.
	Author string
	// HideAuthor is that setting, and AuthorName the name behind it —
	// the recorded author whether or not it is shown. Templates want
	// .Author; these two are for the editor's settings gear, which has
	// to name the byline it is offering to switch off.
	HideAuthor bool
	AuthorName string

	// Thumbnail is the post's listing image with every rendition resolved
	// — src, srcset, and intrinsic size — and is nil when the post has
	// none. Prefer it to the bare URL: a listing card that uses
	// .Thumbnail gets an image sized for a card, where .ThumbnailURL
	// alone leaves the browser to download a full-width one and scale it
	// down. A post's banner is not here — it is a section in the post
	// template's header region.
	Thumbnail *media.Image
	// ThumbnailURL is that image's default src, for templates that want
	// one string. "" when the post has no thumbnail.
	ThumbnailURL string
	// The library id behind it, 0 when the image is external or unset.
	// The in-place editor's post-settings dialog round-trips it.
	ThumbnailMediaID int64

	Draft bool // only ever true on editor renders
}

PostInfo describes one blog or news post to templates: the dot's .Post on a post page, and the entries {{cmsPosts "blog" 10}} returns for listing pages. Title and Summary are the backing page's title and description for the render's locale.

func PostInfoFor

func PostInfoFor(p *content.Post, localePrefix string, images PostImages) *PostInfo

PostInfoFor builds the template-facing view of a stored post. localePrefix ("" or e.g. "/fr", see LocalePrefix) localizes the URL, and images resolves the post's library images.

type PostLister

type PostLister func(feed string, limit, offset int, count bool) (posts []PostInfo, total int)

PostLister supplies a feed's posts, newest first, for {{cmsPosts}} and {{cmsFeed}}. offset skips that many posts from the newest; a non-positive limit means no limit (and no offset). total is how long the feed is in full, but only when count is set — pagination needs it to size its page links, and a plain {{cmsPosts}} does not, so the extra query is not worth running unasked. Nil disables both funcs.

type Region

type Region struct {
	Name string
	Kind string // "text", "html", "image", "sections", or "shared"
}

Region is an editable area a page template declares via {{cmsText "key"}}, {{cmsRegion "key"}}, {{cmsImage "key"}}, or {{cmsSections "key"}}.

type Renderer

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

Renderer holds one parsed template set per page template: the shared templates (layouts, partials) plus that page's file. Per-file sets let every page define the same block names (e.g. "content") without colliding.

func New

func New(fsys fs.FS, shared []string, pages []PageTemplate, sections *SectionStyles, hidden ...PageTemplate) (*Renderer, error)

New parses each page template together with the shared globs and returns a Renderer. It fails fast on any template that doesn't parse. A nil sections gets the Tailwind-first defaults. Hidden templates are parsed like page templates but left out of PageTemplates(), so they never appear in the admin's or editor's template choosers — the post template is one.

func NewWithFuncs

func NewWithFuncs(fsys fs.FS, shared []string, pages []PageTemplate, sections *SectionStyles, host template.FuncMap, hidden ...PageTemplate) (*Renderer, error)

NewWithFuncs is New with the host application's own template functions declared alongside the CMS's. Templates parse against their names, so any number of them may be registered and called like the cms* funcs — typically to reach data the CMS does not own (a product catalogue, a vehicle table) from inside a CMS-managed page.

The map given here is what templates parse against, and what renders use unless Input.Funcs supplies a per-request replacement. Names inside the reserved cms* namespace are rejected; see ValidateFuncNames.

func (*Renderer) Knows

func (r *Renderer) Knows(file string) bool

Knows reports whether file is a registered page template.

func (*Renderer) ListedPageTemplates added in v0.9.0

func (r *Renderer) ListedPageTemplates() []PageTemplate

ListedPageTemplates returns the page templates offered when creating a page — PageTemplates() minus the Unlisted ones, which only superadmins may start new pages from.

func (*Renderer) PageTemplates

func (r *Renderer) PageTemplates() []PageTemplate

PageTemplates returns the templates available for pages, for the admin UI.

func (*Renderer) Regions

func (r *Renderer) Regions(templateFile string) []Region

Regions walks the parse tree of a page template (and every template it invokes) and returns the editable regions it declares, in source order, deduplicated. This is how the admin UI knows which content fields to show for a page without the developer maintaining a separate region list.

Shared regions are left out: they belong to the site, not to any page that happens to render them (see SharedRegions).

func (*Renderer) Render

func (r *Renderer) Render(w io.Writer, in Input) error

Render executes the page's template and writes the result to w. Output is buffered so a template error never sends a partial page.

func (*Renderer) SetContentCSSHref

func (r *Renderer) SetContentCSSHref(href string)

SetContentCSSHref sets (or, with "", clears) the stylesheet link {{cmsHead}} emits for CMS-generated content CSS. Safe to call concurrently with renders.

func (*Renderer) SharedRegions

func (r *Renderer) SharedRegions() []Region

SharedRegions returns every shared region ({{cmsShared "key"}}) any of the host's templates declares, deduplicated, plus the notice bar's own (NoticeRegion). Shared content has no template of its own to be validated against — it is reached from whichever page an editor happens to be on — so this union is what a save checks a region name against.

type SectionOption

type SectionOption struct {
	Key          string `json:"key"`
	Label        string `json:"label"`
	Class        string `json:"class"`
	ContentClass string `json:"contentClass,omitempty"`
}

SectionOption is one choice in a section setting (a background or a content width): a stable Key stored with the content, a Label editors see, and the classes it applies. Class goes on the <section> wrapper for backgrounds and on the inner content container for widths; ContentClass (backgrounds only) is added to the content container — e.g. a dark background pairing with prose-invert.

type SectionStyles

type SectionStyles struct {
	Backgrounds []SectionOption `json:"backgrounds"`
	Widths      []SectionOption `json:"widths"`
	Corners     []SectionOption `json:"corners"`

	// Paddings is the vertical breathing room around a section's
	// content, as its own axis.
	//
	// It is separate from Widths because they answer different
	// questions, and bundling them — the obvious shortcut, since a width
	// preset is already a class string and can just carry a py-* — makes
	// one unanswerable: "the same measure, but tighter" then has no
	// expression except a second width option, and the option list
	// multiplies by every spacing anyone wants.
	//
	// It is also separate from the height setting, which is a min-height
	// and can only ever make a section taller. An editor looking for less
	// space reaches for height first, finds "Auto" already selected, and
	// concludes the CMS cannot do it.
	//
	// Optional: a nil list leaves sections exactly as they render today,
	// so hosts that keep their padding in the width presets are
	// unaffected. When set, the first entry is the default for content
	// saved before the axis existed — make it match whatever padding the
	// width presets used to carry, and nothing shifts.
	Paddings []SectionOption `json:"paddings"`

	// Sizes is the section's text size, as its own axis: one setting
	// that scales the whole section's typography — headings, body,
	// lists, and their leading together — rather than a size picker on
	// every element.
	//
	// That framing is the point. Font size is the one control where
	// per-element freedom reliably produces worse pages than no control
	// at all: a paragraph bumped two steps beside headings the theme
	// set does not read as emphasis, it reads as broken. Scaling the
	// container keeps the ratios the theme chose and moves everything
	// with them, so the worst an editor can do is a section that is
	// bigger or smaller — never one that is internally inconsistent.
	//
	// The default classes are Tailwind Typography's size modifiers,
	// which is why they go on the content container: it is the element
	// the width presets already put prose on, and a modifier with no
	// prose beside it styles nothing. A host whose widths don't carry
	// prose wants its own list here — container-level text-* classes,
	// or nothing.
	//
	// Optional, and the first entry is the default that content saved
	// before the axis existed resolves to, so it must contribute no
	// class: unlike spacing, there is no size the old markup was
	// implicitly carrying, and anything else moves every published
	// section the moment the axis appears.
	Sizes []SectionOption `json:"sizes"`
}

SectionStyles is the curated set of section settings editors choose from. Like editor styles, everything is classes — the host CSS owns the actual appearance. Corner classes go on the <section> wrapper like background classes (the wrapper is what paints the background, so the radius must clip it there).

func DefaultSectionStyles

func DefaultSectionStyles() *SectionStyles

DefaultSectionStyles is the Tailwind-first default set of section settings. The classes need safelisting like editor styles do.

Vertical spacing is an axis of its own rather than part of each width preset, because bundled the two make "the same measure, but tighter" inexpressible except as a second width option — and the option list then multiplies by every spacing anyone wants. A host replacing these wholesale can leave Paddings nil and keep its py-* inside the width classes; the axis simply does not appear.

func (*SectionStyles) Background

func (ss *SectionStyles) Background(key string) SectionOption

Background resolves a stored background key, falling back to the first option for unknown keys.

func (*SectionStyles) Corner

func (ss *SectionStyles) Corner(key string) SectionOption

Corner resolves a stored corner-rounding key, falling back to the first option. A nil Corners list resolves everything to a zero option, so hosts that ship no corner choices render sections unchanged.

func (*SectionStyles) Padding added in v0.9.0

func (ss *SectionStyles) Padding(key string) SectionOption

Padding resolves a stored vertical-spacing key, falling back to the first option. A nil Paddings list resolves to a zero option, which contributes no class — so a host that never configured the axis renders exactly as it did before it existed.

func (*SectionStyles) Size added in v1.1.17

func (ss *SectionStyles) Size(key string) SectionOption

Size resolves a stored text-size key, falling back to the first option. A nil Sizes list resolves to a zero option, which contributes no class — so a host that never configured the axis renders exactly as it did before it existed.

func (*SectionStyles) Width

func (ss *SectionStyles) Width(key string) SectionOption

Width resolves a stored width key, falling back to the first option.

Jump to

Keyboard shortcuts

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