Page Anatomy
Point at any region of a finished site and this page names the component that owns it. Every part below ships — the most expensive mistake a new Espalier project makes is rebuilding one of them from divs. If you are about to write a header, a footer, a light/dark toggle, or a reading-width container: stop, it exists.
The map
┌─ <esp-root> ──────────────────────────────── (invisible: theme + tokens) ─┐
│ ┌─ <esp-page> ─────────────────────────────────── (the layout shell) ───┐ │
│ │ ┌─ slot="header" → <esp-header> ────────────────────────────────────┐ │ │
│ │ │ topbar (optional row) │ │ │
│ │ │ brand │ slot="menu" → <esp-menu mode="horizontal"> │ buttons ●○ │ │ │
│ │ └───────────────────────────────────────────────────────────────────┘ │ │
│ │ ┌─────────────┬─ main (default slot) ─────────────┬─────────────────┐ │ │
│ │ │ │ the content well │ │ │ │
│ │ │ slot= │ ┌─ <esp-section context="…"> ─┐ │ slot="right" │ │ │
│ │ │ "sidebar" │ │ a color band + its well │ │ (right rail) │ │ │
│ │ │ │ └─────────────────────────────┘ │ │ │ │
│ │ │ │ <esp-stack>/<esp-row> flow, │ │ │ │
│ │ │ │ <esp-box> cards, <esp-form> … │ │ │ │
│ │ └─────────────┴───────────────────────────────────┴─────────────────┘ │ │
│ │ ┌─ slot="footer" → <esp-footer> ────────────────────────────────────┐ │ │
│ │ │ media / brand │ link-group │ link-group │ link-group │ aside │ │ │
│ │ │ bottom (legal line) │ │ │
│ │ └───────────────────────────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────────────────┘
Region by region
| You are pointing at… | Component | Import |
|---|---|---|
| Nothing visible — the theme, tokens, scheme | esp-root |
@taprootio/espalier/root |
| The whole layout: header/main/footer grid, content cap, gutters | esp-page |
@taprootio/espalier/page |
| The bar across the top: brand, nav, buttons, light/dark toggle | esp-header |
@taprootio/espalier/header |
| The site name / logo in that bar | esp-header's brand-text / brand-logo attributes (per-scheme variants included) |
— |
| The horizontal navigation | esp-menu mode="horizontal" in the header's menu slot |
@taprootio/espalier/menu |
| Icon buttons at the bar's end | esp-header-button in the buttons slot |
@taprootio/espalier/header |
| The light/dark toggle | Built into esp-header — set theme-toggle="visible" |
— |
| The hamburger + slide-out drawer on small screens | esp-header show-burger with drawer-target pointing at a drawer-capable esp-menu (the target must implement openDrawer()/closeDrawer()) |
@taprootio/espalier/menu |
| The centered column your content lives in | esp-page's default slot — the content well, capped by kind (wide 1536px, narrow 768px, full uncapped, site full-width section stacks) |
— |
| A full-width marketing page of stacked bands | esp-page kind="site" — the main region spans the viewport; sections center their own wells on --esp-page-well-max-width. The sidebar/right rails are unsupported here (they would split the shared column); use kind="wide" for railed layouts |
— |
| A comfortable reading column | esp-page kind="narrow" — constrains slotted children to a 66ch measure |
— |
| A left navigation column | esp-page's sidebar slot |
— |
| A right rail | esp-page's right slot |
— |
| A full-width color band (a "zone") | esp-section — paints the local canvas edge to edge, centers its well, and hosts a context with nothing to neutralize |
@taprootio/espalier/section |
| A column of content with themed gaps | esp-stack — fields fill the column, buttons keep their natural width |
@taprootio/espalier/stack |
| A wrapping row: buttons, badges, icon-label pairs | esp-row |
@taprootio/espalier/row |
| A card / panel with its own raised surface | esp-box — this is what it is for |
@taprootio/espalier/box |
| The columned link area at the bottom | esp-footer with esp-footer-link-group children (and esp-footer-column to pin groups together) |
@taprootio/espalier/footer |
| The legal line under the columns | esp-footer's bottom slot |
— |
The parts people rebuild, and why they shouldn't
The header is not a styled div. esp-header carries the brand (text or
logo, with per-scheme light-brand-logo / dark-brand-logo variants), the
menu and buttons slots, scroll behaviors (compact, elevate,
transparent, reveal, dock, and more), the mobile burger + drawer
wiring, and the built-in scheme toggle. The toggle flips the nearest
esp-root and emits esp-header-theme-toggle only for a real visitor activation,
so persisting the choice is one listener:
document.querySelector("esp-root").addEventListener("esp-header-theme-toggle", (event) => {
localStorage.setItem("scheme", event.detail.scheme);
});
The footer is not a grid you write. esp-footer columns="3" lays out
slotted esp-footer-link-group elements (plain <a> children — each group
is a labelled navigation landmark), with media, brand, aside, and
bottom slots around them.
Alignment is a contract, not a coincidence. Slot esp-header and
esp-footer directly into esp-page's named slots and the page hands them
its resolved content cap and alignment as custom properties
(--esp-header-content-max-width and friends) — the bar backgrounds stay
full-bleed while their content aligns with the well. A wrapper element
between page and header/footer breaks that handshake.
The measure container ships. esp-page kind="narrow" caps the well at
768px and constrains slotted children to a 66ch reading measure. For one
narrow passage inside a wide page, prefer a nested element with
max-inline-size: 66ch over rebuilding the page shell.
Bands are sections
A full-width color band is a theme zone: a
context applied to a
container, which re-derives and re-enforces the full token table for that
subtree. The host is esp-section: it paints the
local canvas edge to edge, centers its content in a well, and carries no
card identity — no raised surface, padding, or shadow to neutralize:
<esp-section context="inverted">
<h2>A reversed band</h2>
<p>Everything in here — including plain light-DOM content — renders on
the zone's tokens.</p>
</esp-section>
A band can also paint one decorative raster mask behind its well. Set
--esp-section-decoration-image plus optional color, position, size, and
opacity hooks; esp-section clips the plane, prevents repetition and pointer
capture, and keeps content above it. The default tint is the context-local
heading color, so one alpha asset can follow normal and inverted zones. Treat
the image value as trusted stylesheet input and validate any externally
supplied URL before assigning it.
Inside esp-page kind="site" sections run edge to edge and share one
content column with the header and footer (--esp-page-well-max-width);
inside the capped kinds a band spans the content well. (Before
esp-section shipped, the recipe was a neutralized esp-box — replace
those with a bare section.) Keep a reversed band's canvas out of the
mid band — a canvas near L 0.45 sits exactly where the zone's own
filled buttons are placed, and swallows them. The mechanism and the fix
are in the theming guide.
Where to go next
- Build a Site — the end-to-end path from brand book to themed page using every part named here.
- Theming — anchors, roles, contexts.
- The Lightness Ramp — seating brand swatches on the ramp.