Blocks Dossier Pack

Blocks & themesStable
@wabbit/tome-blocks-dossier-packv0.14.0

Case-file, evidence, and ledger register blocks for proof-heavy sections — dossier grids, spec sheets, exhibit cards, and record ledgers, built on the house field/motion/style primitives.

Install
  1. Get a registry token from your credentials page. You need a purchase that includes this package, or a Craft Library membership.

  2. Add the registry and your token to the .npmrc at the root of your project, with your token in place of YOUR_TOKEN:

    @wabbit:registry=https://npm.wabbit.com/
    //npm.wabbit.com/:_authToken=YOUR_TOKEN
  3. Then install:

    npm install @wabbit/tome-blocks-dossier-pack

Overview

@wabbit/tome-blocks-dossier-pack

The case-file / evidence / ledger register of wabbit.com's site-local blocks, promoted into a sellable Tome block pack (family: block-packs, tier: addon). Program B, wave 1 of the Tome Blocks — Storefront Expansion and House Packs spec (2026-09-05).

The pack ships 25 blocks under one bundle descriptor (dossier-pack), built on @wabbit/tome-blocks-core (block/registry/render contracts) and @wabbit/tome-blocks-house (shared field/motion/style primitives ported from the wabbit.com site — see that package's PORT-NOTES.md).

Blocks

| Slug | Name | Variants | |---|---|---| | case-file-grid | Case File Grid | default, field-manual, filmstrip | | case-file-row | Case File Row | — | | evidence-sheet | Evidence Sheet | — | | evidence-plate | Evidence Plate | — | | exhibit-artifact | Exhibit Artifact | — | | proof-plates | Proof Plates | — | | receipts-trio | Receipts Trio | — | | record-roster | Record Roster | — | | spec-sheet | Spec Sheet | — | | spec-plate | Spec Plate | — | | ledger | Ledger | default, compact | | compare-ledger | Compare Ledger | — | | comparison-table | Comparison Table | — | | cost-ledger | Cost Ledger | — | | grants-ledger | Grants Ledger | default, compact | | phase-ledger | Phase Ledger | default, rail, index, panel | | term-ledger | Term Ledger | shifts, spec, spec-measure | | shift-rows | Shift Rows | default, compact | | wall-rows | Wall Rows | — | | fit-list | Fit List | default, compact | | fit-prose | Fit Prose | — | | lab-notes | Lab Notes | — | | practice-modes | Practice Modes | — | | zone-directory | Zone Directory | — | | dated-ledger | Dated Ledger | columns, rows |

Some blocks expose a design choice as a field rather than a registered variant — evidence-sheet's four treatments (table, grid, strip, ledger) and proof-plates' Strip or Situational shell are authored per block instance.

A few blocks carry opt-in display options. Each is off by default, and a block that leaves it off renders exactly as it did before the option existed:

| Block | Option | What it does | |---|---|---| | term-ledger | headingFace: 'display' | Sets the heading in a larger, light serif face (default head) and lays the block out in its editorial form. With no figure (the display lead): a tinted band (surface-tint; spec keeps its dark band), the kick label in the left lane beside the heading, the intro in the reading column, the ledger stopping at the end of the prose lane with an 11 to 15rem term column, smaller terms and row bodies held to 58ch, and the close as a ruled serif line across the content width. With a figure: the intro in the reading column, the ledger stopping at the end of the prose lane, the tilted figure plate filling the rest of the row with a shadow (--tome-dossier-figure-shadow, tome-ui's --tome-shadow-lg when unset), heavier terms and row bodies held to 58ch. Below 1024px everything stacks, the figure centred under the rows at 280px. | | term-ledger | figureLayout: 'display' | Only with a supporting figure. Gives the head face the display face's figure layout (default split, the head face's own rows beside a 200 to 320px figure): the intro in the reading column, the ledger stopping at the end of the prose lane with an 11 to 15rem term column, heavier terms, row bodies held to 58ch and the tilted figure plate filling the rest of the row with the figure shadow. The heading, band and close stay the head face's. The display face always lays a figure out this way, so it ignores the option. | | term-ledger | spec-measure variant | The spec band with each row body capped at --tome-prose-max-width (62ch when unset); the rules still run the full width. | | proof-plates | linkStyle: 'button' (per plate) | Sets the plate's link as a button at the plate's foot instead of a link continuing the body sentence (default inline). The plate is a flex column and the button row takes the free space above it, so buttons line up at the bottom across plates of different heights. Shown whenever the link has a label and an href, with or without a body. linkAppearance: outline (default, a transparent button with a currentColor border) or default (filled with --tome-color-accent). | | term-ledger | rows[].cta, introCta | A button after a row's body (in the row's body column) or after the intro: { label, url, appearance? }, appearance default (accent fill, the default here) or outline. Ignored without both a label and a url. | | term-ledger | introEdge: 'rows' | Lines the intro and the close up on the row-body text edge (default lead, the lead slot's own edge). From 721px both take the ledger's column and pad in by the term column and its gap; the close keeps its prose measure (--tome-prose-max-width, 600px when unset) from that edge. Below 721px the rows stack, so nothing is padded. The editorial layouts (headingFace: 'display', a display figure layout) keep their own geometry and ignore it. | | comparison-table | railHeader | Lays the eyebrow out as a narrow ruled column beside a large headline. *accent* runs in the headline drop to their own line in the accent ink. The headline reads @wabbit/tome-blocks-house's heading-voice tokens (--tome-house-heading-*), falling back to the block's own voice. | | comparison-table | emphasisHighlight | Paints highlighted columns with a solid fill in the band's text colour, their text in the band colour, instead of the subtle accent tint. A site with an emphasis colour of its own sets --tome-dossier-emphasis-bg and --tome-dossier-emphasis-fg (and --tome-dossier-emphasis-accent for the column tag on a phone card). | | comparison-table | descriptionAboveTable | Moves the description out of the header to sit after it, directly above the table, with --tome-space-lg below it; the header's bottom margin sets the space above it. Off, the description closes the header, under the headline. | | evidence-sheet | ledgerRails: 'split' | Ledger treatment only: cuts the rows by count across a left and a mirrored right rail, centres the opening narrative between them and moves the scope/stack chips under it (default single). |

Comparison table without a headline. A comparison-table with no headline renders the table with no header (and no eyebrow), any description directly above it, so the table can sit under the heading of the section it belongs to. The schema still requires a headline; this is for data that reaches the renderer from elsewhere.

Comparison table on tablets and phones. Below 1024px comparison-table shows each row as a stacked card instead of the table: the row label as the card head, then every column's answer under a small mono column tag, the highlighted column tinted (or filled, with emphasisHighlight). On phones (below 640px) each answer is one line with its tag inline before it; from 640px the answers sit side by side, one per column. The table stays in the DOM, visually hidden, as the accessible version; the cards are aria-hidden. From 1024px nothing changes. The table's frame reads three tokens, each falling back to today's square, unfilled frame: --tome-dossier-table-radius (0), --tome-dossier-table-fill (transparent) and --tome-dossier-table-rule (an 18% rule of the text colour).

Comparison table header and column heads. Four tokens, each falling back to today's value: --tome-dossier-table-head-gap (the stacked header's bottom margin, --tome-space-xl), --tome-dossier-rail-width (the rail header's eyebrow column, 10.625rem), --tome-dossier-rail-eyebrow-size (the rail eyebrow's size, apart from the column heads, --tome-text-xs) and --tome-dossier-column-head-wrap (the column heads' white-space, nowrap; set normal to let a long label wrap).

Dated Ledger

dated-ledger is a dated record of finished work: a kicker, heading and intro, then 1 to 12 entries and an optional "more" link. Each entry is led by its date as a large day numeral with the month, year and reference (No. 1187, Case 42) on a meta line in tabular figures, then an optional photo, a chip (a place or a category), a title, one result line and an optional figure (value and label). An entry link with a label closes the entry as a link line; without a label the title links. Either way the link covers the whole entry. Entry photos upload to mediaCollection (default media), and their alt text comes from the media item.

  • `columns` (default): up to four entries per row, each a column led by the day numeral, with a hairline between columns from 1024px. Two per row from 640px, one on phones.
  • `rows`: ruled rows under a 2px rule, the date and reference stacked in the left lane, the photo (when any entry has one), the title and result, and the figure in the right lane. One column on phones.

Options. Each renders exactly as before when unset (or set to its default), and adds its hook only when set:

  • `datePrecision` (day by default, or year): with year the year is the large numeral (<time dateTime="YYYY">, hook data-ledger-year, its size --tome-dossier-ledger-year-size), with no month; the reference still follows (in rows from 640px, on its own line under the year, without the · separator). The root carries data-date-precision="year".
  • `density` (regular by default, or compact): tighter rows for short entries with no photo or figure, such as a list of awards: a smaller numeral and title, the result without its rule, and in rows from 1024px the title, result and chip on one line with the chip at its end (grid areas title result chip inside the body). The root carries data-ledger-density="compact".
  • `chipPlacement` (body by default, or dateline): with dateline each entry's chip leaves the body and closes the date line on a line of its own, under the date and reference ([data-ledger-date] [data-ledger-chip]), so in rows it sits in the left margin under "No. 1". The gap above it is --tome-dossier-ledger-dateline-chip-gap. The root carries data-ledger-chip-placement="dateline".
  • `entries[].complianceLabel`: a short line closing the entry (hook data-compliance-label), for the statement some professions' advertising rules require beside a past result ("Past result. It does not guarantee a similar outcome."). The admin description says so, since deleting one removes it from the page. In rows it takes the grid area label under the body, beside the date and before the figure; a theme can move the area (to the right lane, say) by redefining grid-template-areas.
  • `note`: a closing paragraph under the entries, before the "more" link (hook data-ledger-note), for a source line or a disclaimer. With the opt-in reveal on it is its own note group.

The date is read in UTC as a calendar day (<time dateTime="YYYY-MM-DD">), with English short month names. Markup is an <ol> of <li> entries on the reading lanes (kicker lane, heading slot, the entries across the content width).

Theme hooks. The block wrapper carries data-block-type="dated-ledger" and the root data-block-variant (columns | rows). Every part a theme restyles carries a stable attribute (class names are scoped and are not a contract): data-ledger-entries (the <ol>, with data-cols 1 to 4 in columns), data-ledger-entry (each <li>), data-ledger-date (the date line), data-ledger-day (the numeral), data-ledger-month (month and year), data-ledger-ref (the reference), data-ledger-photo, data-ledger-body, data-ledger-chip, data-ledger-title, data-ledger-result, data-ledger-link, data-ledger-figure, data-ledger-figure-value, data-ledger-figure-label and data-ledger-more; with the options set, the root's data-date-precision="year", data-ledger-density="compact" and data-ledger-chip-placement="dateline", the year numeral data-ledger-year, each entry's data-compliance-label and the note data-ledger-note. Tokens, each with a fallback: --tome-dossier-rule (hairlines), --tome-dossier-ledger-day-size (the numeral), --tome-dossier-ledger-year-size (the year numeral) and --tome-dossier-ledger-gap (the gap between columns).

Phase Ledger rail

phase-ledger's rail variant sets the phases beside a progress rail that fills as the section scrolls, each phase's dot lighting once the fill reaches it. From 1024px the phases sit in columns, up to four per row, under a horizontal rail across their top that fills left to right, each dot on the line at its column's start (phases past the first row keep a hairline of their own at their top, with their dot on it). Below 1024px the phases stack beside a vertical rail on the left edge that fills down to a reading line 60% of the way down the viewport. The motion is a small client island (PhaseLedgerRailMotion): one passive scroll listener, throttled to a frame and attached only while the rows are near the viewport, with no GSAP. The server renders the fully drawn rail with every dot on, so the rail is complete without JavaScript, before hydration and under prefers-reduced-motion: reduce. Content saved before the variant existed renders as default.

Theme hooks. The root carries data-block-variant (default | rail | index | panel). In the rail: data-phase-rail (the rows container, with data-cols 1 to 4, data-rail-axis x or y once hydrated, data-rail-state="live" while the island animates it, and the --tome-dossier-rail-progress fill, 0 to 1, unset meaning full), data-phase-rail-track (the line), data-phase-rail-fill (its fill), data-phase-step on each phase with data-active="true" or "false", and data-phase-rail-dot (each dot). In both variants each step number carries data-phase-num and each phase's outcome line data-phase-outcome; the outcome's italic and weight read --tome-dossier-phase-outcome-style (italic when unset) and --tome-dossier-phase-outcome-weight (330 when unset), so a theme restyles them without overriding the declarations. data-rail-axis mirrors the stylesheet's (min-width: 1024px) query: CSS cannot set an attribute, so the island sets it whenever JavaScript runs (reduced motion included); with JavaScript off it is absent, and a theme keys on the same media query. Tokens, each with a fallback: --tome-dossier-rail-gutter (the room made for the vertical rail), --tome-dossier-rail-track (the unfilled line) and --tome-dossier-rail-fill (the fill and the active dots, the accent when unset).

Phase Ledger index

phase-ledger's index variant is a numbered index of ruled rows, for a list of areas or sections that each have their own page. Each row is the number (the phase's num, set as a bare number such as 1 so a theme can set a mark such as § before it; the row's position when empty), the title, the body, and a byline column with the outcome line (e.g. "Led by …") and an optional link line. On phones the number sits beside the title with the body and byline under them; from 768px the body and byline sit side by side under the title; from 1024px the row is one line (number, title, body, byline). Each phase's link (label, href) shows in the admin only for this variant: with a label it renders a link line closing the row ("Read section 1 →"), without one the title links. Either way the link covers the whole row, so each row is one tab stop, and hovering the row underlines the title and the link line.

Theme hooks. The root carries data-block-variant="index"; the list data-phase-index (an <ol>), each row data-phase-row, the number data-phase-num, the byline data-phase-outcome (it reads the outcome tokens above) and the link line data-phase-link. Token: --tome-dossier-phase-index-num-size (the number).

Phase Ledger panel

phase-ledger's panel variant shows one large media panel above a row of numbered steps. The active step swaps the picture, the number, the title, the text and the meta line. Each phase's image and meta show in the admin only for this variant, and phase images upload to mediaCollection (default media). Keep it to 2 to 6 steps with short text.

  • Without JavaScript, and before hydration: every step is listed in full, one ruled row each with its own picture and text (side by side from 768px), every step data-phase-active="true", and no step bar. Nothing is hidden.
  • Live (the client island PhaseLedgerPanelMotion has mounted, data-panel-state="live"): the steps stack in one panel and only the active one shows; the others are visibility: hidden and inert. The step bar is an ARIA tablist (role="tablist" named by the block heading, a role="tab" button per step with aria-selected, aria-controls and a roving tabindex; each step is a role="tabpanel"). Click selects a step; the left and right arrows move between steps and wrap (swapped right to left), Home and End jump to the ends, and Tab leaves the bar.
  • Scroll-follow ((min-width: 768px) and (min-height: 600px), data-panel-follow="true"): the island's root grows a runway (37.5vh per step past one viewport) and the panel sticks below the header (--tome-dossier-panel-top, 5.5rem) while reading down it selects each step in turn. Selecting a step scrolls to its place on the runway, so the page and the step agree. While a step has keyboard focus, scroll does not move the step. Below the query (phones, short windows) the panel is never pinned and there is no runway, so it can never hold the reader in place; click and keys still work.
  • Reduced motion (prefers-reduced-motion: reduce, read live): the jump to a step is instant (behavior: 'instant', which also overrides a page's scroll-behavior: smooth), and there is no crossfade and no sliding accent. Everything else works the same.
  • Motion otherwise: the pictures crossfade (--tome-dossier-panel-fade, 0.45s) and the active step's accent bar slides in; the text swaps at once. The crossfade waits for data-panel-ready="true", set a frame after going live, so the switch from the list to the panel never animates. The island's root carries data-tome-motion="self", so the platform's opt-in scroll reveal leaves it alone.

Theme hooks. The root carries data-block-variant="panel". The island's root data-phase-panel-root (with data-steps, data-panel-state, data-panel-ready and data-panel-follow as above), the media panel data-phase-panel, each step in it data-phase-slide with its picture box data-phase-media, number data-phase-num (two digits, from 01), meta line data-phase-meta and outcome data-phase-outcome, the step bar data-phase-tabs, each step button data-phase-tab with its small label data-phase-tab-num and data-phase-num (the phase's num, else the position) and its name data-phase-title, and data-phase-active="true" | "false" on every step and step button. Tokens, each with a fallback: --tome-dossier-panel-top, --tome-dossier-panel-media-height (the picture while live from 768px, min(52vh, 540px)), --tome-dossier-panel-fade, --tome-dossier-panel-bar (the accent bar, the accent when unset) and --tome-dossier-phase-panel-num-size.

Reading lanes

Twenty blocks lay out on the page's reading lanes through @wabbit/tome-blocks-house's LaneGrid, listed below by arrangement. Below 1024px everything stacks on the content column. In a full-row block wrapper (RenderBlocks' default) the band's background paints the whole row while its content stays on the content lines. From 1024px:

Kicker hook. Every dossier block that sets a lane kicker (or strip label) marks its text with data-lane-kicker: case-file-row, cost-ledger, dated-ledger, fit-list, fit-prose, grants-ledger, phase-ledger, practice-modes, proof-plates, receipts-trio, record-roster, term-ledger, wall-rows and zone-directory. A theme restyles every kicker with one [data-lane-kicker] rule.

Zone directory hooks. The board carries data-zone-board, and each cell's number data-zone-num, title data-zone-title, description data-zone-description and go label data-zone-link. Each cell is the link adapter's anchor and carries data-zone-cell (@wabbit/tome-blocks-core's default anchor now forwards data-* attributes; with a consumer Link that does not forward them, [data-zone-board] > * still reaches the cell). Numbers print as authored: the block never adds a mark such as §, so a theme can set one with ::before (as it can on phase-ledger's index numbers).

  • Kicker beside the heading: cost-ledger, grants-ledger, practice-modes, zone-directory, case-file-row, compare-ledger (no kicker), fit-list, fit-prose, phase-ledger, dated-ledger, receipts-trio and proof-plates in its situational (card) shell. The kicker (or eyebrow) sits in the left marginalia lane and the heading starts at the reading column. Intros sit under the heading (zone directory, case file row, phase ledger, dated ledger) or in the reading column (cost ledger, receipts trio), and tables, rows, cells, columns, quotes, plates and closing lines run the whole content width.
  • Full-width kicker rule: term-ledger, wall-rows and proof-plates in its strip shell. The kicker's rule and the heading (held to 24ch) run the content width; the intro and the closing statement hang from the prose lane to the end of the reading column (the lead slot), their text on the prose measure; the rows, walls and plates run the content width. Term Ledger's optional figure sits beside its rows inside that width. Term Ledger's display face moves its parts onto other lanes (see the display options above).
  • Every part across the content width: case-file-grid, spec-plate and lab-notes. The band paints the whole row; the top strip, title, tiles or cells, entries and footer run the content column.
  • Evidence Sheet: table, grid and strip run every part across the content width. In the ledger treatment the rail runs from the content edge to the prose lane, held one column short of it, and the opening narrative hangs from the prose lane to the end of the reading column. With ledgerRails: 'split' the opening sits in the prose lane and a mirrored right rail runs from the prose lane's end to the content edge, again one column clear. The one-column gap is a margin of one lane-grid column, so it holds on any lane settings.
  • Kicker beside its content: record-roster puts its rows beside the kicker, from the prose lane to the end of the reading column, with the logos across the content width and the coda centred in the reading column. evidence-plate puts its eyebrow in the left lane above the stats, the body and sources in the reading column, and the pull statement from the content edge to the end of the reading column.

Rendered through RenderBlocks with tome-ui's grid, these blocks subgrid the page grid, so their lanes are the page's lanes: set --tome-grid-marginalia-left-cols, --tome-grid-marginalia-right-cols and --tome-grid-prose-pad-cols on the page to change them (defaults 3, 3 and 2), and --tome-prose-max-width to cap intros, lists and closing statements at your prose measure (most hold a character measure of their own when it is unset). Without the page grid they lay out the same tracks themselves, with --tome-grid-padding as the side gutter. See LaneGrid in @wabbit/tome-blocks-house's README for how the parent is detected, and the one case (disableContainer with no grid parent) it cannot detect.

comparison-table, exhibit-artifact and spec-sheet use no lane: they are content-width components rather than bands, and fill the placement they are given, which on the page grid is the content column, gutter to gutter, with any band painting that width only. ledger and shift-rows keep their own layout, and switch their row grid on their own width rather than the window: shift-rows puts label, before, arrow and after side by side once its row list is 51rem wide, and ledger shows its four columns once its table is 58rem wide (container queries, so rem is the page's root size). A block placed in a narrow column stacks even on a wide screen.

Heading voice, bands and labels

Buttons. proof-plates and term-ledger buttons share one look: a 44px-tall mono, uppercase, letter-spaced button on --tome-radius-md, filled with --tome-color-accent and --tome-color-on-accent (default) or transparent with a currentColor border (outline). Buttons in a row wrap with a 12px by 14px gap; at 640px and below they stack full width and the label may wrap. A trailing arrow in a label ( →) is bound to the last word with a non-breaking space. A button carries data-pack-buttons on its row for theme hooks.

Heading voice. Every block heading reads @wabbit/tome-blocks-house's heading-voice tokens (--tome-house-heading-family, -weight, -style, -transform, -tracking, -leading), and its *accent* phrase reads the matching --tome-house-heading-accent-* tokens. Each falls back to the heading's own value, so a site that sets none of them sees no change; set them on a route or a wrapper to give every dossier heading inside it your voice. Face options read them too (term-ledger and spec-plate's headingFace: 'display', case-file-grid's field-manual headline). An accent unset follows the heading tokens, except where the accent has a face of its own: the serif italic phrase in the sans headings of term-ledger, wall-rows and proof-plates' strip shell keeps its serif face and weight unless you also set --tome-house-heading-accent-family and --tome-house-heading-accent-weight. A font chosen in a headline's own font field still wins over the tokens.

Opting in. A heading only reads tokens for the properties it already set itself (face, weight, tracking and line height on most). The properties it never set (font style and letter case on every heading, the face on case-file-grid, lab-notes, ledger and shift-rows, the tracking on evidence-sheet's opening headline), and the accent's own face, weight, case, tracking and line height, are left to your site's own heading styles unless an ancestor carries the data-tome-heading-voice attribute (any value, or none). Put it on the route, wrapper or block wrapper where you set the tokens:

<div data-tome-heading-voice style="--tome-house-heading-transform: uppercase; --tome-house-heading-accent-style: normal">
  <!-- dossier blocks -->
</div>

Under the attribute, a token you leave unset falls back to normal (style, tracking), none (case) or the inherited value (face; the accent's properties follow the heading). Without it, a site rule such as h2 { letter-spacing: -0.025em } keeps applying to every dossier heading.

Heading size. The heading-voice tokens carry no size. This pack adds one size token, --tome-dossier-heading-size, read by every heading rule that sets a size: each block's base heading and the variant rules with a size of their own (compact headings, headingFace: 'display', case-file-grid's field-manual headline, comparison-table's rail headline). Each rule falls back to its own size, so nothing changes until you set the token. It is one name for the whole pack, so scope it to a block from outside (a wrapper around that block). A size an editor picks for one block (case-file-grid's and lab-notes' headline size, or the chrome text size on the blocks that read it) wins over the token; the token then wins over the block's default. shift-rows' heading does not read the chrome text size: there it sizes the intro and the before and after bodies.

Other size tokens. Each falls back to the size it replaced:

| Token | Block | What it sizes | Unset | |---|---|---|---| | --tome-dossier-before-headline-size | shift-rows | Each row's before headline | --tome-type-size-xxl | | --tome-dossier-after-headline-size | shift-rows | Each row's after headline | --tome-type-size-xxl | | --tome-dossier-cost-size | ledger | The cost (debit) cell | --tome-type-size-xl | | --tome-dossier-closing-size | ledger | The closing note | --tome-type-size-xxl |

Bands. Every block with a background field paints the band you store, including a band set per light and dark theme, which follows tome-ui's data-theme attribute and falls back to the base band. fit-prose, spec-sheet, spec-plate and zone-directory used to ignore a stored band; with no band stored (or inherit) they still sit transparent over the page.

Inner panels. On a block with a stored band, case-file-row's lit cell paints its own light band, zone-directory's board paints its paper band and each dark cell its dark band (with that band's inks), and cost-ledger's lit row lifts off the band with a shadow in its own colour. With no band stored these panels keep their earlier, unpainted look. A zone-directory dark cell receives its band as an inline style on the cell link, so a registered link adapter must pass style through.

proof-plates has no background field, so its flagged plate takes a band from three tokens, set on a route or a wrapper. With none set the plate stays unpainted, as before.

| Token | What it sets on a flagged plate | |---|---| | --tome-dossier-flag-bg | The plate's fill | | --tome-dossier-flag-fg | Its text colour | | --tome-dossier-flag-accent-ink | Its accent text (kicker, accent runs, link) |

Band-relative inks. Text and fills that sit on a band read the band's companion inks first, with the theme token as the fallback: record-roster's row index and coda and evidence-plate's accent pull read --blk-primary-ink, and exhibit-artifact's tag reads --blk-accent-ink with --blk-on-accent-ink for its label. shift-rows' row labels and arrows read --blk-accent-ink, and the after panel's border reads it too. On the built-in theme-relative bands the companions are unset and nothing changes; on a dark band, or a site band that carries companions, the text takes the band's own ink.

Emphasis panel. shift-rows' after panel reads the band's emphasis pair from @wabbit/tome-blocks-house (--blk-emph-bg for its fill, --blk-emph-fg for its text and AFTER tag). No built-in band sets the pair, so the panel stays a faint accent tint in the band's own ink; a site band registered or overridden with emph and onEmph turns it into a solid card that stands out against the band.

Ledger closing panel and debit ink. ledger's closing panel reads --tome-dossier-closing-bg and --tome-dossier-closing-fg (tome-ui's solid-dark surface and its ink when unset), and its cost cells read --tome-dossier-debit-ink (--tome-color-error-text); the accent row keeps the band accent. To tie the panel to the band, declare the tokens on the block's root, where --blk-bg and --blk-fg are set, for example --tome-dossier-closing-bg: color-mix(in oklch, var(--blk-bg) 70%, black); a value declared on an outer wrapper resolves there, before the band exists. The root carries data-band-tone (light or dark) for tone-specific values. With no band companions, the tag reads --tome-dossier-tag-bg and --tome-dossier-tag-fg (a site's own tag fill and label ink) before the theme's accent and on-accent.

Links and hover. case-file-grid's link rules (the CTA tile, each tile's link, the index link) and the link colours of lab-notes (handoff links), case-file-row (cell links, closing link), ledger and shift-rows (CTA, with its hover and focus states), evidence-sheet and exhibit-artifact (CTA) and zone-directory's dark cells use two-class selectors, so a site's global link reset such as :root a { color: inherit } does not repaint them. Their hover transitions read --tome-dossier-hover-ease and --tome-dossier-hover-duration, as does evidence-plate's source-link underline. Each block keeps its own timing when they are unset: in case-file-grid tome-ui's --tome-motion-ease-out (all three links) and --tome-motion-fast (the tile link's gap and the index link's colour), in lab-notes ease and 0.18s (the handoff link's gap), in evidence-plate ease and --tome-motion-fast. case-file-row's link underlines follow the band's accent ink (--blk-accent-ink) when the band carries one, as the link text does; without one they keep the theme accent.

Label inks. Two labels read their ink strength from a 0 to 1 token, falling back to today's value: case-file-row's cell kicker (--tome-dossier-cell-kicker-opacity, 0.62) and evidence-plate's eyebrow (--tome-dossier-plate-eyebrow-opacity, 0.78). cost-ledger's line label in the lit row reads --tome-dossier-ledger-lit-label-ink, falling back to 80% of the plate's ink; set it to an accent (for example var(--tome-color-accent-on-solid-dark)) to light the label. It is a token rather than the band's accent ink because the lit plate always carries the solid-dark band, which always sets one.

Term ledger spec term leading. The spec and spec-measure terms read their line height from --tome-dossier-spec-term-leading, falling back to 1.1 (the term's own leading). A site whose mono labels set no leading of their own, and so take their row's, sets it to that value.

Phase ledger row titles. The row title is an <h3> and sets no tracking of its own, so it takes your page's heading tracking (tome-ui's base heading rule reads --tome-type-tracking-tight). The block heading reads --tome-house-heading-tracking first: to retrack the heading alone, set that token rather than re-pointing --tome-type-tracking-tight on the block, which retracks the row titles too.

Cost ledger intro size. cost-ledger's intro reads --tome-dossier-ledger-intro-size, falling back to --tome-text-lg. The debit cells read --tome-text-lg too, so set this token to size the intro alone.

Prose measure. lab-notes' entry bodies are capped at --tome-prose-max-width when a site sets it, and run their whole cell when it is unset. spec-plate's lede holds 62ch, or --tome-prose-max-width where that is narrower (min(62ch, …)), on both heading faces.

Rich-text intros. The intros of wall-rows, term-ledger and phase-ledger start on their slot's start line and hold their measure even when your rich-text adapter puts a centring container class on the element it renders (auto inline margins, max-width: 100%): a two-class rule sets margin-inline: 0 and the cap. With the built-in adapter nothing changes.

Spacing. evidence-sheet's gap above the chips that sit under the opening (split rails) is --tome-dossier-opening-chips-gap, the --tome-space-2xl step when unset.

Editorial CTA register. ledger's and shift-rows' CTAs read the house editorial CTA register from @wabbit/tome-blocks-house (--tome-house-cta-editorial-family, -size, -weight, -tracking, -transform, -margin, -color; see that package's README, "The editorial CTA register"). Unset, each falls back to the link's own value: the inherited sans, --tome-text-sm, 600, --tome-house-tracking-cta-editorial, uppercase, a --tome-space-2xl top margin, and the accent ink (--tome-color-accent-text in shift-rows, the band's --blk-accent-ink first in ledger). Set the tokens on a block's scope to give its CTA the mono editorial voice the other house blocks use. Both CTAs start their row rather than stretching across it, and on a grid root (a site that subgrids the block onto its page grid) they take --tome-house-cta-cols.

Shift rows label column and thumbnails. From 64rem the label column's top padding reads --tome-dossier-row-label-pad-start-wide, falling back to its --tome-space-md (the block padding on every side); the design source's value is the next step, var(--tome-space-lg), which sets the label a little lower beside the panels. The section marker reads --tome-dossier-marker-tracking, falling back to the house mono tracking (--tome-house-tracking-mono), so a site that retracks the mono labels can hold the marker where it was. An empty thumbnail slot's padding reads --tome-dossier-thumb-placeholder-pad (--tome-space-sm) its label's ink --tome-dossier-thumb-placeholder-ink (color-mix(in oklch, currentColor 70%, transparent)) and its outline --tome-dossier-thumb-placeholder-border (1px dashed color-mix(in oklch, currentColor 30%, transparent); none drops it). Shift Rows' CTA is an inline-block, so a label that wraps to two lines leaves no strut under the link's box.

Labels. Kickers, column labels and small captions use one mono label recipe: the mono face, weight 600 and the --tome-type-size-xxs step, uppercase with the house mono tracking. That includes record-roster's placeholder marks, evidence-plate's sources line and the stats line on proof-plates' situational cards.

Scroll reveal (opt-in)

Five blocks always reveal on scroll (case-file-row, evidence-plate, proof-plates, receipts-trio, record-roster). Thirteen more can, when the host asks: cost-ledger, spec-plate, spec-sheet, fit-list, fit-prose, compare-ledger, grants-ledger, phase-ledger, dated-ledger, practice-modes, zone-directory, term-ledger and wall-rows. The switch is the render context that RenderBlocks and RenderBlock from @wabbit/tome-blocks-core/render already forward to every renderer. Nothing is stored on the block, so there is no schema change, and a render without the key is the block's ordinary markup: no wrapper, class, attribute, inline style, stylesheet or client component.

import type { DossierRenderContext } from '@wabbit/tome-blocks-dossier-pack/render'

// The pack's own timing.
const context: DossierRenderContext = { dossierReveal: true }
// Or match your site's motion; every field is optional.
const tuned: DossierRenderContext = {
  dossierReveal: { duration: 0.56, stagger: 0.08, ease: 'expo.out', start: 'top 85%', rise: '1.625rem' },
}

Pass it as <RenderBlocks blocks={layout} context={context} />, or as context straight to a renderer. A context that already carries other keys (a proposal's, say) can carry this one beside them.

| Option | Default | What it sets | |---|---|---| | duration | --tome-motion-base on :root (300ms when unset) | Tween length, in seconds | | stagger | --tome-motion-stagger-base (80ms when unset) | Delay between the parts of one group, in seconds | | ease | power3.out | Any GSAP ease string, passed through unchanged | | start | top 85% | ScrollTrigger start, measured on each group's first part | | rise | 50px | How far pending parts sit below their place: px, rem or em |

An invalid value falls back to its default. Each block animates in groups, and each group starts when its first part reaches start: for example fit-list reveals its kicker and heading together, then its two columns 80ms apart, then its close; spec-plate and spec-sheet reveal as one group started by the block itself. Each renderer's doc comment lists its groups.

The five blocks that always reveal read the same key for their timing. They reveal with or without it; each field it sets replaces that block's own value, and each field it leaves out keeps it: case-file-row, evidence-plate and receipts-trio take duration and stagger from the motion tokens above with GSAP's power3.out; record-roster runs 0.56s with an 80ms stagger and GSAP's default ease (its built-in ease string is not one GSAP parses); proof-plates runs 0.56s, expo.out, 80ms. All five start at top 85% and rise 50px unless start or rise is passed. A kicker and the heading beside it (in case-file-row, receipts-trio and the card layout of proof-plates) reveal together as one step.

Render RevealGate from @wabbit/tome-blocks-house/reveal-gate once in the root layout (see that package's README). Pending parts are hidden by opacity only, and only once the gate has marked <html> before first paint, so content shows in full when JavaScript is off, blocked or fails; without the gate nothing is hidden and nothing moves. Under prefers-reduced-motion: reduce nothing is hidden and nothing moves either. The tween moves opacity and translate only, so nothing shifts layout, and when each part settles its inline reveal styles are cleared, so it renders exactly as it does with the reveal off.

Tile image fallback (opt-in)

With tile images on, a case-file-grid tile that has no populated upload of its own shows a placeholder. A host can supply an image for those tiles through the same render context as the reveal: a function that takes the tile (its fields, including ctaHref) and its index and returns a populated media document or an image URL, or nothing to keep the placeholder. It runs during render, so look anything up before rendering; a function that throws is treated as returning nothing. The tile's own upload always wins.

import type { DossierRenderContext } from '@wabbit/tome-blocks-dossier-pack/render'

// For example: each tile borrows the image of the page its link points at, loaded beforehand.
const context: DossierRenderContext = {
  dossierTileMedia: (tile) => heroByPath[tile.ctaHref ?? ''] ?? null,
}

Images that fill a box

case-file-grid tile images, record-roster logos, proof-plates captures, term-ledger's figure image and shift-rows' 96px thumbnails fill a box the block sizes itself. Each passes the media adapter className (fills the box), imgClassName (the image's own fit: a cover crop for tiles and the figure, the Image Display fit and focus for thumbnails, contain for logos, a cover crop from the top for captures), fill: true and a sizes value for the box's rendered width. The built-in adapter renders one <img> with both classes, as before. A site adapter that wraps its image should render it in fill mode over the wrapper, with imgClassName on the <img> and sizes passed through (see MediaProps in @wabbit/tome-blocks-core's README). The options come from fillMediaOptions in @wabbit/tome-blocks-house/components, shared by every house pack. A shift-rows thumbnail has no border unless its Image Display border is on; an empty thumbnail slot keeps a dashed outline.

Install into an existing Payload project

npm install @wabbit/tome-blocks-dossier-pack

@wabbit/tome-blocks-core and @wabbit/tome-blocks-house are required peers and install automatically (npm 7+/pnpm). Add the blocks you want to an existing blocks field and render them — see @wabbit/tome-blocks-core's "Add Tome blocks to an existing Payload project" for the full walkthrough:

// payload.config.ts
import { ledgerBlock, caseFileGridBlock } from '@wabbit/tome-blocks-dossier-pack'
// blocks: [...existingBlocks, ledgerBlock.block(), caseFileGridBlock.block()]
// blockComponents.ts
import { renderers as dossierRenderers } from '@wabbit/tome-blocks-dossier-pack/render/register'
import { adaptRenderersForPayload } from '@wabbit/tome-blocks-core/render'
// blockComponents: { ...adaptRenderersForPayload(dossierRenderers) }
@import '@wabbit/tome-blocks-core/styles.css';

Render through this package's RenderBlocks with the grid — skipping the grid class doesn't error at install or build time, it just renders every block flush against the viewport edge with zero gutters:

// src/blocks/RenderBlocks.tsx
import { RenderBlocks as TomeRenderBlocks, adaptRenderersForPayload } from '@wabbit/tome-blocks-core/render'
import { renderers as dossierRenderers } from '@wabbit/tome-blocks-dossier-pack/render/register'
import gridStyles from '@wabbit/tome-ui/grid'

const blockComponents = { ...adaptRenderersForPayload(dossierRenderers) }

export function RenderBlocks({ blocks }: { blocks: LayoutBlock[] }) {
  return <TomeRenderBlocks blocks={blocks} components={blockComponents} gridClassName={gridStyles.grid} />
}

Every renderer is typed React.FC<BlockRenderProps<T>> — it destructures { block, className, index }, not the block's own fields spread at the top level; that mismatch is the most common first-render crash. Image fields (case-file-grid's cases, proof-plates, receipts-trio's logos, record-roster's rows/logos, shift-rows, term-ledger) need the upload relationship populated at depth >= 1 — a bare, unpopulated id resolves to nothing and logs a one-time console warning, per @wabbit/tome-blocks-core's default media adapter.

Registering everything from scratch instead of adding to an existing field:

// payload.config.ts
import { BlockRegistry, BundleRegistry } from '@wabbit/tome-blocks-core/registry'
import { register } from '@wabbit/tome-blocks-dossier-pack'

const blockRegistry = new BlockRegistry()
const bundleRegistry = new BundleRegistry()

register(blockRegistry, bundleRegistry)
import { registerRenderers } from '@wabbit/tome-blocks-dossier-pack/render/register'

registerRenderers()

| Peer | Range | Required | |---|---|---| | payload | >=3.67.0 | yes | | @payloadcms/richtext-lexical | >=3.67.0 | yes | | react | >=19.0.0 | yes | | react-dom | >=19.0.0 | yes | | @wabbit/tome-blocks-core | >=0.22.0 <1.0.0 | yes | | @wabbit/tome-blocks-house | >=0.8.4 <1.0.0 | yes |

gsap is not a peer of this package at all — it is a real dependency of @wabbit/tome-blocks-house (a required peer above, backing this pack's entrance and ink-fill motion) and installs automatically with it.

Public API

| Export | Subpath | Description | |---|---|---| | One xBlock descriptor per block + register(blockRegistry, bundleRegistry) | . | Payload block configs and bundle registration | | Render components (Ledger, CompareLedger, ComparisonTable, TermLedger, CostLedger, GrantsLedger, ShiftRows, FitList, …) | ./render | Legacy self-registering render barrel | | readDossierReveal, DOSSIER_REVEAL_CONTEXT_KEY, DossierRevealOptions (type), DossierRenderContext (type) | ./render | The opt-in scroll reveal's context key, options and reader (see Scroll reveal) | | readDossierTileMedia, DOSSIER_TILE_MEDIA_CONTEXT_KEY, DossierTileMediaFallback (type), DossierTileMediaTile (type) | ./render | Case File Grid's tile image fallback (see Tile image fallback) | | renderers map + registerRenderers() | ./render/register | Explicit renderer registration | | getDemoProps(blockSlug, variant, ctx?) plus per-block demo functions | ./demo | Demo-props dispatcher feeding the auto-gallery route | | dossierPackBlockMeta | ./meta | Payload-free block metadata for a storefront gallery |

Server / client posture

Six files under src/render/ carry 'use client': four motion islands (CaseFileRowMotion, EvidencePlateMotion, ReceiptsTrioMotion, RecordRosterMotion, each *.client.tsx), ProofPlates.client.tsx, and DossierReveal.client.tsx, the opt-in scroll reveal's island, which renders only while the reveal is on. Every other renderer is a static server component, so blocks render from a server tree and hydrate only their motion.

Tokens and motion

Consumes @wabbit/tome-ui tokens exclusively, via @wabbit/tome-blocks-house's reduced field/background vocabulary — no literal hex/hsl/px in any render CSS Module, no hardcoded font-family. Every render component is self-contained and width-agnostic, per the house-pack convention in blocks-house/PORT-NOTES.md. case-file-row, evidence-plate, proof-plates, receipts-trio, and record-roster carry entrance-reveal motion (and, for evidence-plate, a sequential ink-fill pull statement) via @wabbit/tome-blocks-house/motion's animateInView/inkIn/readMotionTokens; twelve more can opt in through the render context (see Scroll reveal). Reduced motion is honored through a reactive prefers-reduced-motion subscription, so this pack has no dependency on @wabbit/tome-motion. Every scroll-reveal island that carries the pending state (case-file-row, evidence-plate, proof-plates, receipts-trio, record-roster and the opt-in reveal) follows the preference live: switching from reduced to normal motion after load plays the reveal, and switching to reduced motion shows every part in its finished state.

Blocks

case-file-grid

A scan strip of case-file/proof tiles — a mono case number, an italic-serif proof numeral (a stat, a result, a status), a one-line brief, and a subject name, with an optional arrow CTA per tile. Three variants (default, field-manual, filmstrip) cover a receipts-band register, a tighter architecture-document register, and an edge-to-edge photographic filmstrip. An optional gold CTA panel can close the row as its final tile.

When to use
  • A "receipts" or "proof" section presenting several short case results side by side
  • A portfolio strip where each entry needs a headline stat plus a one-line context brief
  • A closing row that should end on a call-to-action tile rather than a plain link
Page types
  • landing
  • about
  • marketing
  • portfolio
How to use

Author 2-5 tiles: a case number, a proof value, an optional brief, and a subject. Toggle Tile Images on to add a header image per tile — untouched tiles render a bracketed placeholder. Pick a Variant: default (receipts band), field-manual (tighter document register), or filmstrip (full-bleed photographic strip, requires Tile Images on). Set a CTA Panel heading to close the row with a call-to-action tile instead of a plain link.

Pairs with
  • evidence-sheet
  • exhibit-artifact
  • proof-plates
Avoid when
  • You need a single full-bleed case study, not a row of short ones — use exhibit-artifact instead
  • The entries carry no comparable numeral/stat — use a plain card grid instead
Register in

marketing-landing

case-file-row

A single dark band opening with a lane kicker beside a headline, then a bordered row of up to 6 stat cells — each a kicker, a stat, a supporting line, and an optional name + plain link — closing on an anchored see-all link. One cell can be flagged to invert to a light card, useful for calling out the one honest or unusual result in an otherwise uniform set.

When to use
  • A receipts/results row that needs plain, unstyled outbound links rather than relationship-driven cards
  • A "the record" band closing a case-study or evidence section
  • A moment that wants exactly one result visually set apart from its peers
Page types
  • landing
  • about
  • marketing
  • editorial-article
How to use

Write a kicker and heading (wrap a phrase in *asterisks* for an accent run). Add up to 6 cells: kicker, stat, line, name, and an optional plain href + link label. Flag exactly one cell to invert it to a light card. Close with a see-all label + URL.

Pairs with
  • case-file-grid
  • evidence-sheet
  • receipts-trio
Avoid when
  • Each entry needs a real relationship-driven link with a resolved reference — use case-file-grid or proof-plates instead
  • You need more than 6 entries — split into multiple rows or use case-file-grid
Register in

marketing-landing

evidence-sheet

A dossier masthead: mono kicker strip, then a set of spec rows (a mono label + a value, with an optional muted note and an emphasis treatment for the standout row), plus two optional chip rows — Scope (outlined pills) and Stack (filled pills). Four treatments reflow the same data: Table (label-left/value-right), Grid (a 3-column cell grid), Strip (one compact hairline row), and Ledger (a tall narrow column, optionally paired with a short Opening Narrative beside it). An optional call to action shows in all four treatments and can be switched off with Show call to action.

When to use
  • A project/engagement masthead summarizing client, scope, timeline, result
  • A compact spec sheet before or after a case study
  • A verticalized "at a glance" ledger paired with a short narrative opener
Page types
  • landing
  • about
  • marketing
  • dossier
How to use

Fill in Spec Rows (label + value, optional note, optional emphasis). Add Scope/Stack chips as needed. Pick a Treatment: Table for the default wide spread, Grid for a 3-column reflow, Strip for one compact glance row, or Ledger for a tall narrow column — Ledger unlocks the optional Opening Narrative field group beside it, and Ledger Rails: Split spreads a long ledger over two rails with the narrative between them and the chips under it. Add a Call to action to link to the full study: it follows the evidence in Table, Grid and Strip, and sits under the opening narrative in Ledger (under the ledger when there is no narrative). Untick Show call to action to hide it.

Pairs with
  • case-file-grid
  • exhibit-artifact
  • proof-plates
Avoid when
  • You need a single narrative paragraph with no label/value structure — use a plain content block
Register in

marketing-landing

evidence-plate

An evidence moment: an eyebrow, up to 3 stats (two large serif numerals plus an optional smaller, muted "source" cell), a body paragraph, optional linked source citations, and a closing pull statement whose two runs ink-fill in sequence on scroll.

When to use
  • A data-backed argument that needs real, citable numbers
  • A moment that wants to land a two-part statement with a visual ink-fill reveal
Page types
  • landing
  • about
  • marketing
  • editorial-article
How to use

Add up to 3 stats (numeral + value + label; flag the source cell as Muted). Write a body paragraph and, if citing external data, add Sources (label + URL — each renders as a real link). Fill the Ink-in pull with two runs: the first inks in the body ink color, the second in the accent color.

Pairs with
  • case-file-row
  • evidence-sheet
  • proof-plates
Avoid when
  • The numbers are not real/citable — use a plain statement block instead
Register in

marketing-landing

exhibit-artifact

A single exhibited document — the "here is the actual artifact" answer to a case study or study section that has no photograph to show. A tag chip labels the exhibit; the inner card carries header rows (e.g. From/To/Re), a subject line, a rich-text body, and a signature; a centered caption sits below.

When to use
  • A case study with no real photograph to show, but a real communication/document to reproduce
  • A moment that benefits from a letter, memo, or transcript-style artifact
Page types
  • dossier
  • about
  • landing
How to use

Set a Tag (the chip label). Add Header Lines (label/value rows), a Subject Line, a rich-text Body, and a Signature. Add a Caption to describe the artifact below it, and an optional CTA link.

Pairs with
  • evidence-sheet
  • case-file-grid
  • proof-plates
Avoid when
  • A real photograph exists for this moment — use a media block instead
Register in

marketing-landing

proof-plates

A "what it runs on" or "here is the proof" section: a kick label + heading + intro, then 2-4 proof plates, each with a kicker, title, body, an optional stats line, an optional inline continuation link, and an optional proof-capture image. One plate can be flagged to render as an inverted, emphasized card. Two shells are available: Strip (one joined bordered row) or Situational (separate floating cards with their own border/shadow) — Auto derives the shell from whether any plate carries a kicker.

When to use
  • Comparing 2-4 concrete deployments, tiers, or outcomes side by side
  • A chooser section where each option needs its own floating card
  • A closing proof row that wants one option visually flagged as the default/flagship
Page types
  • landing
  • pricing
  • marketing
How to use

Write a kick label, heading (wrap a phrase in *accent*), and intro. Add 2-4 plates: kicker, title, body (supports **bold** and *em* runs), an optional stats line, and an optional inline link. Flag one plate for emphasis. Choose a Shell, or leave Auto to let the kicker-presence heuristic decide. Add a proof-capture image per plate where a real screenshot exists.

Pairs with
  • evidence-sheet
  • case-file-grid
  • exhibit-artifact
Avoid when
  • You have more than 4 comparable items — use a table or a card grid instead
Register in

marketing-landing

receipts-trio

A single dark band making the case for showing real results, not just wins: a kicker, a heading (with an optional accented run), a prose paragraph making the honesty argument, and up to 3 quote cards (quote + name + role) — deliberately built to hold an honest, non-flattering result alongside the wins.

When to use
  • A section arguing for transparency — "here is what actually happened," wins and losses both
  • A quote-grid closing a methodology or process section
Page types
  • landing
  • about
  • marketing
How to use

Write a kicker and heading (wrap a phrase in *asterisks* for an accent run). Write the body paragraph making the honesty case. Add up to 3 quotes — keep any non-flattering one if the source material includes it; the point of this block is not to only show wins.

Pairs with
  • case-file-row
  • evidence-plate
Avoid when
  • Every quote is a straightforward win — a simple testimonial block is a lighter fit
Register in

marketing-landing

record-roster

A single "the record" section: a top-ruled kicker, a ledger of rows (an index, a bold value, and a description), a hairline-bordered logo grid, and an optional centered coda line. The logo grid's cell count follows the number of logos actually uploaded — up to 10 — with empty cells (when fewer than the configured maximum are provided) rendering a numbered mono placeholder.

When to use
  • A social-proof close listing client/partner logos alongside a short ledger of facts
  • A "the numbers" section that wants a real logo wall, not a generic grid
Page types
  • landing
  • about
  • marketing
How to use

Write a kicker. Add ledger rows (index, value, description) — keep numbering real and complete. Add up to 10 logos; the grid sizes itself to however many you add. Add a coda for a short centered closing line.

Pairs with
  • case-file-grid
  • proof-plates
Avoid when
  • You have no real logos to show — an invented or placeholder-only grid is not appropriate here
Register in

marketing-landing

compare-ledger

A ruled-out-vs-full-ink comparison: 2-3 columns each with a mono label and a display value (all but one struck through), then a table of paired rows contrasting the same options attribute by attribute, closed by a short anchored statement. Every field is plain text — no catalog or pricing peer required.

When to use
  • Contrasting the old way against the new way (or a competitor against your own approach) as a single honest display
  • A "before you decide" section that shows what changes across 2-3 options, row by row
  • Closing an argument with a visual verdict rather than a paragraph of prose
Page types
  • landing
  • marketing
  • dossier
How to use

Add 2-3 `columns`, each with a label, a display value, and an "emphasis" flag on the one option that should render in full ink (the rest render struck through, muted). Add up to 6 `rows`, each with one cell per column in column order. Optionally close with an anchored `closeText` statement. For an N-column feature matrix with per-cell Yes/No/tone values, use `comparison-table` instead — this block is for a short, opinionated 2-3-way contrast, not an exhaustive feature grid.

Pairs with
  • comparison-table
  • cost-ledger
  • term-ledger
Avoid when
  • You need more than 3 columns, or a feature-by-feature grid with many rows — use `comparison-table`
  • The comparison is really just one product's price against nothing — use `price-table` instead
Register in

dossier

comparison-table

A feature-by-feature comparison table: 2-6 columns (the options), 1-24 rows (the capabilities), and a cell per intersection with an optional value string and tone (default/affirmative/absent). Columns and rows are free text, so this serves plan comparisons, package matrices, spec sheets, or before/after grids equally. Cells are positional — cell N in a row belongs to column N.

When to use
  • "Which one do I need?" pages comparing 3+ options across many capabilities in a single scan
  • A specification sheet where every option needs the same set of attributes checked off
  • A feature matrix accompanying a pricing or plan page, distinct from the tiers themselves
Page types
  • pricing
  • landing
  • marketing
  • docs
How to use

Add a headline (required) and optional eyebrow/description. Add 2-6 `columns`, each with a label and an optional highlight flag for the recommended option. Add 1-24 `rows`, each with a label and a `cells` array — cell order follows column order; a missing cell renders blank rather than shifting the table. Give a cell a `tone` of "affirm" (accent, bold) or "absent" (muted) to make a column read as a shape at a glance. Three options, all off by default: `railHeader` sets the eyebrow in a narrow ruled column beside a large headline (whose `*accent*` runs drop to their own line), `emphasisHighlight` gives highlighted columns a solid fill instead of the subtle tint, and `descriptionAboveTable` moves the description out of the header to sit directly above the table. For a short 2-3-way opinionated contrast rather than an exhaustive grid, use `compare-ledger` instead.

Pairs with
  • price-table
  • compare-ledger
  • term-ledger
Avoid when
  • You are comparing exactly 2-3 options with a handful of contrast points, not an exhaustive feature grid — use `compare-ledger`
  • There is only one offer with one price — a plain `price-table` or product CTA is enough
Register in

dossier

term-ledger

A ruled list of term + rich-text body pairs — "what changes", "what this covers", a glossary, or a spec sheet read as a ledger rather than a table. The variants share one row template and differ only in tone: `shifts` (light band, bold display term), `spec` (dark band, mono accent term) and `spec-measure` (the `spec` tone with each row body held to the prose measure). An optional supporting image can sit beside the rows, and an optional anchored line closes the section.

When to use
  • Listing what changes, what is included, or what a term means, one row per item, with room for a paragraph of explanation per row
  • A "how it works" or "under the hood" section that reads better as short labeled rows than as a table
  • Pairing a small number of terms with a supporting photo or diagram beside the list
Page types
  • landing
  • marketing
  • docs
How to use

Set the variant: `shifts` for a light band with a bold display term (the default, everyday case), `spec` for a dark, technical-feeling band with a mono accent term, `spec-measure` for the same dark band with row bodies capped at the prose measure (`--tome-prose-max-width`). Add an optional kicker, a heading (`*accent*` runs render italic accent; set `headingFace` to `display` for a larger, light serif heading), and intro rich text. Add `rows` — each needs a term and rich-text body. Optionally add a supporting figure (image + a placeholder note for when no image is set yet) that renders beside the rows. Close with an optional anchored statement (`**strong**`/`*em*` runs supported). For a strict 4-column forensic ledger (line/category/cost/what-compounds) use `cost-ledger` instead; for a feature matrix use `comparison-table`.

Pairs with
  • cost-ledger
  • comparison-table
  • compare-ledger
Avoid when
  • The content is fundamentally tabular with many columns — use `comparison-table`
  • Each row needs a fixed line/category/cost/compounds shape — use `cost-ledger`
Register in

dossier

cost-ledger

A forensic-accounting-styled ledger: an optional kicker + heading + intro, a 3-column strip header labeling the row shape (line/category, the debit, what compounds), up to 6 rows each with a line number, category, a plain-language debit, and what it compounds into — with exactly one row markable as "lit" to render as a dark accent plate — closed by a coda kicker + centered statement naming the total cost.

When to use
  • Making the cost of inaction or a status quo concrete, one line item at a time
  • A "here is what this actually costs you" section building toward a single closing statement
  • Highlighting one specific cost line as the fact the reader cannot unsee
Page types
  • landing
  • marketing
  • dossier
How to use

Add an optional kicker, heading (`*accent*` runs render italic accent), and intro paragraph. Set the three column-strip labels (line/category, the debit, what compounds). Add up to 6 `rows`: line (e.g. "L01"), category, the debit in plain language, what compounds, and a "lit" flag — mark exactly one row lit to render it as the dark plate the reader cannot unsee. Close with a codaKicker + codaText naming the total. For a strict term/definition ledger without the lit-row device, use `term-ledger`; for a multi-option comparison, use `compare-ledger` or `comparison-table`.

Pairs with
  • term-ledger
  • compare-ledger
  • comparison-table
Avoid when
  • You need more than 6 rows or a general feature matrix — use `comparison-table`
  • There is no single "cost" narrative — a plain `term-ledger` fits a neutral list of terms better
Register in

dossier

ledger

A four-column ledger table (Line / Category / Cost / What Compounds) built from 2-10 rows, with one optional accent ("emphasis") row for a running total, and an optional closing note rendered on a dark inset band beneath the table. An optional top strip (label + right-aligned section locator) can run above the headline, and an optional CTA link can run below the block. Two variants: default (full ledger with top strip) and compact (denser rows, no top strip).

When to use
  • Breaking down the hidden or compounding cost of a decision, delay, or status quo into discrete line items
  • A "here is what this actually costs" section that needs to read as itemized and audited rather than a single claim
  • Closing an argument with one line-item table plus a single summary verdict on an inset band
Page types
  • landing
  • marketing
  • about
  • dossier
How to use

Author 2-10 `rows`, each with a line id, category, cost (debit) string, and an optional "what compounds" note; mark at most one row `emphasis` to accent it (typically a running total). Column header labels are editable and default to LINE / CATEGORY / THE COST (DEBIT) / WHAT COMPOUNDS. Add an optional `closingNote` for a summary verdict rendered on a dark inset band beneath the table, with its own `closingLabel` kicker. Use `compact` when the ledger sits inside a denser section and does not need the top strip.

Pairs with
  • shift-rows
  • fit-list
  • grants-ledger
Follows
  • fit-list
Avoid when
  • There are fewer than 2 comparable line items — a single stat or a short paragraph makes the point faster
  • The cost is a single number rather than several compounding factors — use a stat block instead
Register in

dossier

grants-ledger

A real <table> feature matrix: an optional lane kicker + heading above it, then columns (one per plan/tier, each with a label) and rows (one per feature/grant, each with one cell per column). Any column can be flagged `zone2` for a fully inverted dark/accent treatment (e.g. an "Agency" or enterprise tier that reads as categorically different, not just pricier); a separate optional `highlightedColumnIndex` marks exactly one column with a lighter "featured" tint, for when a plan should stand out without the full inversion. Any cell can be flagged `conv` for a mono accent treatment (e.g. "By conversation" instead of a plain value).

When to use
  • Comparing 3+ plans, tiers, or offers feature-by-feature in a real table, not a card grid
  • One column needs to read as categorically different from the others (e.g. an enterprise/agency tier reached by conversation, not checkout)
  • A single column should be called out as recommended without going all the way to a fully inverted band
Page types
  • pricing
  • landing
  • marketing
How to use

Author `columns` (one per plan) and `rows` (one per feature); each row's `cells` are positional against `columns` — `cells[i]` belongs to `columns[i]`. Set `zone2: true` on at most one or two columns for the full dark/accent inversion; set `highlightedColumnIndex` to a 0-based column index for a lighter featured tint on a different column. Set `conv: true` on a cell whose value should read as a conversational/mono-accent exception (e.g. "Conversation") rather than a plain value.

Pairs with
  • price-table
  • ledger
  • fit-list
Follows
  • fit-list
Avoid when
  • There are only 1-2 columns to compare — a simple two-column split reads faster than a table
  • Every column is equivalent with no tier needing special emphasis — a plain comparison table without the zone/highlight fields is simpler
Register in

dossier

shift-rows

A numbered list of 2-6 rows, each a literal before/after transformation: an optional thumbnail + label + short caption, a BEFORE panel (headline + body), an arrow, and an AFTER panel (headline + body) styled as the positive outcome. An optional heading/eyebrow/section-marker runs above the rows and an optional italic footer note and CTA link run below. Reuses copy that is already phrased as a shift rather than a list of features.

When to use
  • Every item already reads as "X becomes Y" — a timeline compressing, a cost dropping, a process simplifying
  • A "here is what changes when you work with us" section built from 3-4 concrete transformations
  • Reinforcing a single argument with a repeated visual rhythm (label / before / arrow / after) rather than a paragraph per point
Page types
  • landing
  • marketing
  • about
How to use

Author 2-6 `rows`; each needs a label, a before headline, and an after headline (body copy and a thumbnail are optional). When any row sets a thumbnail, the thumbnail column activates for every row — rows without one render a blank placeholder frame. Use the shared Image Display controls to set the thumbnail fit/border/alignment. Keep rows to the 3-4 sweet spot; more than that dilutes the rhythm.

Pairs with
  • ledger
  • fit-list
  • grants-ledger
Follows
  • fit-list
Avoid when
  • The benefits are not naturally before/after pairs — a plain feature list or `stat-bar` fits better
  • There is only one transformation to show — a single stat or a short paragraph makes the point without the row apparatus
Register in

dossier

fit-list

A self-qualification section: an optional kicker + heading, then two ruled columns — a "build" list of conditions where the offer fits, and a muted "skip" list of conditions where it does not — closed by an optional anchored statement. Reads as candor rather than a sales pitch because it names the cases where the reader should walk away.

When to use
  • Helping a visitor self-select in or out before they contact sales
  • Building trust by naming who the offer is NOT for, alongside who it is
  • Closing a pitch section with an honest qualifier rather than another benefit claim
Page types
  • landing
  • marketing
  • pricing
How to use

Author up to 6 `buildItems` and up to 6 `skipItems` (short, single-line conditions each). Add an optional `kicker` + `heading` above the columns and an optional `closeText` anchored statement below — both accept `*em*` runs for an accented span. Keep each item to one line; this block is a scanning aid, not a place for paragraphs.

Pairs with
  • ledger
  • shift-rows
  • grants-ledger
Precedes
  • ledger
Avoid when
  • The offer has no real disqualifying cases — a forced "skip" list reads as false modesty
  • The comparison needs more than two columns or per-item detail — use `grants-ledger` instead
Register in

dossier

fit-prose

A qualifying section written as two facing prose plates — one paragraph for who this is NOT for, one for who it IS for — each carrying its own label, then a single anchored closing statement (e.g. a guarantee or refund line). Plain prose paragraphs, never a checklist.

When to use
  • A sales or landing page section that pre-qualifies the reader before the pitch continues
  • An offer page that wants to repel the wrong buyer as deliberately as it attracts the right one
  • A closing statement (guarantee, refund line, commitment) that needs to sit directly beneath a fit judgment rather than float alone
Page types
  • landing
  • marketing
  • about
How to use

Author an optional kicker + heading (wrap a phrase in *asterisks* for the accent run), then a not-fit label/body and a fit label/body — both plain prose paragraphs. The optional anchored close renders as one statement beneath a rule (also accepts *asterisk* accents). Any missing piece collapses cleanly: the block renders nothing when heading, both bodies, and the close are all empty.

Pairs with
  • spec-sheet
  • spec-plate
  • zone-directory
Avoid when
  • The comparison is a checklist of features rather than a spoken paragraph — a bulleted compare block fits better
  • There is no honest "not for you" case to state — a one-sided pitch does not need this device
Register in

marketing-landing

zone-directory

A directory board splitting one decision into 2-4 named zones — each cell is a whole-cell link carrying an outline numeral, a kicker, a title, a short description, and a go label. One or more cells can flip to an inverted dark register independent of the block's own background, for visually separating a self-serve path from a higher-touch one.

When to use
  • A page that offers the reader 2-4 distinct next steps or paths and needs each one named and justified before the link
  • A "how you work with us" section splitting self-serve vs. guided engagement
  • A landing page moment that wants the decision itself to read as the page's own design device
Page types
  • landing
  • marketing
  • about
How to use

Author an optional eyebrow + heading (wrap a phrase in *asterisks* for the accent run) + description, then 2-4 cells: each takes a number (renders as an outline numeral), a kicker, a title, a description, a go label (type the arrow glyph yourself — the component does not add one), an href, and an optional dark toggle to invert that one cell's register. The board's column count and border layout adapt automatically to however many cells (2, 3, or 4) are authored.

Pairs with
  • fit-prose
  • spec-sheet
  • spec-plate
Avoid when
  • There is only one path forward — a single CTA needs no directory
  • More than 4 destinations are being compared — this reads as a considered decision, not a menu; use a larger grid block instead
Register in

marketing-landing

spec-sheet

A row of up to 4 label/value cells on a bordered strip — the shape of an engagement's or offer's terms at a glance (Duration, Fee, What happens if you proceed, …), with an optional muted note line underneath. Cells with neither a label nor a value are dropped rather than rendered empty.

When to use
  • Stating the concrete terms of a paid engagement, sprint, or audit directly on the page (duration, price, next step)
  • A proposal or process page that needs a scannable terms strip near a CTA
  • Any place a handful of short facts belong side by side, not in prose
Page types
  • landing
  • marketing
  • about
How to use

Author 1-4 cells, each an optional label + value pair; the strip's column count matches however many cells actually have content, so the row fills edge to edge without a phantom empty column. Add an optional note for a single muted qualifying line beneath the row. The block renders nothing if every cell is empty and no note is set.

Pairs with
  • spec-plate
  • fit-prose
  • zone-directory
Avoid when
  • There are more than 4 terms to state, or the values need explanation — use `spec-plate`'s larger grid with its heading + lede instead
  • The content is really prose, not discrete label/value facts
Register in

dossier

spec-plate

The technical buyer's cheat sheet — a heading (with an optional larger display face) and a one-sentence lede explaining why the detail exists, followed by up to 6 label/value cells (framework, hosting, integrations, …) each with an optional small muted note under the value. The 3-column desktop grid degrades to 2 columns on narrower viewports with its own border rewiring, never collapsing to 1.

When to use
  • Listing the technical stack or specs behind a build for a reader who asks, without making it the pitch
  • A "Under the Hood" or "Tech Specs" section on a platform, product, or case-study page
  • Launch-detail lists (ownership terms, what's included) that read as facts, not marketing copy
Page types
  • landing
  • marketing
  • about
How to use

Author an optional heading (wrap a phrase in *asterisks* for the accent run; `headingFace: display` renders it at the larger elevation-page serif size) and lede, then up to 6 cells: each takes a label, a value, and an optional small note line beneath the value. Use `spec-sheet` instead when there is no heading/lede and 4 or fewer plain terms to state — the two blocks are intentionally not merged; this one carries a head and a larger cell cap.

Pairs with
  • spec-sheet
  • fit-prose
  • zone-directory
Avoid when
  • There is no heading or lede to frame the cells — `spec-sheet` is the leaner fit
  • More than 6 facts need stating — split into two plates rather than cramming the grid
Register in

dossier

lab-notes

A candid "here is what we tried and what we learned" disclosure beat, styled as a field-notebook entry: an optional top strip (label + right-aligned locator), an italic-serif headline and subtitle, then 2-8 labeled entries — a mono kicker plus a body line — running down a continuous margin rule, with one entry markable as the emphasis landing. Closes with an optional handoff line: a label, a body sentence, and a link, either in its own cell or glued inline at the end of the sentence.

When to use
  • A retrospective or "we tried this, and here is what happened" disclosure section
  • A sequence of attempt/outcome/lesson entries that reads like lab notes or a changelog narrative
  • A closing handoff that sends the reader to a follow-up resource or a next step
Page types
  • editorial-article
  • about
  • docs
How to use

Author a headline and 2-8 entries (kicker + body; mark one as emphasis for the accent landing). Add an optional italic subtitle under the headline, and an optional closing handoff (label + body + link). Toggle the top strip off via "Show Section Header" without losing its text — useful when this block follows another one closely. Switch spacing to "Spacious" for an airier, larger-type treatment.

Pairs with
  • phase-ledger
  • practice-modes
  • wall-rows
Avoid when
  • The content is a forward-looking roadmap rather than a retrospective — use `phase-ledger` instead
  • There is only one point to make — a single paragraph beats the notebook conceit
Register in

dossier

phase-ledger

A curriculum- or process-ledger section: an optional lane kicker + serif heading + rich-text intro, then 1-9 numbered phase rows under a 2px top rule, each carrying a locator (a phase number and a bordered chip, e.g. "21 lessons"), a title, and a body paragraph with an italic outcome line. Reads as a syllabus or a roadmap made literal — the numbering is meant to be real and complete, not decorative. Four variants: default (the ruled rows); rail (the same rows beside a progress rail that fills as the section is read, each phase dot lighting when reached; fully drawn under reduced motion or without JavaScript); index (a numbered index of ruled rows, each with a byline and an optional link covering the row, for a list of areas or sections that each have their own page); and panel (one large media panel above a row of numbered steps that the reader clicks, arrows or, from 768px, scrolls through; every step listed in full without JavaScript).

When to use
  • A course or program page listing its phases/modules in order, each with a scope and an outcome
  • A step-by-step process or roadmap where each step needs more than a one-line label
  • Proving a curriculum is real and complete — the register gate is exactly this: no gaps, no out-of-order phases
  • An index of numbered areas or sections, each linking to its own page (`index`)
  • A short process of 2-6 steps where a picture per step helps, shown one step at a time (`panel`)
Page types
  • docs
  • about
  • editorial-article
How to use

Author 1-9 phases in strict order, each with a number, an optional chip tag, a title, a body paragraph, and an optional italic outcome line. `*accent*` runs in the heading render as the band-relative accent-italic style. Keep the phase count real — gaps or reordering break the register the block is built to prove. Pick `rail` when the order itself is the point (a process the reader moves through), so the filling rail shows how far along they are. Pick `index` for a numbered list of areas that each have a page: set `link` (label and URL) per row and use `outcome` for the byline. Pick `panel` for a stepped process with a picture per step: set `image` and `meta` per phase, keep it to 2-6 steps, and keep the text of each step short enough to read beside the picture.

Pairs with
  • lab-notes
  • practice-modes
  • wall-rows
Avoid when
  • There are fewer than 2-3 real steps — a single paragraph or a `stat-bar` communicates it faster
  • The steps are interchangeable/parallel rather than sequential — a card grid fits better than a numbered ledger
Register in

dossier

practice-modes

A two-mode comparison section for a single practice or approach that comes in exactly two flavors — e.g. "Compressed" vs "Expanded." A kicker-lane heading sits above two ruled, equal-weight prose columns (label + body each), closing with an anchored statement and an optional real link off to the side.

When to use
  • Explaining that the same practice/method has exactly two usable modes and describing each
  • A "how people actually use this" section that needs two contrasting descriptions, not a feature list
  • A closing statement that anchors the section with an optional pointer to more detail
Page types
  • editorial-article
  • docs
  • about
How to use

Author exactly two modes (label + body each). Add an optional kicker and heading above them (`*em*` runs render serif-italic accent). Close with an anchored statement (`*em*` runs render the same way) and, optionally, a real link — leave both close-link fields blank if the beat has no follow-up.

Pairs with
  • lab-notes
  • phase-ledger
  • wall-rows
Avoid when
  • There are more than two modes/variants to compare — a card grid or table fits better
  • The two things being compared are not equal in weight — an asymmetric layout communicates that better than two even columns
Register in

dossier

wall-rows

A "here is who asked and why the answer was no" section: a kick rule + serif heading (with `*accent*` runs) + rich-text intro, then a bordered, rounded ledger of rows — each carrying a who, a need, and a one-word verdict repeated as the row's own anatomy (e.g. "Wall.") — closing with an anchored statement (`**strong**` runs render semibold).

When to use
  • Listing a set of asks/requests and the same verdict landing on each one, to make a pattern visible
  • A "we said no to this, repeatedly" section where the repetition of the verdict word IS the point
  • A closing statement that needs a bordered, scannable ledger of examples behind it
Page types
  • editorial-article
  • about
  • docs
How to use

Author a kick label, a heading (`*accent*` runs render serif-italic accent), and an intro rich-text paragraph. Add any number of wall rows — each with a required "who," an optional "need" body, and an optional verdict word rendered bold and large at the row end. Close with an anchored statement; `**strong**` runs render semibold.

Pairs with
  • lab-notes
  • phase-ledger
  • practice-modes
Avoid when
  • Every row has a different verdict — the block's whole point is ONE repeated verdict word; use a table instead if verdicts vary
  • There is only one example — a single pull quote communicates it with less furniture
Register in

dossier

dated-ledger

A dated log of recent, real entries: an optional lane kicker + heading + rich-text intro, then 1-12 entries. Each entry is led by its date as a large day numeral with a month, year and reference line in tabular figures, then an optional photo, a chip (a place or a category), a title, one result line and an optional big figure, with an optional per-entry link. An optional "more" link closes the log. Two variants: columns (up to four per row) and rows (ruled rows with the date and reference in the left lane and the figure in the right lane). Options: the date as a day (default) or a year (`datePrecision`), a regular or compact density (`density`; compact for short entries such as awards), a compliance label per entry (`complianceLabel`) and a closing note under the entries (`note`).

When to use
  • A log of recently finished work where the date and a reference number prove each entry is real
  • A news-like record of dated outcomes (cases closed, releases shipped, projects delivered) with one result per entry
  • A "related entries" strip under a detail page, linking to each entry
  • Past results or awards by year, each with the label an advertising rule asks for (`datePrecision: year`, `complianceLabel`, `note`)
Page types
  • landing
  • marketing
  • about
  • dossier
  • portfolio
How to use

Author 1-12 entries newest first, each with a date, an optional reference (e.g. a job or case number), an optional chip, a title, one result line, an optional figure (value + label), an optional photo and an optional link. Pick `columns` for a scan strip of up to four entries per row, or `rows` for a longer ruled ledger with the figure in the right lane. Add a "more" link to point at the full record. Set `datePrecision` to `year` when only the year matters, `density` to `compact` for a list of short entries (title, one line, a chip such as who it was for), `complianceLabel` on any entry that states a result your rules require a disclaimer for, and `note` for a closing source line or disclaimer.

Pairs with
  • phase-ledger
  • case-file-row
  • record-roster
Avoid when
  • The entries have no real dates — a card grid or `case-file-grid` fits better than a dated record
  • There is only one entry — a single case study block tells it better
Register in

dossier

Exports

  • @wabbit/tome-blocks-dossier-pack
  • @wabbit/tome-blocks-dossier-pack/render
  • @wabbit/tome-blocks-dossier-pack/render/register
  • @wabbit/tome-blocks-dossier-pack/demo
  • @wabbit/tome-blocks-dossier-pack/meta

Changelog

v0.14.0minor

0435b77: `dated-ledger`: no stray separator under a year, and the chip can move into the date line. - With `datePrecision: 'year'` in `rows`, from 640px the year numeral takes its own line, so the line under it held only the reference, opened by the `·` separator ("· No. 1"). The separator is hidden there. Day precision, and every other layout, is unchanged. - New option `chipPlacement` (`body` by default, or `dateline`). With `dateline`, each entry's chip leaves the body and closes the date line on a line of its own, under the date and reference (`[data-ledger-date] [data-ledger-chip]`), so in `rows` it sits in the left margin under "No. 1". The root carries `data-ledger-chip-placement="dateline"`, and the gap above the chip is `--tome-dossier-ledger-dateline-chip-gap` (default `0.5rem`). Unset or `body` renders exactly as before.

  • 0435b77: `dated-ledger`: no stray separator under a year, and the chip can move into the date line. - With `datePrecision: 'year'` in `rows`, from 640px the year numeral takes its own line, so the line under it held only the reference, opened by the `·` separator ("· No. 1"). The separator is hidden there. Day precision, and every other layout, is unchanged. - New option `chipPlacement` (`body` by default, or `dateline`). With `dateline`, each entry's chip leaves the body and closes the date line on a line of its own, under the date and reference (`[data-ledger-date] [data-ledger-chip]`), so in `rows` it sits in the left margin under "No. 1". The root carries `data-ledger-chip-placement="dateline"`, and the gap above the chip is `--tome-dossier-ledger-dateline-chip-gap` (default `0.5rem`). Unset or `body` renders exactly as before.
  • e3aadfe: Theme hooks on links reach the DOM. - `@wabbit/tome-blocks-core`: `LinkProps` declares `data-*` and `aria-*` attributes, and the default anchor (`NoopLink`, used by `TomeLink` / `resolveLink` when no link adapter is registered) forwards them. It forwarded only `href`, `className`, `style`, `id`, `target` and `rel`, so every hook a pack put on a link (`data-section-label-link`, `data-panel-cta` and others) was dropped. Links without such attributes render exactly as before, and props that are neither `data-*` nor `aria-*` still stay off the anchor. A registered link adapter receives the attributes too: spread the rest of your `Link`'s props onto its anchor to keep them. - `@wabbit/tome-blocks-dossier-pack`: each `zone-directory` cell (the link anchor) carries `data-zone-cell`.
v0.13.0minor

6928b7f: Phase Ledger gains `index` and `panel` variants, and Dated Ledger gains year dates, compliance labels, a note and a compact density. - **Phase Ledger `index`**: a numbered `<ol>` of ruled rows (number, title, body, and a byline column with the outcome and an optional link line). Each phase's new `link` (`label`, `href`) covers the whole row. Hooks: `data-phase-index`, `data-phase-row`, `data-phase-link`; the number keeps `data-phase-num` so a theme can set a mark before it. - **Phase Ledger `panel`**: one large media panel above a step bar. Phases gain optional `image` and `meta`. Without JavaScript every step is listed in full; once hydrated the step bar is an ARIA tablist (click, arrow keys, Home and End), and at 768px wide and 600px tall the steps also follow the scroll along a pinned runway. Phones and short windows are never pinned. Under reduced motion the jump to a step is instant and nothing crossfades. The island root carries `data-tome-motion="self"`. Hooks: `data-phase-panel`, `data-phase-slide`, `data-phase-media`, `data-phase-meta`, `data-phase-tabs`, `data-phase-tab` (its label `data-phase-num`, its name `data-phase-title`), `data-phase-active`, plus the root's `data-panel-state`, `data-panel-ready` and `data-panel-follow`. - **Dated Ledger**: `datePrecision: 'year'` makes the year the large numeral (`data-date-precision`, `data-ledger-year`); `density: 'compact'` tightens the rows for short entries such as awards (`data-ledger-density`); each entry's `complianceLabel` closes the entry (`data-compliance-label`); a block `note` follows the entries (`data-ledger-note`, reveal group `note`). - **Dated Ledger label lane:** from 1024px a theme can redraw the `rows` grid of a labelled ledger to move the compliance label (for example into a right-hand lane) with `--tome-dossier-ledger-label-areas` / `--tome-dossier-ledger-label-cols` (entries without a photo) and `--tome-dossier-ledger-label-media-areas` / `--tome-dossier-ledger-label-media-cols` (with a photo). Each falls back to the current layout, and the column tokens apply only at the regular density, so computed styles are unchanged when they are unset. - **Zone Directory**: part hooks, attributes only: `data-zone-board` (the board; each cell is its direct child), `data-zone-num`, `data-zone-title`, `data-zone-description` and `data-zone-link`. Neither Zone Directory nor the Phase Ledger index prints a § itself; a theme sets one with `::before`. The new phase fields show in the admin only for their variant. Existing content, the `default` and `rail` variants, and a dated ledger with the options unset (or at their defaults) render exactly as before.

  • 6928b7f: Phase Ledger gains `index` and `panel` variants, and Dated Ledger gains year dates, compliance labels, a note and a compact density. - **Phase Ledger `index`**: a numbered `<ol>` of ruled rows (number, title, body, and a byline column with the outcome and an optional link line). Each phase's new `link` (`label`, `href`) covers the whole row. Hooks: `data-phase-index`, `data-phase-row`, `data-phase-link`; the number keeps `data-phase-num` so a theme can set a mark before it. - **Phase Ledger `panel`**: one large media panel above a step bar. Phases gain optional `image` and `meta`. Without JavaScript every step is listed in full; once hydrated the step bar is an ARIA tablist (click, arrow keys, Home and End), and at 768px wide and 600px tall the steps also follow the scroll along a pinned runway. Phones and short windows are never pinned. Under reduced motion the jump to a step is instant and nothing crossfades. The island root carries `data-tome-motion="self"`. Hooks: `data-phase-panel`, `data-phase-slide`, `data-phase-media`, `data-phase-meta`, `data-phase-tabs`, `data-phase-tab` (its label `data-phase-num`, its name `data-phase-title`), `data-phase-active`, plus the root's `data-panel-state`, `data-panel-ready` and `data-panel-follow`. - **Dated Ledger**: `datePrecision: 'year'` makes the year the large numeral (`data-date-precision`, `data-ledger-year`); `density: 'compact'` tightens the rows for short entries such as awards (`data-ledger-density`); each entry's `complianceLabel` closes the entry (`data-compliance-label`); a block `note` follows the entries (`data-ledger-note`, reveal group `note`). - **Dated Ledger label lane:** from 1024px a theme can redraw the `rows` grid of a labelled ledger to move the compliance label (for example into a right-hand lane) with `--tome-dossier-ledger-label-areas` / `--tome-dossier-ledger-label-cols` (entries without a photo) and `--tome-dossier-ledger-label-media-areas` / `--tome-dossier-ledger-label-media-cols` (with a photo). Each falls back to the current layout, and the column tokens apply only at the regular density, so computed styles are unchanged when they are unset. - **Zone Directory**: part hooks, attributes only: `data-zone-board` (the board; each cell is its direct child), `data-zone-num`, `data-zone-title`, `data-zone-description` and `data-zone-link`. Neither Zone Directory nor the Phase Ledger index prints a § itself; a theme sets one with `::before`. The new phase fields show in the admin only for their variant. Existing content, the `default` and `rail` variants, and a dated ledger with the options unset (or at their defaults) render exactly as before.
v0.12.1patch

d10e467: Muted labels and captions now meet WCAG AA contrast on light surfaces. - Labels, captions, column heads, struck and absent values, and placeholder labels that mixed their ink at 40–60% now mix it at 70% (63 rules). Small muted ink below about 61% fails 4.5:1 on cream and tinted light surfaces; a live CompareLedger row label measured 3.92:1 at 55%. - This includes the house `mono-label-muted` and `placeholder-label` mixins, and the defaults of `--tome-dossier-thumb-placeholder-ink`, `--tome-campaign-placeholder-ink` and `--tome-campaign-tier-pledge-ink`. Set those properties to keep the old ink. - Decorative ink is unchanged: outline numerals, scrims, corner brackets and the EvidencePlate sources separator. - Muted text reads slightly darker on every surface.

  • d10e467: Muted labels and captions now meet WCAG AA contrast on light surfaces. - Labels, captions, column heads, struck and absent values, and placeholder labels that mixed their ink at 40–60% now mix it at 70% (63 rules). Small muted ink below about 61% fails 4.5:1 on cream and tinted light surfaces; a live CompareLedger row label measured 3.92:1 at 55%. - This includes the house `mono-label-muted` and `placeholder-label` mixins, and the defaults of `--tome-dossier-thumb-placeholder-ink`, `--tome-campaign-placeholder-ink` and `--tome-campaign-tier-pledge-ink`. Set those properties to keep the old ink. - Decorative ink is unchanged: outline numerals, scrims, corner brackets and the EvidencePlate sources separator. - Muted text reads slightly darker on every surface.
v0.12.0minor

8960ee1: Phase Ledger outcome hooks, an Info Panel card kicker, and two-column margin facts on phones. - **Phase Ledger** (dossier): each phase's outcome line carries `data-phase-outcome`, and its italic and weight read `--tome-dossier-phase-outcome-style` (default `italic`) and `--tome-dossier-phase-outcome-weight` (default `330`), so a theme can restyle the outcome without overriding the declarations. The look is unchanged when neither token is set. - **Info Panel** (editorial): the sticky panel card gains an optional `panelKicker`, a small label rendered first in the card, above the person (hook `data-panel-kicker`). It is read only with `stickyPanel` on, and a kicker alone is enough to render the card. The person's `name` may now be a full lead sentence ("Marisol Vance leads roof replacements."), with `role` as the sentences that follow. Both wrap beside the initials with no clamp or truncation, and the admin label and descriptions say so. Set `initials` when the name is a sentence. - **Editorial Spread** (editorial): margin facts (`rail.statCallouts` that carry a note) sit in two columns on phones (under 640px) instead of one long column. An odd last fact sits alone in the first column. Tablets keep the auto-fill columns, and the desktop rail is unchanged. Existing content renders as before.

  • 8960ee1: Phase Ledger outcome hooks, an Info Panel card kicker, and two-column margin facts on phones. - **Phase Ledger** (dossier): each phase's outcome line carries `data-phase-outcome`, and its italic and weight read `--tome-dossier-phase-outcome-style` (default `italic`) and `--tome-dossier-phase-outcome-weight` (default `330`), so a theme can restyle the outcome without overriding the declarations. The look is unchanged when neither token is set. - **Info Panel** (editorial): the sticky panel card gains an optional `panelKicker`, a small label rendered first in the card, above the person (hook `data-panel-kicker`). It is read only with `stickyPanel` on, and a kicker alone is enough to render the card. The person's `name` may now be a full lead sentence ("Marisol Vance leads roof replacements."), with `role` as the sentences that follow. Both wrap beside the initials with no clamp or truncation, and the admin label and descriptions say so. Set `initials` when the name is a sentence. - **Editorial Spread** (editorial): margin facts (`rail.statCallouts` that carry a note) sit in two columns on phones (under 640px) instead of one long column. An odd last fact sits alone in the first column. Tablets keep the auto-fill columns, and the desktop rail is unchanged. Existing content renders as before.
  • fc84e67: Self-animating motion components now mark their own root with `data-tome-motion="self"`, so the opt-in platform scroll reveal in `@wabbit/tome-ui` never animates their blocks a second time. - `@wabbit/tome-blocks-core`: the `Reveal` boundary carries the marker, which covers every block that wraps its root in `Reveal`. - `@wabbit/tome-blocks-dossier-pack`: the entrance and motion islands (`StageMotion`, `DossierReveal`, the phase-ledger rail, case-file rows, evidence plate, receipts trio, record roster and proof plates) carry it. `RevealScope` renders no element of its own; when the pack reveal is on, it renders `DossierReveal`, which carries the marker. - `@wabbit/tome-blocks-campaign-pack`: `CampaignReveal` carries it. As in the dossier pack, `RevealScope` adds no element. - `@wabbit/tome-blocks-cinema-pack`: the pull interlude, scene caption and statement band motion and the scrub story stage carry it. - `@wabbit/tome-blocks-extras`: the animated `Showcase` root carries it. The static fallback does not, because it does not animate. The attribute changes nothing else. Markup, styles and motion are otherwise unchanged.
v0.11.0minor

43d7aa0: Lab Notes, Fit List and Cost Ledger: new options, line breaks, contrast and an entrance. - **Lab Notes** gains `showNumerals` (default on). Off drops the E.01… numerals and their grid column at every width: the kicker sits above the body on narrow screens, and from 900px the body lines up with the Handoff Body. The handoff body now sets at body size (`--tome-text-md`) on the prose measure (`--tome-prose-max-width`, 62ch when unset) instead of the small size at 62ch. Entry and handoff text wrap `pretty`; the headline wraps `balance`. - **Fit List**: a new line in the Anchored close renders as a line break (`*em*` accents still parse). The muted skip column and its label are raised from 55% to 70% ink for AA contrast. List items wrap `pretty`; the heading and close wrap `balance`. - **Cost Ledger** gains `compoundsStyle` (`label` default, or `prose`). Prose sets the third column in the body face at `--tome-text-md`, sentence case, in the body colour; the lit row keeps its inverted colours. A new line in the coda renders as a line break. The muted column labels and third column are raised from 55% to 70% ink (the lit row's third column from 60% to 70%). The heading wraps `balance`; the intro and coda wrap `pretty`. - **Entrance motion**: the three blocks now enter part by part, with no markup hidden up front. Cost Ledger rows post one by one from the left (170ms apart) with the lit row landing last; Lab Notes prints each entry in order (70ms apart) and the handoff after; Fit List's build column arrives from the left and its skip column from the right (90ms apart). Content is visible with JavaScript off, parts already on screen at load are left alone, and reduced motion is read when the page loads, when it changes, and when each group enters. The Cost Ledger rows and the Fit List columns no longer take part in the opt-in group reveal (`context.dossierReveal`); the kicker, heading, intro, column strip and close still do. Existing content renders as before apart from the contrast lift and the Lab Notes handoff size.

  • 43d7aa0: Lab Notes, Fit List and Cost Ledger: new options, line breaks, contrast and an entrance. - **Lab Notes** gains `showNumerals` (default on). Off drops the E.01… numerals and their grid column at every width: the kicker sits above the body on narrow screens, and from 900px the body lines up with the Handoff Body. The handoff body now sets at body size (`--tome-text-md`) on the prose measure (`--tome-prose-max-width`, 62ch when unset) instead of the small size at 62ch. Entry and handoff text wrap `pretty`; the headline wraps `balance`. - **Fit List**: a new line in the Anchored close renders as a line break (`*em*` accents still parse). The muted skip column and its label are raised from 55% to 70% ink for AA contrast. List items wrap `pretty`; the heading and close wrap `balance`. - **Cost Ledger** gains `compoundsStyle` (`label` default, or `prose`). Prose sets the third column in the body face at `--tome-text-md`, sentence case, in the body colour; the lit row keeps its inverted colours. A new line in the coda renders as a line break. The muted column labels and third column are raised from 55% to 70% ink (the lit row's third column from 60% to 70%). The heading wraps `balance`; the intro and coda wrap `pretty`. - **Entrance motion**: the three blocks now enter part by part, with no markup hidden up front. Cost Ledger rows post one by one from the left (170ms apart) with the lit row landing last; Lab Notes prints each entry in order (70ms apart) and the handoff after; Fit List's build column arrives from the left and its skip column from the right (90ms apart). Content is visible with JavaScript off, parts already on screen at load are left alone, and reduced motion is read when the page loads, when it changes, and when each group enters. The Cost Ledger rows and the Fit List columns no longer take part in the opt-in group reveal (`context.dossierReveal`); the kicker, heading, intro, column strip and close still do. Existing content renders as before apart from the contrast lift and the Lab Notes handoff size.
  • 4fe4ca3: Dossier blocks now stop their scroll animations when reduced motion is switched on or the block unmounts. Previously the tweens and scroll triggers already built kept running (the evidence plate's ink fill kept rewriting its background on scroll) and leaked on unmount. The cleanup works with the existing `@wabbit/tome-blocks-house` peer range.
v0.10.1patch

dd76bb4: The four remaining motion islands (`case-file-row`, `evidence-plate`, `receipts-trio`, `record-roster`) now follow `prefers-reduced-motion` live, as `proof-plates` and the opt-in reveal already do. Each used a one-shot read at mount, so a page that loaded under reduced motion and was then switched to normal motion kept its pending parts hidden by the stylesheet with no effect left to reveal them. They now use the pack's reactive `useReducedMotion` as an effect dependency: reduced to normal plays the reveal, and normal to reduced shows every part in its finished state (the evidence plate's ink fill also drops its scrubbed position). Behaviour is unchanged when the preference never changes.

  • dd76bb4: The four remaining motion islands (`case-file-row`, `evidence-plate`, `receipts-trio`, `record-roster`) now follow `prefers-reduced-motion` live, as `proof-plates` and the opt-in reveal already do. Each used a one-shot read at mount, so a page that loaded under reduced motion and was then switched to normal motion kept its pending parts hidden by the stylesheet with no effect left to reveal them. They now use the pack's reactive `useReducedMotion` as an effect dependency: reduced to normal plays the reveal, and normal to reduced shows every part in its finished state (the evidence plate's ink fill also drops its scrubbed position). Behaviour is unchanged when the preference never changes.
v0.10.0minor

3e42d40: Proof Plates and Term Ledger gain opt-in buttons and a text-edge option; defaults render as before. Proof Plates: a plate's `linkStyle: 'button'` (default `inline`) sets its link as a button at the plate's foot, lined up across plates of different heights, with `linkAppearance` `outline` (default) or `default` (accent fill). Term Ledger: a row's `cta` and the block's `introCta` (`{ label, url, appearance? }`) render as buttons, and `introEdge: 'rows'` lines the intro and close up on the row-body text edge from 721px. The buttons share one stylesheet (44px targets, stacked full width at 640px and below, a trailing arrow bound to the last word). Fixed: a page that loaded under reduced motion and was then switched to normal motion left its pending reveal parts invisible; the reveal islands now follow the preference live and play the reveal on that switch. Term Ledger row bodies are also capped by the host `--tome-prose-max-width` when one is set (68ch/74ch remain the fallbacks).

  • 3e42d40: Proof Plates and Term Ledger gain opt-in buttons and a text-edge option; defaults render as before. Proof Plates: a plate's `linkStyle: 'button'` (default `inline`) sets its link as a button at the plate's foot, lined up across plates of different heights, with `linkAppearance` `outline` (default) or `default` (accent fill). Term Ledger: a row's `cta` and the block's `introCta` (`{ label, url, appearance? }`) render as buttons, and `introEdge: 'rows'` lines the intro and close up on the row-body text edge from 721px. The buttons share one stylesheet (44px targets, stacked full width at 640px and below, a trailing arrow bound to the last word). Fixed: a page that loaded under reduced motion and was then switched to normal motion left its pending reveal parts invisible; the reveal islands now follow the preference live and play the reveal on that switch. Term Ledger row bodies are also capped by the host `--tome-prose-max-width` when one is set (68ch/74ch remain the fallbacks).
v0.9.0minor

245fa47: CompareLedger gains three optional settings. Existing blocks render exactly as before. - `tone` (`contrast` by default, or `equal`): `equal` sets every option at full ink with no strike-through, and left-aligns the display values and the row cells, for two honest options side by side (for example "what the first year costs"). The first column's label takes the same accent colour as the other. - `rows[].label`: a small mono label that spans the row above its cells. A row with only a label still renders. - `closeLink` (`{ label, url }`): a link added at the end of the close statement, underlined in the surrounding text colour. It renders only when both the label and the URL are set. Prose cells keep their existing line-length cap in both tones.

  • 245fa47: CompareLedger gains three optional settings. Existing blocks render exactly as before. - `tone` (`contrast` by default, or `equal`): `equal` sets every option at full ink with no strike-through, and left-aligns the display values and the row cells, for two honest options side by side (for example "what the first year costs"). The first column's label takes the same accent colour as the other. - `rows[].label`: a small mono label that spans the row above its cells. A row with only a label still renders. - `closeLink` (`{ label, url }`): a link added at the end of the close statement, underlined in the surrounding text colour. It renders only when both the label and the URL are set. Prose cells keep their existing line-length cap in both tones.
  • 245fa47: CompareLedger's heading no longer pushes the page into horizontal scroll on a 320px screen. A single long word at hero size could be wider than a narrow column. The heading now sets `overflow-wrap: break-word`, so such a word breaks onto the next line only when it would otherwise overflow. Layouts where every word fits are unchanged.
v0.8.0minor

432d69b: Running copy reads the platform body token. Thirteen dossier blocks set paragraph-level copy at the small step (`--tome-text-sm` / `--tome-type-size-sm`), an `xs` note size, a heading step (`--tome-text-h5`, `--tome-type-size-h5`) or literals (`1.02rem`, `0.92rem`). Their running copy now reads `--tome-text-body`, the platform's body size, so a consumer's body token sets it everywhere: - comparisonTable cells and footnote, recordRoster row descriptions, specSheet note, zoneDirectory cell descriptions, caseFileGrid tile briefs (both treatments), specPlate cell notes, fitList items: small step → body - evidenceSheet ledger row notes: xs → body - practiceModes mode copy (1.02rem), caseFileRow cell lines (0.92rem): literal → body - proofPlates plate body: `--tome-type-size-body` (undefined on the platform) → body - termLedger, proofPlates and wallRows closing paragraphs (and proofPlates' close card): heading step → body Visible change for consumers on the default scale: card and table copy moves from 14px to 16px, and closing paragraphs from 18–20px to 16px. Labels, values, captions and display variants are unchanged.

  • 432d69b: Running copy reads the platform body token. Thirteen dossier blocks set paragraph-level copy at the small step (`--tome-text-sm` / `--tome-type-size-sm`), an `xs` note size, a heading step (`--tome-text-h5`, `--tome-type-size-h5`) or literals (`1.02rem`, `0.92rem`). Their running copy now reads `--tome-text-body`, the platform's body size, so a consumer's body token sets it everywhere: - comparisonTable cells and footnote, recordRoster row descriptions, specSheet note, zoneDirectory cell descriptions, caseFileGrid tile briefs (both treatments), specPlate cell notes, fitList items: small step → body - evidenceSheet ledger row notes: xs → body - practiceModes mode copy (1.02rem), caseFileRow cell lines (0.92rem): literal → body - proofPlates plate body: `--tome-type-size-body` (undefined on the platform) → body - termLedger, proofPlates and wallRows closing paragraphs (and proofPlates' close card): heading step → body Visible change for consumers on the default scale: card and table copy moves from 14px to 16px, and closing paragraphs from 18–20px to 16px. Labels, values, captions and display variants are unchanged.
v0.7.1patch

07e4773: Dossier lane kickers now carry a `data-lane-kicker` hook, and each `phase-ledger` step number carries `data-phase-num`. The hook is on the kicker text of every block that sets one (`dated-ledger`, `phase-ledger`, the cost, grants, term and other ledgers, `fit-list`, `fit-prose`, `practice-modes`, `proof-plates`, `receipts-trio`, `record-roster`, `wall-rows`, `zone-directory`, `case-file-row`), so a theme restyles them with one rule. Attributes only; nothing renders differently.

  • 07e4773: Dossier lane kickers now carry a `data-lane-kicker` hook, and each `phase-ledger` step number carries `data-phase-num`. The hook is on the kicker text of every block that sets one (`dated-ledger`, `phase-ledger`, the cost, grants, term and other ledgers, `fit-list`, `fit-prose`, `practice-modes`, `proof-plates`, `receipts-trio`, `record-roster`, `wall-rows`, `zone-directory`, `case-file-row`), so a theme restyles them with one rule. Attributes only; nothing renders differently.
v0.7.0minor

115bcfb: Add the `dated-ledger` block and a `rail` variant for `phase-ledger`. `dated-ledger` is a dated record of finished work: a kicker, heading and intro, then 1 to 12 entries, each led by a large day numeral with a month, year and reference line in tabular figures (`<time dateTime>`), then an optional photo, chip, title, result line and figure, with an optional per-entry link and a closing "more" link. Two variants: `columns` (default; up to four per row, one on phones) and `rows` (ruled rows, the date in the left lane and the figure in the right lane). Theme hooks are stable `data-ledger-*` attributes and `data-block-variant` on the root; it joins the opt-in scroll reveal (`shead`, `entries`, `more`). `phase-ledger` now declares `default` and `rail` variants. Content saved without a variant renders as `default`, unchanged apart from `data-variant` / `data-block-variant` on the root. `rail` sets the phases beside a progress rail that fills as the section scrolls and lights each phase's dot when reached: from 1024px as up to four columns under a horizontal rail filling left to right, below it as stacked rows beside a vertical rail on the left edge, driven by a small client island (`PhaseLedgerRailMotion`, no GSAP). Without JavaScript and under reduced motion the rail renders fully drawn with every dot on. Hooks: `data-phase-rail` (with `data-cols` and `data-rail-axis="x" | "y"`), `data-phase-rail-track`, `data-phase-rail-fill`, `data-phase-step` with `data-active`, `data-phase-rail-dot`, and the `--tome-dossier-rail-progress` property.

  • 115bcfb: Add the `dated-ledger` block and a `rail` variant for `phase-ledger`. `dated-ledger` is a dated record of finished work: a kicker, heading and intro, then 1 to 12 entries, each led by a large day numeral with a month, year and reference line in tabular figures (`<time dateTime>`), then an optional photo, chip, title, result line and figure, with an optional per-entry link and a closing "more" link. Two variants: `columns` (default; up to four per row, one on phones) and `rows` (ruled rows, the date in the left lane and the figure in the right lane). Theme hooks are stable `data-ledger-*` attributes and `data-block-variant` on the root; it joins the opt-in scroll reveal (`shead`, `entries`, `more`). `phase-ledger` now declares `default` and `rail` variants. Content saved without a variant renders as `default`, unchanged apart from `data-variant` / `data-block-variant` on the root. `rail` sets the phases beside a progress rail that fills as the section scrolls and lights each phase's dot when reached: from 1024px as up to four columns under a horizontal rail filling left to right, below it as stacked rows beside a vertical rail on the left edge, driven by a small client island (`PhaseLedgerRailMotion`, no GSAP). Without JavaScript and under reduced motion the rail renders fully drawn with every dot on. Hooks: `data-phase-rail` (with `data-cols` and `data-rail-axis="x" | "y"`), `data-phase-rail-track`, `data-phase-rail-fill`, `data-phase-step` with `data-active`, `data-phase-rail-dot`, and the `--tome-dossier-rail-progress` property.
v0.6.4patch

f876e5e: Compare Ledger's heading starts on the content edge. Visible change: from 1024px wide, the heading used to start at the reading column, leaving the left lane empty beside it (the block has no kicker to fill it). It now starts on the content edge, aligned with the column labels and the rule below it. Below 1024px nothing moves.

  • f876e5e: Compare Ledger's heading starts on the content edge. Visible change: from 1024px wide, the heading used to start at the reading column, leaving the left lane empty beside it (the block has no kicker to fill it). It now starts on the content edge, aligned with the column labels and the rule below it. Below 1024px nothing moves.
v0.6.3patch

b452c7b: Shift Rows gains a token for an empty thumbnail's outline, and its CTA becomes an inline-block. No change until a site sets the token: - `--tome-dossier-thumb-placeholder-border` (default `1px dashed color-mix(in oklch, currentColor 30%, transparent)`): the outline of an empty Shift Rows thumbnail slot, a `border` shorthand (`none` drops it). Visible change on a default render: - **Shift Rows' CTA** is an `inline-block` instead of an `inline-flex`. The label is plain text, so it renders and wraps the same; a label that wraps to two lines no longer leaves a line-box strut (about 1.5px) under the link's box, so the block ends that much sooner below a two-line CTA. A one-line CTA does not move. Ledger's CTA is unchanged.

  • b452c7b: Shift Rows gains a token for an empty thumbnail's outline, and its CTA becomes an inline-block. No change until a site sets the token: - `--tome-dossier-thumb-placeholder-border` (default `1px dashed color-mix(in oklch, currentColor 30%, transparent)`): the outline of an empty Shift Rows thumbnail slot, a `border` shorthand (`none` drops it). Visible change on a default render: - **Shift Rows' CTA** is an `inline-block` instead of an `inline-flex`. The label is plain text, so it renders and wraps the same; a label that wraps to two lines no longer leaves a line-box strut (about 1.5px) under the link's box, so the block ends that much sooner below a two-line CTA. A one-line CTA does not move. Ledger's CTA is unchanged.
v0.6.2patch

eee4d87: Ledger and Shift Rows CTAs read the house editorial CTA register, and Shift Rows gains tokens for its label column, section marker and empty thumbnails. **CTA register.** Both CTAs read `--tome-house-cta-editorial-family`, `-size`, `-weight`, `-tracking`, `-transform`, `-margin` and `-color` from `@wabbit/tome-blocks-house`, each falling back to the link's current value: the inherited sans, `--tome-text-sm`, 600, `--tome-house-tracking-cta-editorial`, uppercase, a `--tome-space-2xl` top margin and the accent ink (Shift Rows: `--tome-color-accent-text`; Ledger: the band's `--blk-accent-ink` first). On a grid root (a site that subgrids the block onto its page grid) each CTA takes `--tome-house-cta-cols` and starts its row instead of stretching across it; on Shift Rows' stock block root this is inert. No change until a site sets the token: - `--tome-dossier-row-label-pad-start-wide` (default `var(--tome-space-md)`): Shift Rows' label column top padding from 64rem. The design source's value is `var(--tome-space-lg)`, one step more than the bottom, which sets the label a little lower beside the panels. - `--tome-dossier-marker-tracking` (default `var(--tome-house-tracking-mono)`): Shift Rows' section marker tracking, so a site that retracks the mono labels can hold the marker. - `--tome-dossier-thumb-placeholder-pad` (default `var(--tome-space-sm)`) and `--tome-dossier-thumb-placeholder-ink` (default `color-mix(in oklch, currentColor 55%, transparent)`): an empty Shift Rows thumbnail slot's padding and label ink. Visible change on a default render: - **Ledger's CTA** no longer stretches across the block: the link's box (its clickable and focus area) now ends at its label, as Shift Rows' already did. The label itself does not move.

  • eee4d87: Ledger and Shift Rows CTAs read the house editorial CTA register, and Shift Rows gains tokens for its label column, section marker and empty thumbnails. **CTA register.** Both CTAs read `--tome-house-cta-editorial-family`, `-size`, `-weight`, `-tracking`, `-transform`, `-margin` and `-color` from `@wabbit/tome-blocks-house`, each falling back to the link's current value: the inherited sans, `--tome-text-sm`, 600, `--tome-house-tracking-cta-editorial`, uppercase, a `--tome-space-2xl` top margin and the accent ink (Shift Rows: `--tome-color-accent-text`; Ledger: the band's `--blk-accent-ink` first). On a grid root (a site that subgrids the block onto its page grid) each CTA takes `--tome-house-cta-cols` and starts its row instead of stretching across it; on Shift Rows' stock block root this is inert. No change until a site sets the token: - `--tome-dossier-row-label-pad-start-wide` (default `var(--tome-space-md)`): Shift Rows' label column top padding from 64rem. The design source's value is `var(--tome-space-lg)`, one step more than the bottom, which sets the label a little lower beside the panels. - `--tome-dossier-marker-tracking` (default `var(--tome-house-tracking-mono)`): Shift Rows' section marker tracking, so a site that retracks the mono labels can hold the marker. - `--tome-dossier-thumb-placeholder-pad` (default `var(--tome-space-sm)`) and `--tome-dossier-thumb-placeholder-ink` (default `color-mix(in oklch, currentColor 55%, transparent)`): an empty Shift Rows thumbnail slot's padding and label ink. Visible change on a default render: - **Ledger's CTA** no longer stretches across the block: the link's box (its clickable and focus area) now ends at its label, as Shift Rows' already did. The label itself does not move.
v0.6.1patch

7c94229: Term Ledger gains an opt-in figure layout for the head face and a leading token for the spec term. No change to any existing render until an editor sets the option or a site sets the token: - **Term Ledger `figureLayout`** (new select field, default `split`). With a supporting figure, `display` gives the head face the figure layout the display face already uses: the intro in the reading column, the ledger stopping at the end of the prose lane with an 11 to 15rem term column, heavier terms, row bodies held to 58ch and the tilted figure plate (with `--tome-dossier-figure-shadow`) filling the rest of the row. The heading, band and close stay the head face's. Without a figure, and on the display face, the option does nothing. Left at `split`, the head face keeps its own rows beside a 200 to 320px figure. - **Term Ledger spec term leading.** The `spec` and `spec-measure` terms read `--tome-dossier-spec-term-leading`, falling back to 1.1, the leading they already had. Schema: `figureLayout` is a new column on the Term Ledger block. On a SQL adapter (Postgres, SQLite), generate and run a migration after upgrading; existing rows take `split`. Documentation: the README notes that Phase Ledger's row titles take the page's heading tracking, so a site retracking the block heading alone should set `--tome-house-heading-tracking` rather than re-point `--tome-type-tracking-tight`.

  • 7c94229: Term Ledger gains an opt-in figure layout for the head face and a leading token for the spec term. No change to any existing render until an editor sets the option or a site sets the token: - **Term Ledger `figureLayout`** (new select field, default `split`). With a supporting figure, `display` gives the head face the figure layout the display face already uses: the intro in the reading column, the ledger stopping at the end of the prose lane with an 11 to 15rem term column, heavier terms, row bodies held to 58ch and the tilted figure plate (with `--tome-dossier-figure-shadow`) filling the rest of the row. The heading, band and close stay the head face's. Without a figure, and on the display face, the option does nothing. Left at `split`, the head face keeps its own rows beside a 200 to 320px figure. - **Term Ledger spec term leading.** The `spec` and `spec-measure` terms read `--tome-dossier-spec-term-leading`, falling back to 1.1, the leading they already had. Schema: `figureLayout` is a new column on the Term Ledger block. On a SQL adapter (Postgres, SQLite), generate and run a migration after upgrading; existing rows take `split`. Documentation: the README notes that Phase Ledger's row titles take the page's heading tracking, so a site retracking the block heading alone should set `--tome-house-heading-tracking` rather than re-point `--tome-type-tracking-tight`.
v0.6.0minor

59d51da: **BREAKING:** Shift Rows and Ledger switch their row grid on their own width, Shift Rows' after panel reads the band's emphasis pair, and the `@wabbit/tome-blocks-house` peer floor rises to 0.8.4. **Migration:** upgrade `@wabbit/tome-blocks-house` to 0.8.4 or later (the peer range is now `>=0.8.4 <1.0.0`; the pack's fill-mode media options now come from that package's `fillMediaOptions`). No content or config changes. Visible changes on a default render: - **Shift Rows and Ledger, layout.** Shift Rows puts label, before, arrow and after side by side once its row list is 51rem wide, and Ledger shows its four columns once its table is 58rem wide. Both used to switch on the window width (from 816px and 928px). Placed in a column narrower than the window, they now stack until the block itself has room. The thresholds are container queries, so `rem` is the page's root font size: on a page whose root is not 16px they move with it. - **Shift Rows, arrow.** The stacked arrow's vertical padding applies below a 1024px window (it was below 816px). - **Shift Rows, thumbnails.** A thumbnail image no longer has a dashed border; it has a solid one only when its Image Display border is on. An empty thumbnail slot keeps its dashed outline. Thumbnails pass the media adapter `imgClassName`, `fill: true` and `sizes: '96px'`, so an adapter that wraps its image fills the 96px box with the Image Display fit and focus; the built-in adapter renders the same `<img>` with a second class and a `sizes` attribute. - **Shift Rows, editor Text Size.** The chrome text size now sizes the intro and the before and after bodies, and no longer the heading (which used to take the body size when an editor picked one). With no text size picked nothing changes. - **Shift Rows on a stored band.** The after panel's text takes the band's ink instead of the theme foreground, so it stays readable on a dark band. The row labels, arrows, the after panel's border and its AFTER tag read the band's accent ink first: on solid-dark and inverse bands (and site bands that carry the companion) they take the band's accent instead of the theme's. On the other built-in bands they render as before. No other change until a site sets a token or registers a band value: - Shift Rows' after panel reads `--blk-emph-bg` (fill) and `--blk-emph-fg` (text and AFTER tag), the emphasis pair from `@wabbit/tome-blocks-house`. No built-in band sets them, so the panel stays a faint accent tint; a site band registered with `emph` and `onEmph` turns it into a solid card. - Shift Rows' before and after headlines read `--tome-dossier-before-headline-size` and `--tome-dossier-after-headline-size` (`--tome-type-size-xxl` when unset). - Ledger's cost cells read `--tome-dossier-cost-size` (`--tome-type-size-xl`) and `--tome-dossier-debit-ink` (`--tome-color-error-text`); its closing note reads `--tome-dossier-closing-size` (`--tome-type-size-xxl`); its closing panel reads `--tome-dossier-closing-bg` and `--tome-dossier-closing-fg` (tome-ui's solid-dark surface and its ink). Declared on the block's root, the panel tokens can mix from the band's `--blk-bg` and `--blk-fg`.

  • 59d51da: **BREAKING:** Shift Rows and Ledger switch their row grid on their own width, Shift Rows' after panel reads the band's emphasis pair, and the `@wabbit/tome-blocks-house` peer floor rises to 0.8.4. **Migration:** upgrade `@wabbit/tome-blocks-house` to 0.8.4 or later (the peer range is now `>=0.8.4 <1.0.0`; the pack's fill-mode media options now come from that package's `fillMediaOptions`). No content or config changes. Visible changes on a default render: - **Shift Rows and Ledger, layout.** Shift Rows puts label, before, arrow and after side by side once its row list is 51rem wide, and Ledger shows its four columns once its table is 58rem wide. Both used to switch on the window width (from 816px and 928px). Placed in a column narrower than the window, they now stack until the block itself has room. The thresholds are container queries, so `rem` is the page's root font size: on a page whose root is not 16px they move with it. - **Shift Rows, arrow.** The stacked arrow's vertical padding applies below a 1024px window (it was below 816px). - **Shift Rows, thumbnails.** A thumbnail image no longer has a dashed border; it has a solid one only when its Image Display border is on. An empty thumbnail slot keeps its dashed outline. Thumbnails pass the media adapter `imgClassName`, `fill: true` and `sizes: '96px'`, so an adapter that wraps its image fills the 96px box with the Image Display fit and focus; the built-in adapter renders the same `<img>` with a second class and a `sizes` attribute. - **Shift Rows, editor Text Size.** The chrome text size now sizes the intro and the before and after bodies, and no longer the heading (which used to take the body size when an editor picked one). With no text size picked nothing changes. - **Shift Rows on a stored band.** The after panel's text takes the band's ink instead of the theme foreground, so it stays readable on a dark band. The row labels, arrows, the after panel's border and its AFTER tag read the band's accent ink first: on solid-dark and inverse bands (and site bands that carry the companion) they take the band's accent instead of the theme's. On the other built-in bands they render as before. No other change until a site sets a token or registers a band value: - Shift Rows' after panel reads `--blk-emph-bg` (fill) and `--blk-emph-fg` (text and AFTER tag), the emphasis pair from `@wabbit/tome-blocks-house`. No built-in band sets them, so the panel stays a faint accent tint; a site band registered with `emph` and `onEmph` turns it into a solid card. - Shift Rows' before and after headlines read `--tome-dossier-before-headline-size` and `--tome-dossier-after-headline-size` (`--tome-type-size-xxl` when unset). - Ledger's cost cells read `--tome-dossier-cost-size` (`--tome-type-size-xl`) and `--tome-dossier-debit-ink` (`--tome-color-error-text`); its closing note reads `--tome-dossier-closing-size` (`--tome-type-size-xxl`); its closing panel reads `--tome-dossier-closing-bg` and `--tome-dossier-closing-fg` (tome-ui's solid-dark surface and its ink). Declared on the block's root, the panel tokens can mix from the band's `--blk-bg` and `--blk-fg`.
  • ecd624a: Rich-text intros no longer centre under a host adapter's container class, Spec Plate's lede takes the site's prose measure, and Cost Ledger gets two tokens. Visible change only where a site's rich-text adapter adds a centring container class (auto inline margins, `max-width: 100%`) to the element it renders: the intros of Wall Rows, Term Ledger (both faces) and Phase Ledger used to be centred in their slot, or uncapped, depending on stylesheet order. They now start on the slot's start line and hold their measure, through a two-class rule that sets `margin-inline: 0` and the same cap. With the built-in adapter nothing changes. No other change until a site sets a token: - Spec Plate's lede holds `min(62ch, var(--tome-prose-max-width, 62ch))`, so a prose measure narrower than 62ch now caps it. Unset, it is 62ch as before, on both heading faces. - Cost Ledger's intro reads `--tome-dossier-ledger-intro-size`, falling back to `--tome-text-lg`, so the intro can be sized apart from the debit cells, which keep `--tome-text-lg`. - Cost Ledger's line label in the lit row reads `--tome-dossier-ledger-lit-label-ink`, falling back to 80% of the plate's ink. It is a token rather than the band's accent ink because the lit plate always carries the solid-dark band, which always sets one; reading it would have recoloured every lit label.
v0.5.5patch

b3c65ba: Fixed: later rows of a block could appear before the block scrolled into view, then jump back and animate; they now stay hidden until their group enters. This affected the five blocks with their own entrance motion: Case File Row, Evidence Plate, Proof Plates, Receipts Trio and Record Roster. A timer started when the page loaded removed the hiding class from every part of a group after 900ms, while only the group's first part was held hidden by the animation itself. On a group still below the fold, the cells, stats, quotes, roster rows or plates after the first one showed early. Each part's hidden state is now set on the part itself before its group's animation is built, and cleared when that part finishes animating, so a settled part renders from its stylesheet alone. Reduced-motion and no-JavaScript rendering are unchanged, and the tilted flagged card in Proof Plates still settles on its tilt. The same five blocks now read `context.dossierReveal` for their timing, as the opt-in reveal does: `duration`, `stagger`, `ease`, `start` and `rise` each replace the block's own value when set. Without the key each block keeps its own timing: Case File Row, Evidence Plate and Receipts Trio take duration and stagger from the motion tokens, Record Roster and Proof Plates run 0.56s with an 80ms stagger, and all five start at `top 85%` with a 50px rise. Deliberate motion change: in Case File Row, Receipts Trio and the card layout of Proof Plates, the kicker and the heading beside it now reveal together as one step instead of one stagger apart (80ms with the default tokens), so the head enters as one element. Everything after the head moves as before.

  • b3c65ba: Fixed: later rows of a block could appear before the block scrolled into view, then jump back and animate; they now stay hidden until their group enters. This affected the five blocks with their own entrance motion: Case File Row, Evidence Plate, Proof Plates, Receipts Trio and Record Roster. A timer started when the page loaded removed the hiding class from every part of a group after 900ms, while only the group's first part was held hidden by the animation itself. On a group still below the fold, the cells, stats, quotes, roster rows or plates after the first one showed early. Each part's hidden state is now set on the part itself before its group's animation is built, and cleared when that part finishes animating, so a settled part renders from its stylesheet alone. Reduced-motion and no-JavaScript rendering are unchanged, and the tilted flagged card in Proof Plates still settles on its tilt. The same five blocks now read `context.dossierReveal` for their timing, as the opt-in reveal does: `duration`, `stagger`, `ease`, `start` and `rise` each replace the block's own value when set. Without the key each block keeps its own timing: Case File Row, Evidence Plate and Receipts Trio take duration and stagger from the motion tokens, Record Roster and Proof Plates run 0.56s with an 80ms stagger, and all five start at `top 85%` with a 50px rise. Deliberate motion change: in Case File Row, Receipts Trio and the card layout of Proof Plates, the kicker and the heading beside it now reveal together as one step instead of one stagger apart (80ms with the default tokens), so the head enters as one element. Everything after the head moves as before.
  • 9d30f92: Comparison tables show stacked row cards below 1024px, Term Ledger's display face gets its editorial layouts, and Case File Grid, Spec Plate and Lab Notes sit on the reading lanes. Visible changes on a default render: - **Comparison Table, below 1024px.** Each row shows as a stacked card (the row label as the head, every column's answer under a small mono column tag, the highlighted column tinted or filled) instead of a sideways-scrolling table. Answers stack one line each below 640px and sit side by side from 640px. The table stays in the page, visually hidden, for assistive technology. From 1024px nothing changes. - **Term Ledger with `headingFace: 'display'`.** Without a figure the block moves to the tinted surface band, puts the kick label in the left lane beside the heading, sets the intro in the reading column, stops the ledger at the end of the prose lane with an 11 to 15rem term column, smaller terms and row bodies held to 58ch, and closes with a ruled serif line across the content width. With a figure the intro takes the reading column, the ledger stops at the end of the prose lane and the tilted figure plate (now with a shadow) fills the rest of the row, with heavier terms. A Term Ledger that leaves `headingFace` unset renders exactly as before, figure included. - **Case File Grid, Spec Plate and Lab Notes.** They now render a bleeding lane grid with every part in the content column. On a page grid the band now paints the whole row instead of the content column; the content does not move. Without a page grid the content gains side gutters of `--tome-grid-padding`, as every other lane block has. In Case File Grid a headline with no description sits 0.5rem further from the tiles, because its bottom margin no longer collapses into the title's. - **Case File Grid, field manual with tile images on.** The extra top padding and second top rule inside each image card are gone; the cards match the default treatment's. - **Images that fill a box.** Case File Grid tile images, Record Roster logos and Proof Plates captures pass the media adapter `imgClassName`, `fill: true` and a `sizes` value, so an adapter that wraps its image can fill the box with a cover crop (tiles), `contain` (logos) or a cover crop from the top (captures). The built-in adapter renders the same `<img>` as before, now with a `sizes` attribute. No other change until a site sets a token or passes a context key: - Comparison Table's frame reads `--tome-dossier-table-radius`, `--tome-dossier-table-fill` and `--tome-dossier-table-rule`, and its solid fill reads `--tome-dossier-emphasis-bg`, `--tome-dossier-emphasis-fg` and `--tome-dossier-emphasis-accent`, each falling back to today's values. - Case File Grid takes a tile image fallback from the render context (`dossierTileMedia`, exported as `DOSSIER_TILE_MEDIA_CONTEXT_KEY` with `readDossierTileMedia`): a function that returns an image for a tile with no upload of its own. Its link rules use two-class selectors, so a global link reset no longer repaints them, and its hover transitions read `--tome-dossier-hover-ease` and `--tome-dossier-hover-duration`. - Term Ledger's display figure shadow reads `--tome-dossier-figure-shadow`.
  • 8083efd: Comparison Table renders a headerless table when it has no headline and gains a `descriptionAboveTable` option; four blocks gain tokens and three labels take weight 600. Visible changes on a default render: - **Comparison Table with no headline.** It now renders the table with no header and no eyebrow, any description directly above it, instead of rendering nothing. The schema still requires a headline, so this shows only for data that reaches the renderer from elsewhere. - **Labels at weight 600.** Record Roster's placeholder marks (`MARK 01` and so on), Evidence Plate's sources line (the `Sources` label and its links) and the stats line on Proof Plates' situational cards now set weight 600. The situational stats line is also uppercase. The stats line in the strip shell is unchanged. - **Case File Row link underlines on a band with an accent ink.** The cell links' and closing link's underlines follow the band's accent ink (`--blk-accent-ink`), as the link text already did. On a band without one (every built-in theme-relative band) they keep the theme accent, as before. - **Term Ledger figure image.** The figure passes the media adapter `imgClassName`, `fill: true` and a `sizes` value, so an adapter that wraps its image can fill the 4:5 window with a cover crop. The built-in adapter renders the same `<img>` with a second class and a `sizes` attribute; it looks the same. No other change until a site sets a token or an option: - Comparison Table: a `descriptionAboveTable` checkbox (default off) moves the description out of the header to sit directly above the table. Adding the field to a Payload config adds its column, so run your usual migration. Four tokens tune the header and column heads: `--tome-dossier-table-head-gap` (the stacked header's bottom margin, `--tome-space-xl` when unset), `--tome-dossier-rail-width` (the rail eyebrow column, `10.625rem`), `--tome-dossier-rail-eyebrow-size` (the rail eyebrow, `--tome-text-xs`, now apart from the column heads) and `--tome-dossier-column-head-wrap` (the column heads' `white-space`, `nowrap`). - Links: a site's global link reset such as `:root a { color: inherit }` no longer repaints the link colours of Case File Row (cell links and closing link), Ledger and Shift Rows (the CTA, with its hover and focus states), Evidence Sheet and Exhibit Artifact (the CTA), Zone Directory's dark cells or Lab Notes' handoff links: their colour rules now use two-class selectors. The colours themselves are unchanged. - Lab Notes: the handoff link's gap transition reads `--tome-dossier-hover-duration` and `--tome-dossier-hover-ease` (`0.18s` and `ease` when unset); entry bodies are capped at `--tome-prose-max-width` when a site sets it and stay uncapped when it is unset. - Case File Row: the cell kicker's ink reads `--tome-dossier-cell-kicker-opacity`, a 0 to 1 number (0.62 when unset). - Evidence Plate: the eyebrow's ink reads `--tome-dossier-plate-eyebrow-opacity` (0.78 when unset), and the source links' underline transition reads `--tome-dossier-hover-duration` and `--tome-dossier-hover-ease` (`--tome-motion-fast` and `ease` when unset).
v0.5.4patch

37e8702: Fixed: dossier headings no longer override a site's own heading letter-spacing, font style or letter case unless the site opts into the heading voice. In 0.5.3 every block heading declared `font-style` and `text-transform` (and, on some headings, `font-family` or `letter-spacing`) from the heading-voice tokens with no fallback. With the tokens unset, those declarations made the heading take its parent's value, so a site rule such as `h2 { letter-spacing: -0.025em }` stopped applying: an Evidence Sheet opening headline lost its tight tracking and wrapped onto an extra line. The accent phrase had the same problem for its face, weight, case, tracking and line height. Those properties are now read only under an ancestor carrying the `data-tome-heading-voice` attribute, with `normal`, `none` or the inherited value as the fallback. Without the attribute a heading renders as it did before 0.5.3. The properties a heading always set (face, weight, tracking and line height on most) still read the tokens everywhere, falling back to the heading's own values. To keep a caps voice set through the tokens, add `data-tome-heading-voice` to the element that sets them.

  • 37e8702: Fixed: dossier headings no longer override a site's own heading letter-spacing, font style or letter case unless the site opts into the heading voice. In 0.5.3 every block heading declared `font-style` and `text-transform` (and, on some headings, `font-family` or `letter-spacing`) from the heading-voice tokens with no fallback. With the tokens unset, those declarations made the heading take its parent's value, so a site rule such as `h2 { letter-spacing: -0.025em }` stopped applying: an Evidence Sheet opening headline lost its tight tracking and wrapped onto an extra line. The accent phrase had the same problem for its face, weight, case, tracking and line height. Those properties are now read only under an ancestor carrying the `data-tome-heading-voice` attribute, with `normal`, `none` or the inherited value as the fallback. Without the attribute a heading renders as it did before 0.5.3. The properties a heading always set (face, weight, tracking and line height on most) still read the tokens everywhere, falling back to the heading's own values. To keep a caps voice set through the tokens, add `data-tome-heading-voice` to the element that sets them.
v0.5.3patch

e58ca95: Dossier headings read the heading-voice tokens and a new size token, stored bands paint on every block, and labels share one mono recipe. Visible changes on a default render: - **Labels.** These now use the mono face and weight 600: Fit Prose's kicker and column labels, Zone Directory's eyebrow, cell kicker and go label, and Spec Plate's cell labels (all were the page's body face at regular weight); Case File Row's cell kicker, Receipts Trio's attribution name and role, and Evidence Plate's stat numeral and label (were regular weight, already mono). Labels that sat on the `--tome-text-xs` step now use `--tome-type-size-xxs` (Phase Ledger, Practice Modes, Cost Ledger, Compare Ledger, Term Ledger, Wall Rows, and the labels above); on tome-ui's default scale the two steps are the same size. - **Cost Ledger coda.** It now reads `--tome-type-size-xl`, which equals the `--tome-text-h5` it used on tome-ui's default scale, so it renders the same there. No other change until a site sets a token or a block stores a band: - Every block heading and its accent phrase read `--tome-house-heading-*`, falling back to the heading's own values. - Every heading rule that sets a size (base headings and their `compact`, display, field-manual and rail variants) reads `--tome-dossier-heading-size`, falling back to its own size; a size an editor picks for a block (headline size or chrome text size) still wins over it. The heading-voice tokens carry no size; this one does. - Fit Prose, Spec Sheet, Spec Plate and Zone Directory paint a stored band (they ignored it before), and every block with a background field paints a band set per light and dark theme under tome-ui's `data-theme`. - On a block with a stored band, Case File Row's lit cell, Zone Directory's board and dark cells paint their own bands, and Cost Ledger's lit row gains a shadow. Proof Plates' flagged plate takes a band from `--tome-dossier-flag-bg`, `--tome-dossier-flag-fg` and `--tome-dossier-flag-accent-ink`. - Record Roster's index and coda, Evidence Plate's accent pull and Exhibit Artifact's tag read the band's companion inks first; the tag also reads `--tome-dossier-tag-bg` and `--tome-dossier-tag-fg`. Evidence Sheet's gap above the chips under the opening is `--tome-dossier-opening-chips-gap`.

  • e58ca95: Dossier headings read the heading-voice tokens and a new size token, stored bands paint on every block, and labels share one mono recipe. Visible changes on a default render: - **Labels.** These now use the mono face and weight 600: Fit Prose's kicker and column labels, Zone Directory's eyebrow, cell kicker and go label, and Spec Plate's cell labels (all were the page's body face at regular weight); Case File Row's cell kicker, Receipts Trio's attribution name and role, and Evidence Plate's stat numeral and label (were regular weight, already mono). Labels that sat on the `--tome-text-xs` step now use `--tome-type-size-xxs` (Phase Ledger, Practice Modes, Cost Ledger, Compare Ledger, Term Ledger, Wall Rows, and the labels above); on tome-ui's default scale the two steps are the same size. - **Cost Ledger coda.** It now reads `--tome-type-size-xl`, which equals the `--tome-text-h5` it used on tome-ui's default scale, so it renders the same there. No other change until a site sets a token or a block stores a band: - Every block heading and its accent phrase read `--tome-house-heading-*`, falling back to the heading's own values. - Every heading rule that sets a size (base headings and their `compact`, display, field-manual and rail variants) reads `--tome-dossier-heading-size`, falling back to its own size; a size an editor picks for a block (headline size or chrome text size) still wins over it. The heading-voice tokens carry no size; this one does. - Fit Prose, Spec Sheet, Spec Plate and Zone Directory paint a stored band (they ignored it before), and every block with a background field paints a band set per light and dark theme under tome-ui's `data-theme`. - On a block with a stored band, Case File Row's lit cell, Zone Directory's board and dark cells paint their own bands, and Cost Ledger's lit row gains a shadow. Proof Plates' flagged plate takes a band from `--tome-dossier-flag-bg`, `--tome-dossier-flag-fg` and `--tome-dossier-flag-accent-ink`. - Record Roster's index and coda, Evidence Plate's accent pull and Exhibit Artifact's tag read the band's companion inks first; the tag also reads `--tome-dossier-tag-bg` and `--tome-dossier-tag-fg`. Evidence Sheet's gap above the chips under the opening is `--tome-dossier-opening-chips-gap`.
  • cc5d280: Twelve dossier blocks gain an opt-in scroll reveal, switched on through the render context; with it off they render exactly as before. - **Blocks.** Cost Ledger, Spec Plate, Spec Sheet, Fit List, Fit Prose, Compare Ledger, Grants Ledger, Phase Ledger, Practice Modes, Zone Directory, Term Ledger and Wall Rows. The five blocks that already reveal on scroll are unchanged. - **Turning it on.** Pass `context={{ dossierReveal: true }}` to `RenderBlocks` / `RenderBlock`, or straight to a renderer, for the pack's own timing; pass an object to match a site's motion: `duration` and `stagger` in seconds, `ease` (any GSAP ease string), `start` (a ScrollTrigger start) and `rise` (a CSS length). Nothing is stored on the block, so there is no schema change and no migration. - **Off by default.** Without the context key a block renders no extra wrapper, class, attribute, inline style, stylesheet or client component. - **Accessibility.** Pending content is hidden by opacity only, and only once `RevealGate` (from `@wabbit/tome-blocks-house/reveal-gate`) has marked `<html>` before first paint, so content shows when JavaScript is off or fails. Under `prefers-reduced-motion: reduce` nothing is hidden and nothing moves. - New exports from `./render`: `readDossierReveal`, `DOSSIER_REVEAL_CONTEXT_KEY` and the `DossierRevealOptions` and `DossierRenderContext` types.
v0.5.2patch

775f90a: Published packages now contain compiled JavaScript and type declarations under a one-line licence banner, and no longer include source maps. What you install: one compiled `.js` (ESM) and `.cjs` (CommonJS) file per source module, its `.d.ts` / `.d.cts` declarations, and the stylesheets, fonts and other assets a package already shipped. Every JavaScript module opens with a comment naming the package and its licence: `/*! @wabbit/<package> — © Wabbit, LLC. Wabbit Tome Commercial License (see LICENSE.md). Not for redistribution. */`. The `.map` files and the `sourceMappingURL` comments that pointed at them are gone, which roughly halves the size of each tarball. Debugging: the code is still unbundled and unminified, one readable file per module, so a stack trace points at real code with real names. Line numbers in a stack trace are one higher than before, because of the banner line. A `'use client'` directive stays the first statement of its module (the banner is a comment above it), so React Server Component boundaries are unchanged. No API change, no runtime behaviour change, and nothing to do on upgrade. In `@wabbit/tome-blocks-gallery`, the source snapshots `extractGallerySource` writes from an installed pack leave out the licence banner line, so a component or config snapshot starts at the code and a paid block's preview shows its first 15 lines of real code.

  • 775f90a: Published packages now contain compiled JavaScript and type declarations under a one-line licence banner, and no longer include source maps. What you install: one compiled `.js` (ESM) and `.cjs` (CommonJS) file per source module, its `.d.ts` / `.d.cts` declarations, and the stylesheets, fonts and other assets a package already shipped. Every JavaScript module opens with a comment naming the package and its licence: `/*! @wabbit/<package> — © Wabbit, LLC. Wabbit Tome Commercial License (see LICENSE.md). Not for redistribution. */`. The `.map` files and the `sourceMappingURL` comments that pointed at them are gone, which roughly halves the size of each tarball. Debugging: the code is still unbundled and unminified, one readable file per module, so a stack trace points at real code with real names. Line numbers in a stack trace are one higher than before, because of the banner line. A `'use client'` directive stays the first statement of its module (the banner is a comment above it), so React Server Component boundaries are unchanged. No API change, no runtime behaviour change, and nothing to do on upgrade. In `@wabbit/tome-blocks-gallery`, the source snapshots `extractGallerySource` writes from an installed pack leave out the licence banner line, so a component or config snapshot starts at the code and a paid block's preview shows its first 15 lines of real code.
v0.5.1patch

87ebabd: Spec Sheet labels use the pack's mono label style: mono face, the `xxs` step and weight 600, as the other dossier blocks' labels do. Visible change: the label above each value was the page's body face at the `xs` step and regular weight. It kept the uppercase and wide tracking of a mono label without the face. The tracking now falls back to `0.2em` on a site that does not load the house tokens.

  • 87ebabd: Spec Sheet labels use the pack's mono label style: mono face, the `xxs` step and weight 600, as the other dossier blocks' labels do. Visible change: the label above each value was the page's body face at the `xs` step and regular weight. It kept the uppercase and wide tracking of a mono label without the face. The tracking now falls back to `0.2em` on a site that does not load the house tokens.
v0.5.0minor

c8f15cc: **BREAKING:** Six blocks now lay out on the page's reading lanes by default, and Case File Grid drops its 80rem width cap, so these blocks move on the page. **Migration:** upgrade `@wabbit/tome-blocks-house` to 0.8.1 or later (the peer range is now `>=0.8.1 <1.0.0`; 0.8.1 adds the lane grid's `lead` slot, which Term Ledger, Wall Rows, Proof Plates, Evidence Sheet and Record Roster use). No content or config changes. If the new placement does not suit a page, set the lane tokens below on that page; there is no switch back to the old flex layout. What you will see move on `cost-ledger`, `grants-ledger`, `practice-modes`, `zone-directory`, `case-file-row` and `compare-ledger`, from 1024px wide: - **Kicker.** It sits in the left marginalia lane at the lane's full width. It used to be as wide as its words (Cost Ledger, Grants Ledger), a fifth of the row (Practice Modes), a quarter (Case File Row) or 16rem (Zone Directory). Its top rule stops short of the heading. - **Heading.** It starts at the reading column in every block, one lane in from the content edge, whether or not there is a kicker. Compare Ledger has no kicker, so its heading now starts one lane in and the left lane stays empty. - **Intros.** Cost Ledger's intro and its centred coda sit in the reading column instead of starting at the content edge; Zone Directory's and Case File Row's descriptions sit under the heading. Intro paragraphs, and the closing statements of Practice Modes and Compare Ledger, are capped at `--tome-prose-max-width` when you set it. - **Content.** Tables, ledger rows, mode columns, cells and closing lines run the whole content column. Case File Row used to sit in an 80rem box with its own side padding; it now runs gutter to gutter. Cost Ledger's rows and column labels are inset to line up with the inset lit row. - **Band.** In a full-row block wrapper (RenderBlocks' default) the background paints the whole row and the content sits on the page's content lines, with the page grid's gutter as the only side inset. - **Narrower screens.** Below 1024px everything stacks on the content column: kicker, heading, then content. Practice Modes and Case File Row used to put the kicker beside the heading from 900px, and Cost Ledger, Grants Ledger and Zone Directory whenever the row had room; they now stack until 1024px. The lane widths are the page grid's: `--tome-grid-marginalia-left-cols`, `--tome-grid-marginalia-right-cols` and `--tome-grid-prose-pad-cols` (defaults 3, 3 and 2). Without tome-ui's page grid the blocks lay out the same tracks themselves, with `--tome-grid-padding` as the side gutter. **Case File Grid** loses its 80rem cap and its inline padding: its strip, title, tiles and closing link run the full width the page gives the block (the content column, gutter to gutter, on the page grid), like the pack's other full-width blocks. On a page without the page grid it now sits flush with its container, so give the container side padding. The other blocks that move in this release are listed in its companion entry; `ledger` and `shift-rows` render as before.

  • c8f15cc: **BREAKING:** Six blocks now lay out on the page's reading lanes by default, and Case File Grid drops its 80rem width cap, so these blocks move on the page. **Migration:** upgrade `@wabbit/tome-blocks-house` to 0.8.1 or later (the peer range is now `>=0.8.1 <1.0.0`; 0.8.1 adds the lane grid's `lead` slot, which Term Ledger, Wall Rows, Proof Plates, Evidence Sheet and Record Roster use). No content or config changes. If the new placement does not suit a page, set the lane tokens below on that page; there is no switch back to the old flex layout. What you will see move on `cost-ledger`, `grants-ledger`, `practice-modes`, `zone-directory`, `case-file-row` and `compare-ledger`, from 1024px wide: - **Kicker.** It sits in the left marginalia lane at the lane's full width. It used to be as wide as its words (Cost Ledger, Grants Ledger), a fifth of the row (Practice Modes), a quarter (Case File Row) or 16rem (Zone Directory). Its top rule stops short of the heading. - **Heading.** It starts at the reading column in every block, one lane in from the content edge, whether or not there is a kicker. Compare Ledger has no kicker, so its heading now starts one lane in and the left lane stays empty. - **Intros.** Cost Ledger's intro and its centred coda sit in the reading column instead of starting at the content edge; Zone Directory's and Case File Row's descriptions sit under the heading. Intro paragraphs, and the closing statements of Practice Modes and Compare Ledger, are capped at `--tome-prose-max-width` when you set it. - **Content.** Tables, ledger rows, mode columns, cells and closing lines run the whole content column. Case File Row used to sit in an 80rem box with its own side padding; it now runs gutter to gutter. Cost Ledger's rows and column labels are inset to line up with the inset lit row. - **Band.** In a full-row block wrapper (RenderBlocks' default) the background paints the whole row and the content sits on the page's content lines, with the page grid's gutter as the only side inset. - **Narrower screens.** Below 1024px everything stacks on the content column: kicker, heading, then content. Practice Modes and Case File Row used to put the kicker beside the heading from 900px, and Cost Ledger, Grants Ledger and Zone Directory whenever the row had room; they now stack until 1024px. The lane widths are the page grid's: `--tome-grid-marginalia-left-cols`, `--tome-grid-marginalia-right-cols` and `--tome-grid-prose-pad-cols` (defaults 3, 3 and 2). Without tome-ui's page grid the blocks lay out the same tracks themselves, with `--tome-grid-padding` as the side gutter. **Case File Grid** loses its 80rem cap and its inline padding: its strip, title, tiles and closing link run the full width the page gives the block (the content column, gutter to gutter, on the page grid), like the pack's other full-width blocks. On a page without the page grid it now sits flush with its container, so give the container side padding. The other blocks that move in this release are listed in its companion entry; `ledger` and `shift-rows` render as before.
  • 1656e52: **BREAKING:** Ten more blocks lay out on the page's reading lanes by default, so they move on the page. **Migration:** upgrade `@wabbit/tome-blocks-house` to 0.8.1 or later (the peer range is now `>=0.8.1 <1.0.0`; these blocks use the lane grid's `lead` slot, new in 0.8.1). No content or config changes, and every display option keeps working (`term-ledger`'s `headingFace` and `spec-measure`, `evidence-sheet`'s `ledgerRails` and `showCta`). If the new placement does not suit a page, set the lane tokens on that page; there is no switch back to the old layout. What you will see move, from 1024px wide unless a line says otherwise: - **Fit List, Fit Prose, Phase Ledger.** The kicker sits in the left marginalia lane at the lane's full width, its top rule stopping short of the heading; the heading starts at the reading column. They used to sit in a flex row (Fit List's kicker as wide as its words up to 18rem, Fit Prose's 16rem) or a grid with a 20% kicker column (Phase Ledger). Phase Ledger's intro stays under the heading and holds `--tome-prose-max-width` (62ch when unset). Fit List's two lists hold `--tome-prose-max-width` when you set it. Columns, rows and closing lines run the whole content width; the closing statements are capped at `--tome-prose-max-width` when it is narrower than 52ch. - **Receipts Trio.** The kicker and heading move to the left lane and the reading column (they were a one-quarter / three-quarter split inside an 80rem box). The body sits in the reading column with its text on `--tome-prose-max-width` (62ch when unset); the quote grid runs the whole content width, gutter to gutter. - **Proof Plates, card shell** (`shell: 'situational'`, or `auto` with no plate kickers). Kicker in the left lane, heading from the reading column (was one quarter / three quarters inside an 80rem box). An empty kicker no longer reserves its column. The intro hangs from the prose lane to the end of the reading column; the cards and the closing statement run the content width. - **Proof Plates, strip shell; Term Ledger; Wall Rows.** The kicker's rule and the heading (still held to 24ch) run the content width as before. The intro and the closing statement now start at the prose lane and run to the end of the reading column, instead of starting at the content edge; the intro text is capped at `--tome-prose-max-width` (62ch when unset) and the close at the narrower of 52ch and that token. Rows, walls and plates run the whole content width. Proof Plates loses its 80rem box. Term Ledger's optional figure still sits beside its rows, inside the content width. - **Evidence Sheet, ledger treatment.** The rail and the opening narrative swap sides and proportions: the rail now sits on the left, from the content edge to the prose lane and held one column short of it, and the opening hangs from the prose lane to the end of the reading column, its body on `--tome-prose-max-width` (62ch when unset). With `ledgerRails: 'split'` the opening sits in the prose lane and the right rail runs from the end of the prose lane to the content edge, again one column clear. The gap is one lane-grid column on any lane settings (it was a fluid gap between 2 : 3 : 2 tracks). Below 1024px it stacks as before: opening, then the rails. - **Evidence Sheet, table, grid and strip treatments.** The top strip, rows, chips and call to action run the whole content width, gutter to gutter, instead of an 80rem box. The call to action keeps its own width. - **Record Roster.** The kicker sits in the left lane with the rows beside it, from the prose lane to the end of the reading column (they used to stack, kicker above rows, in an 80rem box). The logo grid runs the content width; the coda centres in the reading column. Below 1024px the kicker stacks above the rows as before. - **Evidence Plate.** The eyebrow sits in the left lane above the stats; the stat row runs the content width; the body (its text on `--tome-prose-max-width`, 62ch when unset) and the sources sit in the reading column; the pull statement runs from the content edge toward the end of the reading column, still held to 26ch. It used to sit in an 80rem box. - **Bands.** In a full-row block wrapper (RenderBlocks' default) each band's background paints the whole row and its content sits on the page's content lines, with the page grid's gutter as the only side inset. - **Narrower screens.** Below 1024px everything stacks on the content column. Blocks that used to put the kicker beside the heading from 900px (Fit List, Fit Prose and Phase Ledger when the row had room, Receipts Trio and Proof Plates' cards from 900px) now stack until 1024px. The lane widths are the page grid's: `--tome-grid-marginalia-left-cols`, `--tome-grid-marginalia-right-cols` and `--tome-grid-prose-pad-cols` (defaults 3, 3 and 2). Without tome-ui's page grid the blocks lay out the same tracks themselves, with `--tome-grid-padding` as the side gutter. `ledger` and `shift-rows` render as before.
  • 5ea0301: Term Ledger, Comparison Table and Evidence Sheet gain opt-in display options, all off by default. Additive: a block that does not set an option renders the same markup and styles as before. Adding the fields to a Payload config adds their columns, so run your usual migration. - **Term Ledger.** A `headingFace` select (`head` by default, `display` for a larger, light serif heading) and a third variant, `spec-measure`: the `spec` band with each row body capped at `--tome-prose-max-width` (62ch when unset) while the rules still run the full width. - **Comparison Table.** A `railHeader` checkbox lays the eyebrow out as a narrow ruled column beside a large headline, whose `*accent*` runs drop to their own line in the accent ink; the headline reads the `--tome-house-heading-*` heading-voice tokens, falling back to the block's own voice. An `emphasisHighlight` checkbox paints highlighted columns with a solid fill in the band's text colour instead of the subtle accent tint. - **Evidence Sheet.** A `ledgerRails` select for the Ledger treatment (`single` by default). `split` cuts the rows by count across a left and a mirrored right rail, centres the opening narrative between them and moves the scope and stack chips under it; with no opening, the chips and the call to action close the right rail.
v0.4.1patch

f18bfd0: The Ledger and Shift Rows call-to-action links now use the `rel` a registered CTA resolver supplies. With no resolver registered the markup is unchanged: a new-tab link still gets `target="_blank"` and `rel="noopener noreferrer"`. Needs `@wabbit/tome-blocks-house` with CTA resolver registration to take effect.

  • f18bfd0: The Ledger and Shift Rows call-to-action links now use the `rel` a registered CTA resolver supplies. With no resolver registered the markup is unchanged: a new-tab link still gets `target="_blank"` and `rel="noopener noreferrer"`. Needs `@wabbit/tome-blocks-house` with CTA resolver registration to take effect.
v0.4.0minor

6be9a25: Evidence Sheet can now show its call to action in the Strip and Ledger treatments, with a new "Show call to action" toggle. Additive: the new `showCta` checkbox defaults to on for new blocks, and existing documents render as before (link in Table and Grid only) until they are re-saved. Unticking it hides the link in every treatment. In Strip the link sits on its own line after the row; in Ledger it closes the opening narrative, or the ledger column when there is no narrative. The ledger now has a gap between its two columns at wide widths.

  • 6be9a25: Evidence Sheet can now show its call to action in the Strip and Ledger treatments, with a new "Show call to action" toggle. Additive: the new `showCta` checkbox defaults to on for new blocks, and existing documents render as before (link in Table and Grid only) until they are re-saved. Unticking it hides the link in every treatment. In Strip the link sits on its own line after the row; in Ledger it closes the opening narrative, or the ledger column when there is no narrative. The ledger now has a gap between its two columns at wide widths.
v0.3.3patch

483e0a1: The dossier pack's comparison table, wall rows and spec plate previews now use fictional content. The comparison table shows an invented analytics plan ladder, wall rows tell an invented company story, and the spec plate lists generic technology cells. Block fields and variants are unchanged.

  • 483e0a1: The dossier pack's comparison table, wall rows and spec plate previews now use fictional content. The comparison table shows an invented analytics plan ladder, wall rows tell an invented company story, and the spec plate lists generic technology cells. Block fields and variants are unchanged.
  • d08fc38: The exhibit artifact block no longer overflows a phone-width screen. The block's root is a figure whose default side margins were added on top of its full width; it now sizes with border-box and zero inline margin, and long header and subject text wraps.
v0.3.2patch

c14a133: The pack works on a stock Next.js site: its per-block stylesheets now ship precompiled, so the site needs no next.config plugin. Each renderer's `.tome-css` stylesheet is compiled when the pack is built, into a JS module next to it (`<Name>.tome-css.js` / `.cjs`), and the renderers import that. A site no longer has to wrap next.config with `withTomeBlockStyles` to use the pack, and `./render` and `./render/register` now load under plain Node, so seed scripts, tests and the Payload CLI can import them. Each block's CSS is still inlined only on pages that render the block. A site that already uses `withTomeBlockStyles` needs no change. Scoped class names change once, because they are now keyed on the package rather than on where it is installed. The raw `.tome-css` files stay in the package as readable source.

  • c14a133: The pack works on a stock Next.js site: its per-block stylesheets now ship precompiled, so the site needs no next.config plugin. Each renderer's `.tome-css` stylesheet is compiled when the pack is built, into a JS module next to it (`<Name>.tome-css.js` / `.cjs`), and the renderers import that. A site no longer has to wrap next.config with `withTomeBlockStyles` to use the pack, and `./render` and `./render/register` now load under plain Node, so seed scripts, tests and the Payload CLI can import them. Each block's CSS is still inlined only on pages that render the block. A site that already uses `withTomeBlockStyles` needs no change. Scoped class names change once, because they are now keyed on the package rather than on where it is installed. The raw `.tome-css` files stay in the package as readable source.
v0.3.1patch

8c84e70: Accent and primary text on solid-dark bands is readable in light themes. case-file-row, receipts-trio, evidence-sheet, term-ledger, ledger, grants-ledger, proof-plates, cost-ledger and zone-directory read the band's accent and primary inks from `resolveBackground`, with their previous tokens as fallback. Flagged proof plates and dark zone-directory cells no longer take a solid-dark band, because nothing paints that band yet and its light inks would land on the light card; they render exactly as before.

  • 8c84e70: Accent and primary text on solid-dark bands is readable in light themes. case-file-row, receipts-trio, evidence-sheet, term-ledger, ledger, grants-ledger, proof-plates, cost-ledger and zone-directory read the band's accent and primary inks from `resolveBackground`, with their previous tokens as fallback. Flagged proof plates and dark zone-directory cells no longer take a solid-dark band, because nothing paints that band yet and its light inks would land on the light card; they render exactly as before.
v0.3.0minor

**Breaking: block stylesheets are now per-block (`.tome-css`).** Each block's CSS ships only on pages that render it, instead of in every page's CSS bundle. The 24 stylesheets moved from `X.module.css` to `X.tome-css`, and each renderer renders `<BlockStyles sheet={styles} />` from `@wabbit/tome-blocks-core/block-styles`. **Required in the consuming site:** wrap next.config with `withTomeBlockStyles` (`@wabbit/tome-blocks-core/next`, blocks-core 0.22.0 or later); without it the `.tome-css` imports fail to build. See the blocks-core README, "Per-block stylesheets". - The `@wabbit/tome-blocks-core` peer range is now `>=0.22.0 <1.0.0`. - ProofPlates is a client component; it is now a server-safe wrapper (`ProofPlates.tsx`, which renders the stylesheet and registers the renderer) around `ProofPlates.client.tsx`, with its exported types re-exported. Four blocks already split into a server renderer plus a `...Motion.client.tsx` keep that split; the client half imports the same `.tome-css` for class names. Exported names and props are unchanged. - Block CSS now loads after all bundled CSS. A site-level rule that overrode one of this pack's classes at equal specificity, and won only by loading later, no longer wins.

  • **Breaking: block stylesheets are now per-block (`.tome-css`).** Each block's CSS ships only on pages that render it, instead of in every page's CSS bundle. The 24 stylesheets moved from `X.module.css` to `X.tome-css`, and each renderer renders `<BlockStyles sheet={styles} />` from `@wabbit/tome-blocks-core/block-styles`. **Required in the consuming site:** wrap next.config with `withTomeBlockStyles` (`@wabbit/tome-blocks-core/next`, blocks-core 0.22.0 or later); without it the `.tome-css` imports fail to build. See the blocks-core README, "Per-block stylesheets". - The `@wabbit/tome-blocks-core` peer range is now `>=0.22.0 <1.0.0`. - ProofPlates is a client component; it is now a server-safe wrapper (`ProofPlates.tsx`, which renders the stylesheet and registers the renderer) around `ProofPlates.client.tsx`, with its exported types re-exported. Four blocks already split into a server renderer plus a `...Motion.client.tsx` keep that split; the client half imports the same `.tome-css` for class names. Exported names and props are unchanged. - Block CSS now loads after all bundled CSS. A site-level rule that overrode one of this pack's classes at equal specificity, and won only by loading later, no longer wins.
v0.2.2patch

7862f30: Swaps the plain primary token for the on-solid-dark pairing on text and borders that sit on a dark surface in three packs. `@wabbit/tome-blocks-dossier-pack`: the `evidence-sheet` block's `ledger` treatment now uses the on-solid-dark pairing for its kicker, headline emphasis, body link and row-label text, instead of the plain primary token — that token is a surface-tint fill, not guaranteed legible as text on the block's dark surface. `@wabbit/tome-blocks-cinema-pack`: the `scene-plate` block's seated panel kicker and body emphasis text get the same on-solid-dark pairing, since the panel itself is a partially-opaque dark surface over the scene image. `@wabbit/tome-blocks-catalog-pack`: the `price-table` block's highlighted-tier border now uses the on-solid-dark pairing specifically on the `dark` variant, leaving the default/light variant's border on the plain primary token unchanged.

  • 7862f30: Swaps the plain primary token for the on-solid-dark pairing on text and borders that sit on a dark surface in three packs. `@wabbit/tome-blocks-dossier-pack`: the `evidence-sheet` block's `ledger` treatment now uses the on-solid-dark pairing for its kicker, headline emphasis, body link and row-label text, instead of the plain primary token — that token is a surface-tint fill, not guaranteed legible as text on the block's dark surface. `@wabbit/tome-blocks-cinema-pack`: the `scene-plate` block's seated panel kicker and body emphasis text get the same on-solid-dark pairing, since the panel itself is a partially-opaque dark surface over the scene image. `@wabbit/tome-blocks-catalog-pack`: the `price-table` block's highlighted-tier border now uses the on-solid-dark pairing specifically on the `dark` variant, leaving the default/light variant's border on the plain primary token unchanged.
v0.2.1patch

c3468b0: `register()` is now built with blocks-core's `createPackRegistrar`, and media fields take their `relationTo` from `mediaRelation(config)` instead of a local `as CollectionSlug` cast. Behaviour and signatures are unchanged. The `@wabbit/tome-blocks-core` peer floor goes up to `>=0.18.0` because that is the first version exporting the helpers.

  • c3468b0: `register()` is now built with blocks-core's `createPackRegistrar`, and media fields take their `relationTo` from `mediaRelation(config)` instead of a local `as CollectionSlug` cast. Behaviour and signatures are unchanged. The `@wabbit/tome-blocks-core` peer floor goes up to `>=0.18.0` because that is the first version exporting the helpers.
v0.2.0minor

404d325: Tome block packs now install into an existing Payload project the way the README says: one `npm install`, one CSS import, no undocumented steps. Proven by the new fresh-install smoke test (`scripts/blocks-fresh-install-smoke.mjs`) against a brand-new `create-payload-app` website-template site. **Consumers: list `@wabbit/tome-blocks-core` and `@wabbit/tome-ui` in your own `package.json`** if you import from them (npm 7+ and pnpm install required peers automatically, so a fresh `npm install` of a pack already brings them in). - **One shared `blocks-core` per site.** Every pack, `blocks-house` and `blocks-extras` now declare `@wabbit/tome-blocks-core` (and, where used, `-house` / `-extras`) as a required peer with an explicit range instead of a regular dependency, so a site gets exactly one hoisted copy and one adapter registry. - **No more ERESOLVE in plain Payload sites.** `blocks-core` no longer declares `@wabbit/tome-core` or `@wabbit/tome-catalog` (their optional peer graph pulled `better-auth` → `@sveltejs/kit` → `vite@8` against a site's `vite@7`). The `block-bundle` product type still auto-registers when both are installed; new structural types `BlockBundleProductTypeDeps`, `BlockBundleProductTypeRegistryLike`, `RegisterProductTypeHooksLike`. - **Tokens in one line:** `@import '@wabbit/tome-blocks-core/styles.css';` (new export; imports `@wabbit/tome-ui/tokens`). `@wabbit/tome-ui` is now a required peer of `blocks-core`. - **Rich text and images render with no adapter setup.** Built-in defaults render Lexical through `@payloadcms/richtext-lexical/react` and resolve populated Payload uploads; an unpopulated upload id warns once in every environment (previously content vanished silently in production). Registered adapters still win. - **Payload's spread-props convention:** new `adaptRenderersForPayload(renderers)` / `adaptRendererForPayload(Component)` wrap any pack's `renderers` map for a site that renders `<Block {...block} />`. - **Slug collisions with Payload's templates** (`cta`, `banner`, `archive`, `content`, `code`): new `applyBlockSlugOverrides(blocks, overrides)` and `remapRendererSlugs(renderers, overrides)` (`@wabbit/tome-blocks-core/slugOverrides`). Defaults are unchanged; no stored data migrates. - **`blocks-house`** owns `gsap` and `hls.js` as dependencies (previously optional peers that still broke the build when missing), and registers GSAP's `ScrollTrigger` itself before first use. - **Full-bleed bands actually span the grid.** Eight `pinnedBand` blocks (cinema-pack AmbientBand, MediaPanel, PullInterlude, SceneCaption, ScenePlate, ScrubStory, StatementBand; blocks-house FullBleedInterstitial) now declare `grid-column: 1 / -1` at their root as the contract requires. **Visible change:** inside a tome-ui `.grid`, these render edge to edge where they were previously squeezed to content width. - **`@wabbit/tome-ui`:** `.grid` declares `reading-start` / `reading-end` below 768px (aliased to the content column), so blocks placed on the reading column no longer collapse to a sliver on phones. - Every pack README gains an "Install into an existing Payload project" section and a peer table that matches `package.json`; `blocks-core`'s README carries the full walkthrough.

  • 404d325: Tome block packs now install into an existing Payload project the way the README says: one `npm install`, one CSS import, no undocumented steps. Proven by the new fresh-install smoke test (`scripts/blocks-fresh-install-smoke.mjs`) against a brand-new `create-payload-app` website-template site. **Consumers: list `@wabbit/tome-blocks-core` and `@wabbit/tome-ui` in your own `package.json`** if you import from them (npm 7+ and pnpm install required peers automatically, so a fresh `npm install` of a pack already brings them in). - **One shared `blocks-core` per site.** Every pack, `blocks-house` and `blocks-extras` now declare `@wabbit/tome-blocks-core` (and, where used, `-house` / `-extras`) as a required peer with an explicit range instead of a regular dependency, so a site gets exactly one hoisted copy and one adapter registry. - **No more ERESOLVE in plain Payload sites.** `blocks-core` no longer declares `@wabbit/tome-core` or `@wabbit/tome-catalog` (their optional peer graph pulled `better-auth` → `@sveltejs/kit` → `vite@8` against a site's `vite@7`). The `block-bundle` product type still auto-registers when both are installed; new structural types `BlockBundleProductTypeDeps`, `BlockBundleProductTypeRegistryLike`, `RegisterProductTypeHooksLike`. - **Tokens in one line:** `@import '@wabbit/tome-blocks-core/styles.css';` (new export; imports `@wabbit/tome-ui/tokens`). `@wabbit/tome-ui` is now a required peer of `blocks-core`. - **Rich text and images render with no adapter setup.** Built-in defaults render Lexical through `@payloadcms/richtext-lexical/react` and resolve populated Payload uploads; an unpopulated upload id warns once in every environment (previously content vanished silently in production). Registered adapters still win. - **Payload's spread-props convention:** new `adaptRenderersForPayload(renderers)` / `adaptRendererForPayload(Component)` wrap any pack's `renderers` map for a site that renders `<Block {...block} />`. - **Slug collisions with Payload's templates** (`cta`, `banner`, `archive`, `content`, `code`): new `applyBlockSlugOverrides(blocks, overrides)` and `remapRendererSlugs(renderers, overrides)` (`@wabbit/tome-blocks-core/slugOverrides`). Defaults are unchanged; no stored data migrates. - **`blocks-house`** owns `gsap` and `hls.js` as dependencies (previously optional peers that still broke the build when missing), and registers GSAP's `ScrollTrigger` itself before first use. - **Full-bleed bands actually span the grid.** Eight `pinnedBand` blocks (cinema-pack AmbientBand, MediaPanel, PullInterlude, SceneCaption, ScenePlate, ScrubStory, StatementBand; blocks-house FullBleedInterstitial) now declare `grid-column: 1 / -1` at their root as the contract requires. **Visible change:** inside a tome-ui `.grid`, these render edge to edge where they were previously squeezed to content width. - **`@wabbit/tome-ui`:** `.grid` declares `reading-start` / `reading-end` below 768px (aliased to the content column), so blocks placed on the reading column no longer collapse to a sliver on phones. - Every pack README gains an "Install into an existing Payload project" section and a peer table that matches `package.json`; `blocks-core`'s README carries the full walkthrough.
v0.1.4patch

Updated dependencies [e044594] - @wabbit/tome-blocks-core@0.16.6

  • Updated dependencies [e044594] - @wabbit/tome-blocks-core@0.16.6
v0.1.3patch

Updated dependencies [a2f2dfa] - @wabbit/tome-blocks-house@0.3.0

  • Updated dependencies [a2f2dfa] - @wabbit/tome-blocks-house@0.3.0
v0.1.2patch

37fca4e: Text over the solid-dark surface now uses `var(--tome-color-on-solid-dark, var(--tome-color-surface-inverse))` — the tome-ui pairing idiom — so consumers on tome-ui < 0.10 (which lacks `on-solid-dark`) no longer render dark-on-black in light theme (case-file-grid, case-file-row, evidence-sheet, ledger, receipts-trio, zone-directory).

  • 37fca4e: Text over the solid-dark surface now uses `var(--tome-color-on-solid-dark, var(--tome-color-surface-inverse))` — the tome-ui pairing idiom — so consumers on tome-ui < 0.10 (which lacks `on-solid-dark`) no longer render dark-on-black in light theme (case-file-grid, case-file-row, evidence-sheet, ledger, receipts-trio, zone-directory).
  • Updated dependencies [7850b7a] - @wabbit/tome-blocks-house@0.2.0
v0.1.1patch

b529fa6: Demo content is brand-free: compare-ledger's emphasised column ("The Wabbit way" → "The documented way"), shift-rows' heading (no platform name), and a demo subject renamed away from an internal persona name. Catalog captures are a public surface for anyone licensing Tome; demo fiction must not name Wabbit, Tome, or house personas. Demo props now carry `_variant`, so gallery thumbs for term-ledger, ledger, grants-ledger, shift-rows and fit-list render the requested variant instead of the default.

  • b529fa6: Demo content is brand-free: compare-ledger's emphasised column ("The Wabbit way" → "The documented way"), shift-rows' heading (no platform name), and a demo subject renamed away from an internal persona name. Catalog captures are a public surface for anyone licensing Tome; demo fiction must not name Wabbit, Tome, or house personas. Demo props now carry `_variant`, so gallery thumbs for term-ledger, ledger, grants-ledger, shift-rows and fit-list render the requested variant instead of the default.