core · Current reference
Native CSS and shared fonts
Portable source contracts, adoption guidance and compatibility limits for native css and shared fonts.
Applies to v1.1.21
Component pins for v1.1.21
| Component | Role | Version |
|---|---|---|
| core | runtime | v1.1.21 |
| theme-shield | theme | v0.1.17 |
| docs | reference | v0.1.6 |
| distribution | tooling | v0.3.28 |
Core v1.1.21 exposes ordered Hugo resources so a site can bundle its styles
without copying the framework’s asset list. The normal build uses Hugo Extended
0.165.0 and Go 1.27. No browser, Control service, font compiler or Node runtime
is required. Existing css.html, theme and override calls remain compatible.
Human path
With Shield v0.1.17, opt into the whole-head CSS collector in site configuration:
params:
foundry:
assets:
css_mode: bundled
font_profile: shared-full
CSS and fonts are independent options. Omit font_profile to retain the small
attribution-subsets default. Shield defaults to css_mode: individual; its
legacy discovery behavior is unchanged. The explicit bundled path calls the
Core partial directly and does not depend on that legacy discovery guard.
Unknown Shield modes and attribution font profiles fail the build.
A custom shell can call {{ partial "foundry/head/styles.html" . }} once where
its styles belong. This replaces its calls to css.html, theme CSS and
foundry/head/site-overrides-css.html; calling both paths duplicates styling.
Keep SEO, scripts, image preloads and unrelated head hooks in their own places.
Add the return-only site hook at
layouts/partials/site/foundry-styles.html (or Hugo’s _partials/site/ form):
{{- $styles := slice (dict "id" "site-brand" "resource" (resources.Get "css/brand.css")) -}}
{{- if .Params.gallery -}}
{{- $styles = $styles | append (dict "id" "site-gallery" "resource" (resources.Get "css/gallery.css")) -}}
{{- end -}}
{{- return $styles -}}
Use the site’s real conditions and order. An absent hook means no page bundle.
The hook is a site extension, outside the Core partial namespace; no shadow
allowlist or copy of Core internals is needed. Each entry needs a unique id
and a real Hugo CSS resource. Missing or duplicate inputs fail clearly.
Cascade and identity contract
foundry/assets/core-styles.html returns ordered descriptors for syntax, base,
conditional theme-family CSS, motion, flow, creative, search, forms and the
existing optional processed main stylesheet. It emits no markup. The legacy
css.html uses foundry/assets/individual-styles.html to retain individual
resource paths, development behavior and production minification/integrity.
The legacy optional Tailwind compatibility transform is retained; selecting it
is outside the native Node-free path and remains subject to the site’s tooling
policy. This release does not install or execute that toolchain.
foundry/assets/collect-styles.html returns two groups, each with id and an
ordered styles slice. foundation is Core, optional css/theme.css, then
nonempty css/foundry-overrides.css. page is exactly the site hook’s entries.
foundry/assets/bundled-styles.html emits one blocking stylesheet per nonempty
group in that order. Every link has fingerprinted URL, integrity, anonymous
crossorigin, data-foundry-styles and ordered data-style-sources attributes.
No asynchronous CSS, coverage pruning or motion changes occur. Feature partials
such as appearance/preferences keep their existing demand-driven CSS emitters;
the collector replaces the common css.html/theme/override chain, not every
stylesheet that any shortcode or helper may emit. Such pages can legitimately
have more than two stylesheet links. Do not pull all optional helper CSS into
every foundation merely to obtain a fixed request count.
Targets contain the SHA256 of ordered resource names and content hashes before
Hugo concatenation. Foundation can be cached across routes while page or language
variants cannot collide merely because they share descriptor IDs. Hugo’s
resources.FromString also caches by target: site-generated resources must use
a content/variant-specific target if their contents differ within one build.
Bundling does not rebase relative URLs. Supply fingerprinted resource
RelPermalink values or root-relative, fragment, data or absolute URLs. Relative
url(...) values and unresolved @import, @charset, @namespace directives
are rejected in the bundled path. Resolve imports and font/image resource URLs
before returning the resource. The legacy path is unchanged. These checks are
conservative and may also reject such syntax inside CSS comments; remove those
comments or prepare a bundle-safe resource. Do not change public cache/security
policy merely to adopt this output.
One font registry, two delivery profiles
foundry/assets/font-registry.html returns a map keyed by the original filename
stem: Newsreader72pt-Regular, Newsreader72pt-Italic, SourceSans3-Regular
and SourceSans3-Semibold. Each entry preserves the metadata in
assets/fonts/foundry-attribution/delivery.json and adds id, resource
(fingerprinted full original) and subset_resource (fingerprinted derivative).
Accessing a resource object alone does not publish it; using its permalink does.
There is no second font manifest and no changed font/licence bytes.
Brand CSS can use full originals through foundry/assets/font-face.html:
{{- $face := partial "foundry/assets/font-face.html" (dict "id" "Newsreader72pt-Regular" "family" "My Display") -}}
{{- $css := printf "%s\nh1{font-family:'My Display',Georgia,serif}" $face -}}
{{- $resource := resources.FromString "css/site-type.css" $css -}}
{{- return (slice (dict "id" "site-type" "resource" $resource)) -}}
The helper emits a full original face with its verified weight/style and
font-display:swap. It never substitutes the phrase subsets for brand text.
Use the registry’s resource.RelPermalink for a justified site-owned preload;
no new preloads are automatic. Fonts not in this registry remain site-owned.
| Profile | Attribution delivery | Intended use |
|---|---|---|
attribution-subsets (default) | Existing disjoint subset/original ranges; original Sans for dynamic details | Sites using other brand fonts; three closed-footer requests total 37,732 bytes, under 40,000-byte budget |
shared-full (opt-in) | Original full masters, isolated attribution family aliases, same URLs as brand helper | Sites already using these original typefaces; avoids adding separate phrase subset requests |
Default expanded details intentionally add original Sans Regular, for a combined 147,364 bytes. Full profile’s three ordinary footer styles total 205,708 bytes; all four original masters total 314,608 bytes if the page also uses Semibold. CSS declarations do not mean every face downloads. Full profile can increase bytes on a site using other fonts, and is not the default. Both profiles retain one original Sans face for arbitrary dynamic values and multilingual/combining shaping. Attribution keeps its own CSS family aliases and appearance isolation; brand styling does not inherit into it. Identical URLs, not alias spelling, provide browser resource reuse. No scripted font loader, data URI or remote font service is involved.
Migration and proof
Replace a consumer’s repeated Core/theme/override list with the whole-head call, then return only its own conditional CSS. Replace copied font URL assembly with the registry or full-face helper. Keep other site fonts, brand rules and motion policy. Check emitted resource order, page variants, startup layout shift, no-JavaScript content, reduced motion, menu/focus and attribution details at mobile, desktop and reflow widths before deployment. A successful build or a shared release alone does not prove a consumer’s live performance.
Visual Change Protocol governs shared changes; Consumer Integration Quality Gate governs adoption. Foundry Control can add managed review, publishing and care around this portable source contract; normal Hugo builds remain independent of it.