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
- Variables
- func BuildMenus(items []content.MenuItem, currentSlug, locale, defaultLocale string, ...) map[string][]MenuEntry
- func CheckTemplate(name, src string) error
- func CheckTemplateFuncs(name, src string, host template.FuncMap) error
- func EditorAppliedClasses() []string
- func EditorScriptPath() string
- func LocalePrefix(locale, defaultLocale string) string
- func SharedRegionName(marker string) (string, bool)
- func ValidBackgroundColor(s string) string
- func ValidBackgroundPosition(s string) string
- func ValidBackgroundURL(s string) string
- func ValidNoticeStyle(key string) string
- func ValidSectionHeight(s string) string
- func ValidSectionVAlign(s string) string
- func ValidateFuncNames(m template.FuncMap) error
- type CodeLookup
- type EditInfo
- type EditorStyle
- type FeedPage
- type Input
- type LocaleLink
- type MenuEntry
- type NoticeStyle
- type PageData
- type PageLink
- type PageTemplate
- type Pager
- type PostImages
- type PostInfo
- type PostLister
- type Region
- type Renderer
- func (r *Renderer) Knows(file string) bool
- func (r *Renderer) ListedPageTemplates() []PageTemplate
- func (r *Renderer) PageTemplates() []PageTemplate
- func (r *Renderer) Regions(templateFile string) []Region
- func (r *Renderer) Render(w io.Writer, in Input) error
- func (r *Renderer) SetContentCSSHref(href string)
- func (r *Renderer) SharedRegions() []Region
- type SectionOption
- type SectionStyles
Constants ¶
const DefaultPostsPerPage = 10
DefaultPostsPerPage is how many posts a paginated listing shows when neither the host's Config.PostsPerPage nor the template says otherwise.
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.
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.
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.
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.
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.
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 ¶
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 ¶
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 ¶
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: today 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. Widening what that tool 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 ¶
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 ¶
SharedRegionName strips the marker prefix from a region name, reporting whether it was there — the server side of SharedRegionPrefix.
func ValidBackgroundColor ¶
ValidBackgroundColor returns the value if it is a safe #rrggbb color, or "" otherwise.
func ValidBackgroundPosition ¶
ValidBackgroundPosition returns the value if it is a pair of percentages in range and not the centered default, or "" otherwise. "" renders centered.
func ValidBackgroundURL ¶
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
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 ¶
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 ¶
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 ¶
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
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}}.
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.
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 ¶
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 ¶
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
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 ¶
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 ¶
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 ¶
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.
type PostImages ¶
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 ¶
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 ¶
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) 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 ¶
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 ¶
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 ¶
SetContentCSSHref sets (or, with "", clears) the stylesheet link {{cmsHead}} emits for CMS-generated content CSS. Safe to call concurrently with renders.
func (*Renderer) SharedRegions ¶
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"`
}
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) Width ¶
func (ss *SectionStyles) Width(key string) SectionOption
Width resolves a stored width key, falling back to the first option.