v0.21.0
Released June 12, 2026 · All releases
Registry-driven composition and sandbox integration. A minor release turning the entity registry into something you can author over and visualize — making content queryable by frontmatter, and letting sandboxes be fed registry data to drive arbitrary client-side renderings (3D sitemaps, relationship graphs). The thread that runs through it: the registry's render targets grow from HTML and SVG to bring-your-own-renderer.
Changes
- Data-bound sandboxes (SPEC-093 core) — the registry's third render target, after
collection(HTML) andaggregate(SVG): bring your own renderer. A{% sandbox data="type:page" %}binds a registry query (SPEC-070 field-match grammar); the build resolves it, projects (data-fields) and shapes it (data-shape="flat"|"tree"), caps the payload, and injects the JSON so the iframe code can read it as a frozenwindow.RF_DATA. The payload rides the same data-attribute rail as design tokens, so it works across every adapter. Over-cap payloads warn and truncate; a sandbox with no static fallback warns (progressive-enhancement reminder). Enables registry-fed visualizations like a 3D sitemap or relationship graph. - Hero cover layout + animated sandbox backgrounds (SPEC-101).
herois now a first-classmedia-position="cover"host: the media well fills the section interior and the headline/blurb/actions overlay it, with the cover knobs (content-place,height,aspect), a band-appropriate height authority (a viewport-relative floor instead of the 3/4 tile default), root padding rerouted to the overlay, and a centred-band overlay default with an even scrim. Any non-img/videomedia guest now fills a cover well;sandboxgainsheight="fill"(iframe pinned to 100%, auto-resize negotiation disabled), applied automatically when a sandbox is a cover backdrop — so a live three.js scene drops into a hero as a full-bleed, inert animated background. A non-eager (activation="visible"|"click") sandbox under cover warns at build time (the Run affordance is unreachable on an inert backdrop). Ships with the wireframe-waves showcase (site/examples/wireframe-waves/) — a displaced wireframe terrain whose crests pick up the niwaki palette. Also fixes nested-density title sizing:[data-section="title"]now sizes via--rf-title-size(set per density root), so a full-density rune inside a compact host (a hero in apreview) keeps its real title size. And fixes inverted stacked media labels (BUG-001): hero/feature/step have content-first DOM, so the shared media-first stacked CSS renderedtopat the bottom and vice versa — their default is now a truthfulbottom(zero visual change for existing content) and explicittop/bottomrender where they say. - Deferred sandbox activation (WORK-381) — keep heavy sandboxes off the critical path.
{% sandbox %}gainsactivation(eagerdefault |visible|click) and aposterimage URL. A non-eager sandbox shows the poster and an explicit Run control in the iframe's place and defers iframe creation and every dependency download until activated —visiblemounts on scroll-in (IntersectionObserver),clickmounts on user action. Underprefers-reduced-motiona non-eager sandbox never auto-activates: the poster and control stay so motion-sensitive visitors opt in. Eager sandboxes are unchanged (no markup or behaviour regression). The site's 3D star-map sitemap now loads only when scrolled into view. reference list/reference dumpare now site-aware (--site <name>) — they resolve a multi-site config's per-siteplugins(andrunes) instead of only reading the flat single-site shape, so the rune reference for a multi-site project includes its plugin runes, not just core.- xref no longer warns on same-destination name collisions. With SPEC-092's typed page entities, a
/runes/<x>doc page is registered as both apageand aruneof the same name — an{% xref %}by that name matched both and warned, even though both resolve to the same URL. The resolver now only warns when the candidates' destinations actually diverge. - Pages can now register as typed registry entities (SPEC-092 Layers 2 & 3). A page that sets a frontmatter
type(and optionalid) is registered as that entity type in addition to itspageentity, reusing the page's reserved-filtered data;iddefaults to the page URL. A newrouteRulesentityfield types a whole section by convention (e.g.{ "pattern": "runes/**", "entity": "rune" }) without per-page frontmatter — a page's owntypeoverrides the rule. Duplicate(type, id)registrations warn. This letscollection/aggregatequery content by domain type (e.g.collection type="rune"), and it's the complement ofentityRoutes(entity → page). Works across all adapters. sectionrune — a generic page section: the shared eyebrow → headline → blurb (+ image) header above arbitrary body content. Unlikefeature/hero/reveal, it doesn't bake in a specific body layout, so it can introduce header-less primitives likebentowith a title and intro. Every header slot is optional (body-only renders just the body);align(start|center|end) positions the header while the body spans the section.- Page frontmatter is now indexed onto the
pageentity, socollectionandaggregatecan filter and group pages by any frontmatter field —tags,author,image, or your own custom fields (e.g.category,status). Routing/render-control keys (layout,tint,tint-mode,tint-lock,slug,redirect) are excluded so they don't pollute queries; the curated fields (title,description,date,order,icon,draft) keep their normalised values. Enables tag-driven page collections and registry-fed catalogues (SPEC-092 Layer 1). data-shape="graph"for data-bound sandboxes (SPEC-093 / WORK-390) — the third payload shape, afterflatandtree. A{% sandbox data="type:spec type:work" data-shape="graph" %}projects the queried entities as nodes and walks their SPEC-072 relationship edges into a node-link payload:window.RF_DATA = { shape: "graph", nodes, edges }, where each edge is{ from, to, kind }. Only edges whose both endpoints are in the selection are kept, so the graph is closed — ready for a force-directed or node-link layout.- Fix the
treeshape of a data-bound sandbox: page entityurls carry no trailing slash whileparentUrls do, so the nesting never matched and the tree came out flat. Normalize trailing slashes when building the tree so parent↔child relationships resolve.