v0.19.0
Released June 9, 2026 · All releases
Polish, composability & rollups. Where v0.18.0 hardened the rune output contract, v0.19.0 builds on that stable base along several threads: visual polish of the Lumina theme, making rune composability a supported and validated surface (Rune composability contract), turning bento into a proper composition substrate (Bento as a composition substrate), and improving the cross-page aggregation runes that the plan plugin leans on.
Changes
- Chart theming contract (SPEC-083 / WORK-353): the
rf-chartSVG renderer no longer hardcodes its palette or geometry. Every paint + geometry value is now an--rf-chart-*custom property Lumina ships on.rf-chart— a dedicated categorical series palette (distinct from the semantic status tokens), bar/point/ line geometry, and typography/grid. The renderer emits only tagged elements (.rf-chart__bar[data-series],__point,__line,__axis,__label) thatchart.csspaints from the props, and reads layout geometry viagetComputedStyle— so a theme retones a chart by setting--rf-chart-*alone, and a future canvas/ d3 provider reads the same vocabulary. Adds a sentiment colouring mode: data cells carryingdata-meta-sentimentcolour by the semantic token (positive→success, negative→danger, caution→warning, neutral→muted). - Also fixes
aggregate layout="chart"to emit the chart rune's field channel, so a non-barchart-typesurvives the identity transform and the.rf-chartclass isn't doubled. - Per-group sentiment projection in
aggregate(SPEC-076 / WORK-357).aggregatenow projects$item.sentimentonto the per-group template (and tags chart data cells withdata-meta-sentiment), looked up from a `(type → field → value → - Add a pair of opt-in text-zone knobs to
bento, settable as a grid-level default or a per-cell override (cell wins; the grid default is the only lever for heading-sugar grids): content-height(sm|md|lg→ 3 / 5 / 7rem) — pins the text area on column cells (top/bottom media) so they line up vertically; the media zone absorbs the remaining row-track height.media-ratio(1/3|2/5|1/2|3/5|2/3) — pins the media zone's share of the width on beside cells (start/end media); the content absorbs the rest.- The two act on perpendicular axes (a cell is either a column cell or a beside cell), so they never collide. Both default to the existing behavior (natural text height / 42% media split) and revert to natural height on the mobile stack.
- Add a
row-heightattribute to thebentorune (sm|md|lg|xl) for control over the uniform grid row-track height in grid mode (8 / 12 / 16 / 20rem;mdmatches the previous default). Falls back to the theme's--rf-bento-row-heightwhen unset, and is overridden by the stack's auto rows on mobile. - Bento substrate (SPEC-085) — v0.19.0 batch C.
- Bento is a grid primitive, not a page-section. Dropped the eyebrow/title/blurb preamble; every heading is now a cell. A titled bento is a composition (wrap it in
feature/section). Content before the first heading renders as loose content above the grid. - Cell adopts card's zone contract. A
bento-cell's content splits on a top-level---intomedia/ body / footer zones (data-section), mirroringcard. The media zone is clipped/sized by the name-agnostic WORK-339 selector (no bento-specific per-guest CSS) so ashowcasebleed peeks. The cell background is tint-deferrable, and the leading heading becomes a uniform-level<h3>title contributing to the outline. - Proportional sizing model. A 6-column default for both authoring modes;
sizepresets resolve as fractions of the column count (small ⅓, medium ½, large ⅔ × 2 rows, full = all), andcols/rowsgive precise per-axis spans that override the preset. Uniform fixed row tracks (grid-auto-rows: var(--rf-bento-row-height), never column-tied). Author-controlledcollapse="sm|md|lg|never"plus automatic progressive column reduction withmin(span, current-columns)auto-capping. - Size-derived media placement + link tiles.
media-position(top|bottom|start|end) is author-controllable per cell with a size-derived default (large/full → beside, smaller → on top); an optionalhrefmakes a whole cell a link. - Explicit
{% bento-cell %}authoring. A bento whose children includebento-celltags uses them directly — full per-tile control (the dashboard case) — short-circuiting heading conversion (explicit wins, no mixing). The legacyspanattribute is removed (subsumed bycols).cols/rowsauthor as unquoted numbers (cols=4 rows=2), matchingcolumns. - Rewrote the bento rune reference docs for the new substrate.
- Even column ladder + landscape 2-up. Reduction now steps 6 → 4 → 2 → 1, skipping the odd 3-col step where a
smallcell can't pair. On tablets (≤1024)mediumcells drop to a half so two pair per row. At≤768the grid is a 2-up auto-row stack:smallcells take half a row (two pair up), medium/large/full span full width, and media reflows to an aspect-ratio banner so wide cells no longer crop their image to a thin strip. - Collapse is a stack, not a shrunken grid. At a single column the fixed row track is dropped (
grid-auto-rows: auto) so cells size to their content and text is never clipped; media reflows to an aspect-ratio banner (--bento-media-aspect, default 16/9) on top. Cells are text-first in grid mode too — the body keeps its height and the media zone absorbs the leftover track and crops. - Composability foundation (SPEC-084) — v0.19.0 batch B.
- Media-zone guest adaptation. Replaced Lumina's name-enumerated media allow-list with one name-agnostic rule: any visual rune dropped into a
[data-section="media"]slot is sized, clipped, rounded, and given a container-query context (so intrinsically responsive guests likemockupauto-scale). Guests that manage their own bleed (preview,juxtapose, a bleedingshowcase) self-declare an opt-out. Coverscard/feature/hero/recipewith one rule. requiresParentnesting validation. New self-declaredRuneConfig.requiresParent(distinct from the advisoryparent). The identity transform validates it at build time — a rune that opts in must have the named parent as its nearest ancestor rune, else it's reported: an error for structurally-meaningless children (accordion-item, tab, step, tier, map-pin, …), a warning otherwise. Opt-in, so standalone-capable runes (track) are never flagged. No container-side allow-list.- Context-modifier audit. Removed the one nonsensical pairing (Hero
→ in-feature); every remaining context modifier has CSS coverage.refrakt inspect --auditreports context-modifier coverage, andinspectnow surfaces a rune'srequiresParent. - Docs. A new composability authoring guide documents the open-world contract.
- Lumina polish: token hygiene, dark parity, theme-aware chrome, and a11y (v0.19.0 batch A).
- Token hygiene. Reconciled seven phantom colour tokens that were painting stale literal fallbacks (the out-of-place blue and cold-gray muted text):
text-muted→muted,heading→text,accent→primary,background→bg,border-light→border,warning-fg→warning. Added a derived--rf-color-primary-bg(tracksprimaryin both modes) and a mode-flipped--rf-color-on-primary(fixes invisible white text on the light dark-mode primary). - Breaking (theme authors): the misnamed
--rf-color-primary-50…950ramp and thePrimaryScaletype are removed — it was a warm-neutral scale mislabelled "primary" with almost no consumers. Set--rf-color-primarydirectly;primary-bgderives from it. - Dark parity. Verified every absolute token has a dark override; the remaining four are intentionally shared (derived from mode-aware tokens), now annotated.
- Theme-aware chrome. Branded
::selection, token-driven scrollbars, andcolor-schemeper mode so native chrome (including the main scrollbar) follows the theme. - Accessibility. A uniform
:focus-visiblering across all interactive elements and a globalprefers-reduced-motionreset. - Two-tier surface rounding + inset media on mobile.
- New semantic radius tokens:
--rf-radius-container(→lg, 16px) for outer surfaces and--rf-radius-media(→md, 10px) for media guests nested inside them. Cards, the card/inset surface groups, feature items and bento cells move to the container tier; the shared[data-section="media"]zone and figure / recipe images move to the media tier, so a guest never reads as more rounded than its container. SPEC-086'sframewill override these per-instance. - Media in a
data-media-position="top"container no longer becomes a full-bleed square banner when the layout collapses on mobile — it stays inset within the card padding and fully rounded, matching its desktop framed look. - Top media now bleeds out past the body padding to a small, consistent margin on its top/left/right edges (new
--rf-media-margin, defaulting to--rf-spacing-sm), figure-style, so the image isn't over-inset in containers with generous padding (e.g. bento cells). Thefigureimage drops its box-shadow to match the unshadowed media elsewhere. - Bento media/responsive fixes:
- Container-query responsiveness — the bento grid is now a query container (
container-type: inline-size) and its progressive-reduction/collapse rules use@containerinstead of@media, so it reduces columns and stacks based on its own width. Grids in doc previews, sidebars, or narrow tracks now break correctly instead of only at viewport breakpoints. - Unwrap paragraph-wrapped images — images in a bento cell's media (and body) zone are unwrapped from their
<p>, so the media zone holds a bare<img>and layouts size it directly. - Neutralize the global media-zone block margin —
[data-section="media"]no longer applies avar(--rf-spacing-sm)top/bottom margin that misaligned media in flex/beside layouts; media spacing now comes from each layout. Affects all media zones (card, recipe, realm, faction, split, bento). - Surface/media polish:
- More generous vertical spacing (
--rf-spacing-md) between a top media banner and the content below it, in card/recipe/bento and on mobile stacks. - A
code-groupnested in a media zone now rounds at the media radius tier so its border lines up with the zone's clip instead of the larger container radius. - Dark-mode code background was a near-neutral grey that clashed with the warm
surface; it's been warmed to the surface hue (#1e1c19, also applied to the derived--rf-syntax-background). - A
cardwith nohrefno longer shows a hover background — the hover is now gated on the presence of the stretched card link. - The semantic
--rf-radius-container/--rf-radius-media/--rf-media-marginaliases moved from the generated token contract into the styles layer (they reference the scale rather than holding raw values). - Unify the media-zone corner radius with the card radius. Media slots (
[data-section="media"], shared by card, recipe, bento cell, feature, hero, realm, faction) rounded at--rf-radius-lgwhile their containers round at--rf-radius-md, so images looked more rounded than the card holding them. The media zone now uses--rf-radius-md, and recipe's redundantlgimage override is removed (figure was alreadymd). - Decompose
plan-progressinto sugar over theaggregaterune (SPEC-076). It now composes one aggregate per entity type — a type heading ("Work", "Specs", …) above a progress bar labelled with that type's achieved status ("Done", "Accepted", …) plus a per-status badge row — resolved by the sharedresolveAggregates. Mixing types under a single ratio was misleading (workdoneand bugfixedmeasure different things). Plan defaults are baked in (type="work,bug", achieved-status per type,group="status",milestone=scoping); the bespoke plan-side render path is removed. A bare{% plan-progress /%}scopes towork,bug; widen withtype=/show=. (Per-status badge colour is deferred — see WORK-357.) - Surface containers (card, the card/inset surface groups, feature items, bento cells) now carry a subtle 1px border in addition to their surface fill, pairing with the inset media framing.
- The dark neutral surface family was browner than the rest of the palette: the page background and inline-code background are warm-grays (R=G) but
surface, its hover/active/raised steps,border, andcode-bgcarried a red lift (R>G ≈ 36° hue). That red lift is removed so the whole dark neutral ladder shares one warm-gray character (kept warm via a blue deficit, just no longer brown), andcode-bg/--rf-syntax-backgroundfollow it. - Fix dark surface staying brown in a scoped scheme override (e.g. a preview rune toggled to dark, or a sandbox).
tint.cssre-declares the palette under[data-color-scheme="dark"]— a hand-kept third copy that the token coverage test didn't reach — and it still held the pre-de-brown surface/border/code values. Synced it to the neutralized palette, and added a coverage test that checks every--rf-*key tint.css's[data-color-scheme]overrides share with the canonical token CSS, so this copy can't silently drift again. - Add
layout="chart"to theaggregaterune (SPEC-076 chart layout): grouped counts render as a chart — one bar/point per group — by emitting thechartrune's<rf-chart>+data-name="data"table pipeline (SVG with a no-JS table fallback).chart-type(bardefault /line/area/pie) andchart-titleare configurable; avaluesub-filter adds a second series; the axis honors domain-aware group ordering; an empty query renders theemptyfallback rather than a broken chart. - Modernize
backlogto compose over thebarrune (SPEC-084 / WORK-342). Its default item is now acardwhose top strip is abar— the identifier on the left, a sentiment-coloured statusbadgeon the right, title below — built from a universal projection that works for every plan type. Newlayoutattribute (cardsdefault ·list·table) is forwarded tocollection. A type chip appears only for a mixed set; a single-type backlog also surfaces that type's key field (work→priority, bug→severity). The$itemprojection gainsidentifier(id || name, so milestones slot in),sentiment, andmixed, shared by every collection/aggregate rollup. - Expose group metadata on
$itemin a groupedcollection(SPEC-070 / WORK-344): the per-item template can now read$item.group(its group key) and$item.groupCount(the group's size), so a grouped collection can render group context inline without dropping toaggregate. Ungrouped collections are unaffected (groupempty,groupCount0). cardnow recognizes its title as the first heading in the body rather than only the first child, so a composed header (e.g. a{% bar %}strip before the title) no longer leaves the title carrying a prose-sized top margin. Fixes the gap between a bar header and the title inbacklogcards.- Fix
escapeFenceTagsdesyncing on code fences with info strings. A titled/attributed fence (e.g.```yaml title="config.ts") was not recognised as a fence opener, so fence tracking lost sync and{% %}tags following the fence were wrongly escaped — corrupting document structure (for instance a{% /codegroup %}after a titled fence leaking as literal text). The opener now matches the full info string. - Add a
levelsattribute to thebentorune's heading-sugar path: an author-defined footprint ladder, indexed by relative heading depth, where each rung is a column countW(× 1 row) or a footprintWxH— e.g.levels="6,5,4,3,2,1"(uniform-height, width-by-depth; the formerspanmode) orlevels="4x2,3x1,2x1". Depth is measured from the auto-detected base (shallowest heading), so the shallowest is always rung 0; ladders shorter than the heading depth clamp to the last rung. Omittinglevelskeeps the default tiered sizing unchanged, and explicit{% bento-cell %}grids ignore it.