Site shell contract
Configure the shared Foundry header, navigation, footer, and shell behavior.
- Owner
- Foundry Core
- Source
core/docs/SHELL.md- Ref
v0.1.24- Policy
- release-bound
Component pins for v0.2.6
| Component | Role | Version |
|---|---|---|
| core | runtime | v0.1.24 |
| theme-shield | theme | v0.1.15 |
| starter | reference | v0.1.3 |
| docs | reference | v0.1.4 |
| distribution | tooling | v0.3.4 |
| restricted-component-1 | consumer-reference | v0.1.22 |
| restricted-component-2 | consumer-reference | v1.1.21 |
Phase: 08-shell-contract-governance Status: Stable (v1)
The Foundry shell contract defines how downstream themes and sites configure site chrome — header, navigation, footer, utility surfaces, and page-level exceptions — without copying shell partials locally.
Why a Shell Contract?
Without a contract, every site ends up with:
- Local copies of
layouts/partials/header/,layouts/partials/footer/, andlayouts/_default/baseof.html - Bespoke template conditions scattered across dozens of partials
- No machine-readable ownership model → AI editors can’t safely touch shell code
- Governance by tribal knowledge rather than validated structure
The shell contract moves this behaviour into typed, schema-backed Core ownership that downstream repos configure rather than copy.
Artifacts
| File | Purpose |
|---|---|
schemas/foundry/shell-manifest.schema.json | Typed contract for all shell surfaces |
schemas/foundry/page-shell.schema.json | Page-level chrome exception fields |
layouts/partials/helpers/page-shell-params.html | Hugo partial that normalizes page front matter into the contract |
scripts/validate-shell-contract.js | Validates Core contract artifacts are internally consistent |
scripts/validate-shell-overrides.js | Audits downstream repos for unallowlisted shell shadows |
Shell Surfaces (shell-manifest)
The shell manifest covers these surfaces:
| Surface | Required | Description |
|---|---|---|
brand | ✓ | Site name, logo assets, home URL |
nav | ✓ | Primary navigation (menu-driven or explicit links) |
utility_nav | — | Secondary nav (language, preferences, auth) |
search | — | Search entry point |
preferences | — | Language/theme/locale toggles |
announcement | — | Top-of-page announcement banner |
primary_cta | — | Header call-to-action button |
footer | — | Footer columns, legal nav, contact, social |
Typed Link Kinds
All link entries in the manifest use a typed kind field:
| Kind | Meaning |
|---|---|
internal | Site-local link (Hugo relURL) |
external | Off-site link (opens with explicit rel) |
anchor | On-page anchor (#section-id) |
menu-driven | Resolved from Hugo menus at build time |
route-owner | Points to the canonical owner of a content section |
Page-Level Chrome Exceptions (page-shell)
Pages declare shell behaviour through front matter fields that are normalized by helpers/page-shell-params.html:
# content/some-page/_index.md
---
title: Landing Page
header_mode: transparent # overlays first content block
footer_mode: minimal # legal links only
hide_site_chrome: false # true = full chrome suppression
route_owner: consult # canonical section key
utility_surface: cta-only # show only primary CTA in header
show_breadcrumbs: true # render visible breadcrumbs when shell supports it
noindex: false
suppress_announcement: false
shell_compat_mode: false # set true only during phased migration
---
Field Reference
| Field | Type | Default | Values |
|---|---|---|---|
hide_site_chrome | bool | false | Suppresses header + footer |
header_mode | string | "default" | default, transparent, minimal, hidden |
footer_mode | string | "default" | default, minimal, hidden |
announcement_key | string | — | Maps to data/announcements/{key}.yaml |
suppress_announcement | bool | false | Hides site-level announcement on this page |
route_owner | string | — | Content section key (e.g. consult, blog) |
utility_surface | string | "default" | default, none, cta-only, search-only |
show_breadcrumbs | bool | false | Opts the page into visible breadcrumbs |
breadcrumb_home_label | string | site home title | Optional home crumb label |
breadcrumb_label | string | "Breadcrumb" | Accessible nav label |
breadcrumb_include_current | bool | true | Includes current page as final crumb |
noindex | bool | false | Adds <meta name="robots" content="noindex"> |
shell_compat_mode | bool | false | Enables legacy param resolution during migration |
Using the Helper Partial
In your base layout or shell partial, resolve page-shell params once at the top:
{{/* layouts/_default/baseof.html */}}
{{ $shell := partial "foundry/helpers/page-shell-params.html" . }}
{{ if not $shell.hide_site_chrome }}
{{ partial "header/site-header.html" (dict "page" . "shell" $shell) }}
{{ end }}
{{ block "main" . }}{{ end }}
{{ if not $shell.hide_site_chrome }}
{{ partial "footer/site-footer.html" (dict "page" . "shell" $shell) }}
{{ end }}
The helper handles:
- Reading typed contract fields from
.Params - Applying defaults for missing fields
- Legacy compat: falls back to older param names during migration
Visible Breadcrumbs
Foundry already emits breadcrumb structured data through SEO schema. For visible page breadcrumbs, call the shared partial from the shell where the site wants breadcrumbs to appear:
{{ partial "foundry/navigation/breadcrumbs.html" (dict "page" . "shell" $shell) }}
The partial renders only when show_breadcrumbs: true and when the current page has more than one crumb. It uses Hugo ancestors, respects breadcrumb_home_label, and marks the current page with aria-current="page".
Ownership Model
| Layer | Owns |
|---|---|
| Foundry Core | Shell schemas, typed surface definitions, validators, helper partial |
| Theme | Shell variant implementations (transparent header, minimal footer layouts) |
| Site | Shell configuration (menu keys, CTA copy, contact info, route priorities) |
Sites configure shell behaviour through the contract. They do not copy shell partials unless:
- The needed behaviour cannot be expressed through the contract
- The shadow is documented in
.planning/shadow-copy-review.mdwith a reason and migration phase - The shadow passes the override validator with an explicit allowlist entry
Validation
# 1. Validate Core contract artifacts
cd /path/to/likestyle-foundry-core
node scripts/validate-shell-contract.js
# 2. Audit a downstream repo for shell shadows
node scripts/validate-shell-overrides.js /path/to/downstream-repo
# 3. With allowlist (for documented temporary shadows)
node scripts/validate-shell-overrides.js /path/to/downstream-repo \
--allowlist /path/to/downstream-repo/.planning/shell-shadow-allowlist.json
Shell Shadow Migration Path
If a downstream repo has temporary shell shadows (common during initial adoption):
- Document each shadow in
.planning/shadow-copy-review.mdunder “Shell Shadows” with a reason and target phase. - Create
.planning/shell-shadow-allowlist.jsonlisting the shadow paths. - Run
validate-shell-overrides.js --allowlistin CI to catch any new unreviewed shadows. - Retire the shadow in the designated phase by adopting the Core contract field or theme variant.
- Remove the allowlist entry and confirm the validator exits 0.
Compatibility Mode
During migration, set shell_compat_mode: true in page front matter to enable legacy param resolution alongside the new contract. This allows incremental per-page migration without a big-bang rewrite. Remove shell_compat_mode once all pages in a section are migrated.
What This Phase Does Not Do
- Move high-consideration service proof site route labels, CTA copy, or contact info into Core defaults
- Implement final header/footer rendering for any specific theme
- Expand into runtime personalization or non-shell product work
- Promote any shell primitive to “shield” status before a second consumer proves genericity
These are targets for Phase 09 (Theme Capability & Shared Shell Rendering) and Phase 10 (high-consideration service proof site Pilot & Exception Retirement).