On this page
The assembly's one set of page-chrome assets: a single site-level stylesheet per theme in use, with every grafted page re-pointed at it on each deploy.
#internal/blog/chrome
#internal/blog/chrome
Package chrome is the assembly's one set of page-chrome assets.
Every project's build emits its own "style.css" at its own output root, and a standalone deploy of that project needs it -- a project published on its own is a whole site and carries its own presentation. Inside the assembly that same file is one copy per subtree of the same stylesheet, which makes a presentation fix a republish of every project rather than a deploy.
This package is the assembly's answer: the shared generator writes one site-level asset per theme actually in use, sourced from the theme files of the toolchain running the deploy, and re-points every emitted page at it. The pass runs on every deploy of any project, so a toolchain upgrade reaches the whole site on the next deploy instead of waiting for each project to publish again.
What this does not touch is a project's own build: a constraint by design. "selfdoc build" keeps writing a self-contained "style.css" beside the pages that reference it, because a standalone deploy has no assembly to serve a site-level asset. The re-pointing is assembly-mount behaviour and belongs here, on the assembly side of the line.
Names are content-hashed rather than version-stamped. A toolchain version changes on releases that do not touch the CSS, and the CSS changes during development without a version change; the hash is the only name that is as stable as the bytes it addresses, which is what a cache needs. Identical content also produces an identical name, so a deploy that changes nothing about the chrome writes nothing about it either.
Themes are per project, so the asset set is theme-keyed: one asset per distinct theme the roster's manifests declare, and a page references the one its own project uses. A page that is the site's rather than any project's -- the shared pages, the home project's pages at the root, the site-level blog -- references the home project's. A manifest naming no theme is every manifest published so far, and means [DefaultTheme], which is what "selfdoc build" itself uses when a project's config names none.
#Migrating an already-published site
Nothing has to be republished, and nothing breaks in between. The subtrees on a live site reference their own "style.css", which is still there -- the re-pointing rewrites the reference in the emitted HTML and leaves the file alone, because the file is in each project's published-file record and deleting it out from under that record is the prune's business, not this pass's. On the first deploy after this ships, of any project, the shared generator runs over the whole tree and every page in it moves to the site-level asset in one pass. Until that deploy the site is as it was.
#Dir
const Dir = "_chrome"Dir is the site-level directory the chrome assets are served from.
It is one of the assembly's own directories, so no project may claim it as a slug -- see the assembly's reserved site directories.
#DefaultTheme
const DefaultTheme = manifest.DefaultThemeDefaultTheme is the theme a project gets when it names none.
This is not a choice made here: it is the same default "selfdoc build" applies when a project's selfdoc.json carries no "theme" key, and the two have to agree or a page would be styled by a stylesheet its build never rendered against. Read from the manifest package rather than restated, so there is one value and not two that have to be kept equal by hand.
#ManifestTheme
func ManifestTheme(loaded map[string]any) stringManifestTheme is the theme a loaded manifest declares, or the build's own default.
A manifest that names no theme is every manifest published so far: the key is read here so a project that starts declaring one is honoured without another change on this side.
#Themes
func Themes(manifests []map[string]any, homeSlug string, override string) (map[string]string, string)Themes returns the slug-to-theme mapping for a roster's manifests together with the home project's theme.
Theme choice is per project, so the site-level asset is theme-keyed rather than singular: the assembly emits one asset per distinct theme its projects declare, and a page references the one its own project uses.
override names one theme every project is treated as declaring. It exists for the preview's --theme, which builds every checkout under one theme so the whole site can be judged under it at once; the pages were rendered against that theme, so the asset they reference has to be that theme too, whatever each project's manifest says. Empty -- always, on a deploy -- means every project keeps its own.
#CSS
func CSS(theme string) (string, error)CSS is the full stylesheet the site-level asset for a theme carries.
The theme's own CSS plus the highlight rules its metadata names -- the same two pieces, in the same order, that a project's build writes into its own "style.css" -- plus the assembly's shared-page rules, minified.
#AssetRel
func AssetRel(theme string, css string) (string, error)AssetRel is the site-relative path the asset for a theme with the given composed stylesheet takes.
A plain theme is one file. A framework theme is a directory -- "_chrome/
The digest covers the composed stylesheet and the module payload. The stylesheet alone would be enough for a framework whose CSS changes whenever its JavaScript does, and that is not a property anything guarantees: a framework release that fixed only a module would leave the directory name unchanged and every cache would go on serving the old modules from it. What names the payload has to be everything inside it.
#WriteAssets
func WriteAssets(siteDir string, themeNames []string, h *effects.Handle) (map[string]string, error)WriteAssets writes one asset per theme named and returns the theme-to-site-path mapping.
Any other entry under [Dir] is deleted: an asset whose content changed took a new name, and the old name is a file no page references and every deploy would otherwise carry forever. A framework theme's payload is a directory, and is pruned whole for the same reason.
themeNames may repeat and may arrive in any order; the assets are written in sorted order over the distinct names.
#SiteRootPrefix
func SiteRootPrefix(pageRel string) stringSiteRootPrefix is the hop from a site-relative page back to the site root.
"" at the root, "../" one level in. The same fact the shared pages already carry as their search prefix: references are relative because the assembled tree has to resolve under any mount point, so a site-level asset is addressed by hopping out rather than by a leading slash.
#Href
func Href(pageRel, assetRel string) stringHref is the reference a page at pageRel writes to reach the chrome asset at assetRel.
#PageTheme
func PageTheme(pageRel string, bySlug map[string]string, homeTheme string) stringPageTheme is which theme's asset a page at pageRel references.
A page inside a project's subtree gets that project's theme. Everything else -- the home project's pages at the site root, the site-level blog, and the assembly's own shared pages -- gets the home project's: those addresses belong to the site rather than to one project, and the site reads as the front page reads.
#IsReference
func IsReference(ref string) boolIsReference reports whether ref is a page's reference to its page-chrome stylesheet.
Two spellings are recognised, because both are on a live site at once: a project build's own "style.css", relative to the page, and an earlier deploy's site-level asset under [Dir]. A custom stylesheet is neither and is left where it is -- "custom.css" is the project's content, not the chrome the assembly owns.
#RepointPage
func RepointPage(pageHTML, pageRel, assetRel string) stringRepointPage returns pageHTML with every chrome reference aimed at assetRel.
#RepointPages
func RepointPages(siteDir string, pages []string, bySlug map[string]string,RepointPages re-points every page named at the site-level chrome asset and returns the site-relative paths that actually changed, so a caller can say what a deploy touched.
pages are site-relative HTML paths and assets is the theme-to-path mapping [WriteAssets] returned.
A page whose theme has no asset is a page whose project declares a theme the assembly did not emit, and that is a hard error rather than a page left pointing at a file the graft no longer serves.
#EmittedPages
func EmittedPages(siteDir string) ([]string, error)EmittedPages is every ".html" file under siteDir, site-relative and sorted.
A siteDir that does not exist has emitted nothing, and is reported as the empty list rather than as an error: that is what the walk this replaces did, and the deploy calls it before it knows whether any project has grafted yet.