registry:ui · Layout
website-scaffold — the grid shell
The layout root of every page on this site — including this one. A named container host whose ONE grid both layers subgrid (columns shared, placement by named areas), forms driven by container queries on the host's own width, per-zone immersive motion, the boot splash seat, and systematized view transitions. The strictest prop contract in the fleet: four snippet seams, eight axes, no rest.
$npx jixoai-ui add website-scaffold
https://ui.jixoai.com/r/website-scaffold.json
overview
Overview — the shell documenting itself
The self-reference at maximum: this page LIVES in the shell it documents. Every claim names its surface — the LIVE shell (this page's own chrome) or a measured demo instance — and the theme strata terminate here, because the scaffold hosts them.
The architecture is four names. .jx-shell-host is
the named container (container-type: inline-size, container-name: jx-shell) — the ONE
responsive truth source: forms switch on ITS width, so the scaffold is embeddable (a
narrow host shows the narrow form inside a wide window — measured: a 480px instance ran
the single-column form while this page ran the three-column one). .jx-shell is one grid; both layers overlap by
spanning all columns of row 1 (line placement — never grid-template-areas on the
shell). .jx-top-layer is the click-through
chrome plane: pointer-transparent, children opt back in, per-zone immersive transforms
— the plane itself never moves. .jx-shell-body spans the whole shell as THE scroll container, reserving chrome via padding driven by
the single measured var --jx-header-h.
Three forms, container-queried on the host: at 1200px+ the grid is
[rail 16rem][content 1fr][toc 15rem] (tree nav rail); at 900–1199 the tree folds to a
bottom bar; below 900 the tocbar row slides under the header. The named areas move
with them — measured: header header header / tree
stage toc wide, header header / stage toc mid, header tocbar stage narrow — the named-area
budget every adopted node places into. The immersive law is per-zone and
scroll-position-driven (an 8px threshold, rAF-throttled): scroll DOWN and the header
leaves upward (measured translateY −74.74px — the header-h itself); scroll UP and
everything returns; the reading rails NEVER leave — they compact by the header height.
The prop contract is the fleet's strictest: FOUR snippet seams (header, chrome,
children, footer) plus an optional splash seat and the eight axes — and NOTHING else.
No rest spread exists: consumers author their chrome in the seams, static chrome nodes
carry data-area roles (toc | tree), and dynamic
adoption rides the jx-top-layer context
(adopt(node, area) — ordered, release fn
returned, teardown the consumer's job). Theme is a declarative stamp over the page
ground (measured flip); density/size/shape/radius/color/elevation/motion are the
no-own container's supply lanes. Kinship: terminal-header, toc and terminal-footer compose INTO this shell — the
docs pages for each live inside it.
website-scaffold
This page is already the running instance — the canvas below holds the architecture diagram, and the playground drives the REAL top layer you are reading through.
architecture · no nested LIVE instance
This page is already the demo
You are reading main#main inside .jx-shell-body right now — the nav band above is .jx-top-layer. A second scaffold cannot nest here without trapping the page in two competing scroll planes, so the LIVE area holds the architecture; use the Playground buttons to drive the real top layer.
.jx-shell-host container: jx-shell (inline-size) · 100dvh
└── .jx-shell ONE grid · cols [rail][content][toc] per form
├── .jx-top-layer grid-row 1 · col-subgrid · rows [header][tocbar][stage]
│ ├── .jx-scaffold-header the nav band (visible above)
│ └── .jx-float-slot subgrid — adopted nodes land by [data-area]
└── .jx-shell-body grid-row 1 · col-subgrid · THE scroll container
├── main#main [content] column · padding = --jx-header-h (+tocbar)
└── footer in the content flow - one measurement — a single ResizeObserver publishes
--jx-header-h; the body's chrome reservation, the toc compaction offset and the shared--jx-toc-lineall derive from it in CSS. Disclosure rows re-reserve automatically. The correction lands on .jx-shell — the var's DECLARING scope (a host-level write is shadowed for every descendant; the toc-flush bug) - per-zone immersive law — scroll DOWN past 8px: the header leaves upward and the tree bottom bar slides down; the toc and the catalog rail NEVER leave — they compact by exactly the header height (Owner ruling, 2026-08-24); the slightest scroll UP returns everything; reduced motion swaps to instant. Measured: translateY(−74.74px) = the header-h itself
- view transitions — the header band keeps view-transition-name
site-headerand persists across navigations; main#main animates aspage-main(measured live) - skip link — a keyboard-reachable
Skip to contenttarget on #main, hidden until focused - top-layer context — the scaffold publishes the
jx-top-layercontext:adopt(node, area)with semantic roles (toc | tree | float); the shell grid resolves the physical cell per container form — one adoption mechanism, many cells, zero per-breakpoint JS (Owner request, 2026-08-23)
<script lang="ts">
import WebsiteScaffold from '@ui/website-scaffold.svelte';
import '@ui/website-scaffold/website-scaffold.css';
import TerminalFooter from '@ui/terminal-footer.svelte';
import TerminalHeader from '@ui/terminal-header.svelte';
import { NavigationMenu, NavigationMenuLink } from '@ui/navigation-menu/index';
</script>
<!-- the layout root: one named container, two subgrid layers -->
<WebsiteScaffold>
{#snippet header()}
<!-- chrome-only header: the nav is composed from the family -->
<TerminalHeader brand="my-app">
<NavigationMenu label="primary">
<NavigationMenuLink href="/">home</NavigationMenuLink>
<NavigationMenuLink href="/docs" current>docs</NavigationMenuLink>
</NavigationMenu>
{#snippet drawer()}
<!-- the mobile tier's nav, authored in your tree -->
{/snippet}
</TerminalHeader>
{/snippet}
<!-- STATIC chrome (SSR-stable): toc rails / nav trees authored in
their final grid cells — never moved by hydration -->
{#snippet chrome()}
<MyPageToc data-area="toc" />
<MyCatalogTree data-area="tree" />
{/snippet}
<!-- default snippet → main#main in the content column -->
<RouterOutlet />
{#snippet footer()}
<TerminalFooter ghost="MY-APP" />
{/snippet}
</WebsiteScaffold>W3C foundation
What the platform gives
Container queries size the forms on the host's own width (embeddability without JS); subgrid shares the columns so chrome and content can never drift; :has() targets chrome from the content side; view transitions persist the chrome across navigations. The scaffold composes these four platform powers into one grid.
<script lang="ts">
import WebsiteScaffold from '@ui/website-scaffold.svelte';
import '@ui/website-scaffold/website-scaffold.css';
import TerminalFooter from '@ui/terminal-footer.svelte';
import TerminalHeader from '@ui/terminal-header.svelte';
import { NavigationMenu, NavigationMenuLink } from '@ui/navigation-menu/index';
</script>
<!-- the layout root: one named container, two subgrid layers -->
<WebsiteScaffold>
{#snippet header()}
<!-- chrome-only header: the nav is composed from the family -->
<TerminalHeader brand="my-app">
<NavigationMenu label="primary">
<NavigationMenuLink href="/">home</NavigationMenuLink>
<NavigationMenuLink href="/docs" current>docs</NavigationMenuLink>
</NavigationMenu>
{#snippet drawer()}
<!-- the mobile tier's nav, authored in your tree -->
{/snippet}
</TerminalHeader>
{/snippet}
<!-- STATIC chrome (SSR-stable): toc rails / nav trees authored in
their final grid cells — never moved by hydration -->
{#snippet chrome()}
<MyPageToc data-area="toc" />
<MyCatalogTree data-area="tree" />
{/snippet}
<!-- default snippet → main#main in the content column -->
<RouterOutlet />
{#snippet footer()}
<TerminalFooter ghost="MY-APP" />
{/snippet}
</WebsiteScaffold>law
The grid law
Why one grid instead of absolute positioning: the two layers share the SAME named columns through subgrid, so chrome and content can never drift apart — and forms are container queries on the host, so an embedded scaffold shows the right form at any width. Placement is declarative; motion stays per-zone transforms.
- ONE scroll plane:
.jx-shell-bodyowns every scroll on the site — window scrolling and fixed-position demos must target it, not the window - ONE grid, two layers: both layers span the same shell grid and subgrid its columns — the toc rail and the content column align by construction, and adopted nodes land by [data-area] role
- THE OVERLAY POINTER LAW (adjudicated D-1 fix): overlay containers are pointer-TRANSPARENT; real content opts back in — and the grant STOPS at float wrappers, so an adopted float covering the stage can never shield the page beneath (measured: header pointer-events auto, the float slot none)
- STATIC chrome and DYNAMIC floats never mix: the toc rail and catalog tree render in the chrome snippet (SSR-stable, never moved by hydration); adopted float nodes land in .jx-float-slot through the context, in adoption order
types
Types
One shell, four snippet seams, three container-query forms — the same grid re-grids per width.
[rail 16rem][content 1fr][toc 15rem] — measured 256 / 1104 / 240 at a 1600 viewport; the catalog tree joins the top layer.
[content 1fr][toc 15rem] — the tree folds to a bottom bar; the toc rail keeps its column.
[content 1fr] + the tocbar row under the header — the glass single-row rail's home (the toc family's mobile surface).
usage
Usage
The layout root: one named container, header/chrome/children/footer snippets in their final cells.
<script lang="ts">
import WebsiteScaffold from '@ui/website-scaffold.svelte';
import '@ui/website-scaffold/website-scaffold.css';
import TerminalFooter from '@ui/terminal-footer.svelte';
import TerminalHeader from '@ui/terminal-header.svelte';
import { NavigationMenu, NavigationMenuLink } from '@ui/navigation-menu/index';
</script>
<!-- the layout root: one named container, two subgrid layers -->
<WebsiteScaffold>
{#snippet header()}
<!-- chrome-only header: the nav is composed from the family -->
<TerminalHeader brand="my-app">
<NavigationMenu label="primary">
<NavigationMenuLink href="/">home</NavigationMenuLink>
<NavigationMenuLink href="/docs" current>docs</NavigationMenuLink>
</NavigationMenu>
{#snippet drawer()}
<!-- the mobile tier's nav, authored in your tree -->
{/snippet}
</TerminalHeader>
{/snippet}
<!-- STATIC chrome (SSR-stable): toc rails / nav trees authored in
their final grid cells — never moved by hydration -->
{#snippet chrome()}
<MyPageToc data-area="toc" />
<MyCatalogTree data-area="tree" />
{/snippet}
<!-- default snippet → main#main in the content column -->
<RouterOutlet />
{#snippet footer()}
<TerminalFooter ghost="MY-APP" />
{/snippet}
</WebsiteScaffold>theming
Theming
No density tokens — the shell publishes structural measurements; everything else derives from them in CSS. The theme stamp is declarative: the shell IS the page ground, and the canvas stage's light-island pin (the host layer) lives one family over.
| Token | Default | Source |
|---|---|---|
| --jx-header-h | 58px / 74px per form (RO-corrected) | structural |
| --jx-rail-w | grid rail width | structural |
| --jx-toc-w | toc column width | structural |
| --jx-toc-line | 76px :root standalone · 106px shell-derived | structural |
| --jx-chrome-bar | 44px | structural |
| --jx-chrome-top / --jx-chrome-bottom | zone insets | structural |
api
API
The strictest contract in the fleet: FOUR snippet seams (header, chrome, children, footer) plus the splash seat and the eight axes — and NOTHING else. The scaffold is SNIPPET-COMPOSED AND REST-LESS: no rest spread exists, so nothing consumer-authored lands on the shell except through the seams. Placement for consumer chrome rides data-area roles (static) or the adopt() context (dynamic floats — ordered, release fn, consumer teardown). The eight axes are a FIRST-TIME all-no-own container contract (the universal fold serves the rows).
Properties
| Property | Type | Default | Description |
|---|---|---|---|
| header * | Snippet | — | The nav band chrome (usually terminal-header with composed nav). |
| splash | Snippet | — | The BOOT SPLASH seat: rendered at the HOST ROOT, above every layer — the splash’s own styles ride the HTML so it paints before any async stylesheet (the FOUC mask). |
| chrome | Snippet | — | Static chrome, SSR-stable: the toc rail and catalog tree render here, never moved by hydration. |
| children * | Snippet | — | The default snippet → main#main in the content column. |
| footer | Snippet | — | Optional footer in the content flow. |
| TopLayerApi (context) | adopt(node, { area }) → release | — | Dynamic float adoption into the top layer — ordered, multi-node, semantic areas (toc | tree | float); consumers keep node ownership. |
Universal props
| Property | Type | Default | Description |
|---|---|---|---|
| size | 'small' | 'medium' | 'large' | 'auto' | number | 'auto' · ambient scope | the base scale — root font-size; parts size in em |
| shape | 'round' | 'scoop' | 'bevel' | 'notch' | 'square' | 'squircle' | 'auto' | 'auto' · ambient scope | corner geometry (CSS corner-shape; §14 degrade table) |
| radius | 'small' | 'medium' | 'large' | 'auto' | number | 'auto' · ambient scope | corner size; auto = the concentric broadcast (§3) |
| density | 'small' | 'medium' | 'large' | 'auto' | number | 'auto' · ambient scope | spacing/leading scale over the kernel channels (§4) |
| color | 'primary' | 'secondary' | 'error' | 'warn' | 'success' | 'info' | 'auto' | number | string | 'auto' · ambient scope | the hue axis of the fixed oklch primary system (§5) |
| theme | 'light' | 'dark' | 'system' | 'auto' | 'auto' · ambient scope | light/dark profile; system = the JS-mutable global (§6) |
| elevation | 'level-1' | 'level0' | 'level1' | 'level2' | 'level3' | 'level4' | 'level5' | 'auto' | number | 'auto' · ambient scope | official M3 levels over the surface ladder (§7) |
| motion | 'reduced' | 'subtle' | 'normal' | 'expressive' | 'auto' | number | 'auto' · ambient scope | intensity across the motion kernels (§8) |
axes
The eight axes on website-scaffold
The FIRST-TIME all-no-own CONTAINER contract, measured on the live shell: density stamps nothing at auto (the no-opinion law made visible); size is the type-scale seam at the top (every zone supplies downward); theme is the declarative stamp over the page ground — the strata's host, not a member; shape/radius/color/elevation/motion supply unread (the immersive leaves are transforms, the count readout discipline lives in the fields).
| Property | Type | Default | Description |
|---|---|---|---|
| density | '2xs' | 'xs' | 'sm' | 'default' | 'lg' | 'auto' | number (+ the five legacy spellings) | 'auto' | NO-OPINION CONTAINER, MEASURED ABSENT — the zones carry no density paint, and at auto the rung attr stamps NOTHING on the host (measured: data-density absent on the live shell — the no-opinion law is visible, not just declared). Zero --jx-density-effective readers (grep receipt). Number unit: coefficient. |
| size | 'small' | 'medium' | 'large' | 'auto' | number | 'auto' | THE TYPE-SCALE SEAM AT THE TOP — the §1 stamp scales the HOST root and every zone supplies downward: the header, the toc rail and the page content are the consumers (吃也供 — the site-level type-scale seam: one number scales the whole scaffold). Zero --jx-size-effective readers (grep receipt). Number unit: px. |
| shape | 'round' | 'scoop' | 'bevel' | 'notch' | 'square' | 'squircle' | 'auto' | 'auto' | SUPPLY-ONLY — zero shape-channel readers (grep receipt: no corner-shape or factor consumer in ui/website-scaffold/); the shell is structural chrome. Number unit: none. |
| radius | 'small' | 'medium' | 'large' | 'auto' | number | 'auto' | SUPPLY-ONLY — zero radius-effective readers (grep receipt); the shell owns no decorative corners (the zones are full-bleed structural bands). Number unit: px. |
| color | 'primary' | 'secondary' | 'error' | 'warn' | 'success' | 'info' | 'auto' | number | string | 'auto' | SUPPLY-ONLY — the zones paint the theme profile directly (var(--border)/var(--background)/the --jx-* inks); zero --jx-color-effective readers (grep receipt). Number unit: hue degrees. |
| theme | 'light' | 'dark' | 'system' | 'auto' | 'auto' | DECLARATIVE STAMP — THE HOST OF HOSTS. theme="dark" stamps the .dark class on the shell-host, and the shell IS the page ground: measured, the body ground flips oklch(1 0 0) → oklch(0 0 0) under the page bridge. The theme story’s strata terminate here — the canvas stage’s data-theme="light" + .jx-light island (the host layer that pins demo subtrees, measured on the sibling lanes) is stamped by the COMPONENT CANVAS, not by this scaffold: the scaffold hosts the strata rather than joining them all. system/auto = tree inheritance. No number lane. |
| elevation | 'level-1' | 'level0' | 'level1' | 'level2' | 'level3' | 'level4' | 'level5' | 'auto' | number | 'auto' | SUPPLY-ONLY — zero elevation-carrier readers (grep receipt); the immersive leaves are TRANSFORMS (translateY, measured matrix(…, −74.74)), never shadow tiers. Number unit: dp. |
| motion | 'reduced' | 'subtle' | 'normal' | 'expressive' | 'auto' | number | 'auto' | SUPPLY-ONLY — zero --jx-motion-effective readers (grep receipt); the immersive law is SCROLL-POSITION-driven (an 8px threshold, rAF-throttled, one data-hidden state) — a behavioral channel with no autonomous animation, so it is reduced-motion-safe by nature (reduced motion swaps the playground’s smooth scroll to instant). Number unit: coefficient. |
Receipts (all on the LIVE shell — this page's own chrome — unless noted): the form grid (256 / 1104 / 240 at a 1600 viewport; re-grids measured at 700/1000), --jx-header-h 74px with the ResizeObserver correction landing on .jx-shell (the declaring scope), the body reservation (scroll-padding 106px; 6164px scrollable at the task-70 read — a point-in-time digit, the page grows with the campaign), the immersive law (data-hidden + header translateY −74.74px on scroll-down, reveal on scroll-up), the overlay pointer law (header auto / float slot none), the named-area budget per form (header header header / tree stage toc → header header / stage toc → header tocbar / stage), the skip link (#main), the view-transition names (site-header / page-main), the boot splash (SSR-shipped, unmounted live — the seat is the layout's), the embeddable 480px instance (the narrow form inside the wide window) and the canvas stage's data-theme="light" + .jx-light island pin were measured by the task-70 probe; the unread rows carry grep receipts over ui/website-scaffold/. The query() seat below rides the md viewport key (48rem) on the size lane.
<script lang="ts">
import WebsiteScaffold from '@ui/website-scaffold.svelte';
import { query } from '@lib/universal-props-query.svelte';
</script>
<!-- below 48rem the base (13px root) applies; at 48rem+ the md case (18px)
wins — the type-scale seam scales every zone at once -->
<WebsiteScaffold size={query({ md: 18 }, 13)}>…</WebsiteScaffold>website-scaffold · query()
Media keys are min-width: below 48rem the base (13px root) applies; at 48rem and wider the md case wins (18px) — the type-scale seam scales the HOST root, so every zone (header, rail, content) grows together. The number lane goes bare. Resize across 48rem and watch the whole shell breathe.
<script lang="ts">
import WebsiteScaffold from '@ui/website-scaffold.svelte';
import { query } from '@lib/universal-props-query.svelte';
</script>
<!-- below 48rem the base (13px root) applies; at 48rem+ the md case (18px)
wins — the type-scale seam scales every zone at once -->
<WebsiteScaffold size={query({ md: 18 }, 13)}>…</WebsiteScaffold>WebsiteScaffold · universal props
The size stamp scales the host root — the site-level type-scale seam (one number, every zone). A scaffold cannot nest inside itself; this specimen rides the page's own shell.
<WebsiteScaffold size={18} density="small">…</WebsiteScaffold>a11y
Accessibility
Skip link to main#main, one landmark per zone, and chrome reservations that keep reflow honest.
Keyboard
| Key | Action |
|---|---|
| Tab | The hidden Skip to content link is the first stop; then header nav, then main |
| Enter | Follows the skip link — the VIEW lands on #main at the toc line (scroll-padding); focus itself stays on body (main carries no tabindex −1 — the classic skip-link caveat) |
ARIA
| Attribute | Value | Description |
|---|---|---|
| skip link | "Skip to content" | Keyboard-reachable target on #main, hidden until focused |
| landmarks | header / main#main / footer | The shell renders one landmark per zone; adopted nodes keep their authored semantics |
| view-transition-names | site-header / page-main | Persist across navigations so chrome never flashes |