Blocks Signal Theme

Blocks & Themes
@wabbit/tome-blocks-signal-themev0.18.0

Addon-tier Signal editorial theme — 33 Star-Citizen-flavored operational blocks (ship cards, personnel cards, comm intercepts, classification banners, threat panels) for org and editorial sites; ships bundled inside @wabbit/tome-sc.

Installnpm install @wabbit/tome-blocks-signal-theme

Overview

@wabbit/tome-blocks-signal-theme

Tier: Addon — Signal theme blocks for Star Citizen org and editorial sites.

39 Signal-theme editorial blocks total (23 offered; 16 pre-consolidation slugs stay registered for existing content but are no longer offered by the gallery/inserter — see src/index.ts's OFFERED_BLOCKS/DEPRECATED_BLOCKS split).

SC-tier positioning

Signal was always SC-flavored — several blocks carry Star-Citizen-specific semantics (signal-ship-card reads an RSI-style ship data schema; signal-personnel-card reads an org-member card shape; comm-intercept/classification-banner/threat-panel are all SC operational-intel concepts). Rather than splitting those blocks out into @wabbit/tome-blocks-sc-pack or leaving the pack fighting its own identity as "generic," signal-theme ships bundled inside `@wabbit/tome-sc` as its Signal editorial pack, alongside @wabbit/tome-blocks-sc-pack and @wabbit/tome-cop. Nothing code-level moved — this is a positioning/ownership decision, not a block migration:

  • Tier stays `'addon'` (no new tier enum value — see the blocks family CHANGELOG's 0.7.0 entry, "signal-theme + sc-pack: tier 'pro' → 'addon'").
  • Tags now include 'sc' alongside 'star-citizen' (matching blocks-sc-pack's tag set).
  • This package remains independently installable on any editorial site (non-SC included) — the generic editorial DNA that doesn't carry SC-specific semantics already flowed into @wabbit/tome-longform / dispatch / readout (approved 2026-05-03). Working outside SC contexts is a bonus, not a design constraint.
  • Installing @wabbit/tome-sc bundles this package automatically (createSCPlatform's blocks.signalTheme config, default true) — see that package's README for the one-call composition path.

Block List

| Slug | Name | Notes | |---|---|---| | signal-banner | Signal Banner | Consolidation parent (Batch 5) — full-width status banner: classification level or ship system alert. One block, seven status variants. Supersedes signal-classification-banner, signal-system-alert | | signal-comms | Signal Comms | Consolidation parent (Batch 5) — intercepted-communications or multi-speaker comms-transcript block. One block, six variants. Supersedes signal-comm-intercept, signal-comms-transcript | | signal-divider | Signal Divider | Consolidation parent (Batch 5) — structural break: numbered phase marker, decorative chapter divider, or named anchor-section header. One block, nine variants. Supersedes signal-phase-marker, signal-chapter-divider, signal-anchor-section | | signal-nav | Signal Nav | Consolidation parent (Batch 5) — navigation link card: cross-system link (with optional stat metrics) or multi-part series navigator. One block, five variants. Supersedes signal-cross-link, signal-series-nav | | signal-note | Signal Note | Consolidation parent (Batch 5) — styled editorial note: tactical/info/lore/log callout, floating or inline aside, author/editor note, or literary epigraph. One block, twelve variants. Supersedes signal-callout, signal-aside, signal-author-aside, signal-epigraph | | signal-stats | Signal Stats | Consolidation parent (Batch 5) — stats/metrics block: key-facts panel, multi-metric grid, or horizontal stat strip. One block, nine variants. Supersedes signal-key-facts, signal-metric-grid, signal-stat-strip | | signal-accordion | Signal Accordion | Expandable panels, single/stacked/FAQ variants | | signal-ambient-audio | Signal Ambient Audio | Background audio toggle | | signal-anchor-section | Signal Anchor Section | Named section anchor for in-page navigation | | signal-aside | Signal Aside | Inline aside annotation | | signal-author-aside | Signal Author Aside | Author bio sidebar card | | signal-callout | Signal Callout | Highlighted callout box | | signal-chapter-divider | Signal Chapter Divider | Visual chapter break | | signal-classification-banner | Signal Classification Banner | Document classification header | | signal-comm-intercept | Signal Comm Intercept | Intercepted transmission UI | | signal-comms-transcript | Signal Comms Transcript | Communication log transcript | | signal-cross-link | Signal Cross Link | Cross-document reference link | | signal-data-table | Signal Data Table | Structured data table | | signal-drop-cap | Signal Drop Cap | Large decorative drop cap opener | | signal-epigraph | Signal Epigraph | Introductory quote block | | signal-footnotes | Signal Footnotes | Collected footnotes with back-links | | signal-image-grid | Signal Image Grid | Multi-image grid | | signal-key-facts | Signal Key Facts | Key fact summary panel | | signal-log-header | Signal Log Header | Mission/operation log header | | signal-map-legend | Signal Map Legend | Map key/legend panel | | signal-metric-grid | Signal Metric Grid | Grid of metrics/stats | | signal-objective-list | Signal Objective List | Mission objective checklist | | signal-personnel-card | Signal Personnel Card | Personnel/crew member card | | signal-phase-marker | Signal Phase Marker | Phase/chapter progression marker | | signal-progress-bar | Signal Progress Bar | Progress indicator | | signal-redacted | Signal Redacted | Redacted/censored text block | | signal-sensor-readout | Signal Sensor Readout | Sensor data display | | signal-series-nav | Signal Series Nav | Series navigation links | | signal-ship-card | Signal Ship Card | Star Citizen ship specification card | | signal-spoiler | Signal Spoiler | Collapsible spoiler reveal | | signal-stat-strip | Signal Stat Strip | Horizontal stat row | | signal-system-alert | Signal System Alert | System/status alert banner | | signal-tabbed-content | Signal Tabbed Content | Tabbed panel layout | | signal-threat-panel | Signal Threat Panel | Threat assessment panel |

Install

Tome site (via meta-package — recommended)

If your site uses @wabbit/tome-blocks, you already have this bundle. No additional install needed. The meta-package's registerAll() call includes signal-theme.

Stock Payload site — add to an existing blocks field

npm install @wabbit/tome-blocks-signal-theme

@wabbit/tome-blocks-core and @wabbit/tome-blocks-extras are required peers and install automatically (npm 7+/pnpm). @wabbit/tome-ui is a regular dependency of this pack (not a peer) and installs automatically too. Add the blocks you want alongside your existing ones 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 { signalBannerBlock, signalNoteBlock } from '@wabbit/tome-blocks-signal-theme'
// blocks: [...existingBlocks, signalBannerBlock.block(), signalNoteBlock.block()]
// blockComponents.ts
import { renderers as signalRenderers } from '@wabbit/tome-blocks-signal-theme/render/register'
import { adaptRenderersForPayload } from '@wabbit/tome-blocks-core/render'
// blockComponents: { ...adaptRenderersForPayload(signalRenderers) }
@import '@wabbit/tome-blocks-core/styles.css';

Registering everything from scratch instead, wire the register() call into your Payload config:

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

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

register(blockRegistry, bundleRegistry)

export default buildConfig({
  collections: [
    {
      slug: 'pages',
      fields: [
        {
          name: 'layout',
          type: 'blocks',
          blocks: blockRegistry.resolveAll(),
        },
      ],
    },
  ],
})

Peer dependencies

Generated from package.json#peerDependencies (the README gate fails if this table and the manifest disagree).

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

@wabbit/tome-ui is a regular dependency of this pack (not a peer) and installs automatically.

Public API

| Export | Subpath | Description | |---|---|---| | 39 block descriptors (signalAccordionBlock, signalShipCardBlock, signalPersonnelCardBlock, …, one per row in the Block List above) + register(blockRegistry, bundleRegistry) | . | Payload block config descriptors and bundle registration — OFFERED_BLOCKS/DEPRECATED_BLOCKS split lives in src/index.ts | | One render component per offered/deprecated slug (SignalAccordion, SignalShipCard, SignalPersonnelCard, …) | ./render | Legacy self-registering render barrel | | renderers map + registerRenderers() | ./render/register | Explicit registration (server-safe adapter contract) — no side effects on import | | Demo props feeding the Starter's auto-gallery route | ./demo | Payload-free fixture data | | Bundle/block metadata (slugs + variants) | ./meta | Payload-free surface for the gallery storefront |

Surface / ink pairing contract

Signal blocks split into two families, and the split is load-bearing for readability. Get it wrong and the block renders ink-on-ink — that is exactly what happened before 2026-08-09, when the pack paired --tome-color-surface (the CARD family) with --tome-color-foreground (the PAGE family). Those are two independently themable house tokens; a consumer that set one and not the other, or set them to different lightnesses — both entirely legal — split the pair, and 15 components measured 1.66–2.46:1 on starter.wabbit.com.

Panel blocks paint an operational dark surface and own both halves of every pair through the --signal-* set. No --tome-color-* surface or text token is read in this family.

| Token | Default | Role | |---|---|---| | --signal-panel | hsl(220 14% 9%) | base panel surface | | --signal-panel-raised | hsl(220 13% 14%) | headers, insets, tracks, nested cards | | --signal-line | hsl(220 10% 28%) | panel chrome | | --signal-ink | hsl(220 12% 92%) | primary text — 15.0:1 on panel, 13.2:1 on raised | | --signal-ink-muted | hsl(220 9% 70%) | secondary text — 8.4:1 / 7.4:1 | | --signal-ink-faint | hsl(220 8% 56%) | metadata — 5.4:1 / 4.7:1 |

Prose blocks (AmbientAudio, AnchorSection, ChapterDivider, ClassificationBanner, DropCap, Epigraph, Footnotes, ImageGrid, plus the INLINE/BLOCK_PARA variants of Redacted and the label of Spoiler) paint nothing. They sit in the article and correctly use the house page pair --tome-color-background / --tome-color-foreground / --tome-color-on-surface-muted. Do not migrate these to `--signal-ink` — a near-white ink on a light article is the same bug pointing the other way.

Accents carry three roles per hue for the same reason one value cannot serve both surfaces:

| Token | Used for | |---|---| | --signal-accent | identity colour — fills, rules, borders, dots; legible mid-tone for prose contexts | | --signal-accent-on-panel | the same hue lightened to clear 4.5:1 as TEXT on --signal-panel | | --signal-on-accent | the ink that sits ON a --signal-accent fill, chosen per hue |

Override as a SET, never individually. @wabbit/tome-cop overrides the whole set together (tokens/cop-tokens.css §5.2.8b); so should you. Redefining --signal-panel alone re-creates the split by hand.

Server / client posture

The large majority of this pack's renderers are plain static components. Three carry a 'use client' directive: SignalSpoiler (collapsible reveal state), SignalTabbedContent (tab-panel state), SignalAccordion (expand/collapse state) — verified 2026-07-12, scripts/assert-rsc-boundaries.mjs MANIFEST: blocks-signal-theme: 3. Prefer the ./render/register subpath (registerRenderers()) when registering from payload.config.ts or any Node/server context — the legacy ./render barrel re-exports CSS Modules at its root, which crashes Node's ESM loader outside a bundler (see @wabbit/tome-blocks's README, "Two rules for consumers"); ./render/register imports each component directly and sidesteps that barrel-level hazard.

Compatibility

| Dependency | Version | |---|---| | @wabbit/tome-blocks-core | >=0.18.0 <1.0.0 (peer) | | @wabbit/tome-blocks-extras | >=0.16.3 <1.0.0 (peer) | | @wabbit/tome-ui | workspace:^ (regular dependency) | | payload | >=3.67.0 (peer) | | react | >=19.0.0 (peer) | | react-dom | >=19.0.0 (peer) | | @payloadcms/richtext-lexical | >=3.67.0 (peer) |

Decisions that shaped this package

  • SC-tier ownership under `@wabbit/tome-sc` — rather than splitting the SC-flavored blocks (ship-card, personnel-card, comm-intercept, threat-panel) into @wabbit/tome-blocks-sc-pack, signal-theme ships bundled as @wabbit/tome-sc's Signal editorial pack while remaining independently installable on any editorial site.
  • Tier `addon`, not `pro` — signal-theme (and sc-pack) were reclassified from pro to the addon tier value the tiering/extras-split work added to the bundle-tier enum, tagged 'sc' alongside the existing 'star-citizen' tag.
  • 39 total → 23 offered / 16 deprecated consolidation — the inserter+variant family-consolidation pass introduced 6 consolidation-parent blocks (signal-banner, signal-comms, signal-divider, signal-nav, signal-note, signal-stats) and narrowed what the gallery/inserter actively offers while keeping the 16 pre-consolidation slugs registered so existing authored content keeps rendering.
  • Generic editorial DNA was extracted elsewhere, not genericized here — the non-SC-coupled parts of this block family's ancestry were promoted into separate @wabbit/tome-longform/tome-dispatch/tome-readout packs instead of stripping signal-theme's own SC-specific schemas.
  • Server-safe adapter/RSC clientization — part of the same clientization wave as the rest of the blocks family: only 3 of 39 renderers (SignalSpoiler, SignalTabbedContent, SignalAccordion — all state-bearing) stay client, enforced by scripts/assert-rsc-boundaries.mjs.

Blocks

signal-accordion

A Signal accordion — a stack of expandable panels, each with a title, optional mono eyebrow Label, and rich-text body. Single-expand (opening one closes the last), Stacked (all panels independent), or FAQ presentation.

When to use
  • A FAQ section where questions expand to reveal answers
  • Collapsing long supplementary or technical detail so the page scans short
  • Grouping several optional reading paths the reader opens on demand
Page types
  • dossier
  • docs
  • faq
  • editorial-article
How to use

Set designVersion (SINGLE / STACKED / FAQ) and an accentColor matching the Signal section. Add at least one item; each item needs a title and takes an optional Label eyebrow plus rich-text content. Use SINGLE when only one panel should be open at a time; STACKED when readers may open several. For step-by-step mission objectives use `signal-objective-list`, and for switching between parallel content panels use `signal-tabbed-content`.

Pairs with
  • signal-tabbed-content
  • signal-note
  • signal-data-table
Avoid when
  • Showing parallel views the reader switches between, not stacks — use `signal-tabbed-content`
  • A short list of objectives with status — use `signal-objective-list`
  • Hiding a single spoiler or redacted passage — use `signal-spoiler` or `signal-redacted`
Register in

dossier

signal-ambient-audio

An ambient-audio indicator — a small animated diegetic cue (bridge comms chatter, engine hum, alert klaxon) with a mono Label. Pure atmosphere: it sets the soundscape of a scene without playing actual audio.

When to use
  • Establishing the ambient soundscape of a log entry or scene (comms / engine / alert)
  • Marking a tense moment in a dossier narrative with an alert-state cue
  • Adding diegetic flavor beneath a log header or transcript
Page types
  • dossier
  • editorial-article
  • blog
How to use

Set designVersion (COMMS / ENGINE / ALERT), write a short uppercase Label (e.g. "BRIDGE COMMS CHATTER"), and pick an accentColor — ALERT typically pairs with red, ENGINE with amber, COMMS with cyan or blue. This is decorative atmosphere only; for an actual intercepted message use `signal-comm-intercept`, and for a status notice use `signal-system-alert`.

Pairs with
  • signal-log-header
  • signal-comm-intercept
  • signal-sensor-readout
Avoid when
  • You need to render an actual message or transmission body — use `signal-comm-intercept` or `signal-comms`
  • Communicating a real status / warning to the reader — use `signal-system-alert`
Register in

dossier

signal-banner

A full-width Signal status banner. Pick a variant: the classification variants (Declassified, Restricted, Secret) render a classification strip; the alert variants (Hull Breach, Quantum, Comms, All Clear) render an animated ship-system alert. Author the banner text and an accent color.

When to use
  • Marking a document or section with a classification level — Restricted / Secret / Declassified
  • Raising an in-fiction ship/system alert above content — Hull Breach / Quantum / Comms / All Clear
Page types
  • dossier
  • landing
  • marketing
  • about
How to use

Pick the variant for the status you want, write the banner text (e.g. "RESTRICTED // EXPEDITION COMMAND // EYES ONLY" or "HULL BREACH DETECTED — SECTION 4-ALPHA"), and set the accent color to match the surrounding Signal section.

Pairs with
  • signal-log-header
  • signal-personnel-card
  • signal-threat-panel
Avoid when
  • Body copy or a styled note — use `signal-note`
  • A standing alert that needs dismissal/interaction — this is a static immersive banner
Register in

marketing-landing

signal-comms

A Signal comms block. Pick a variant: Comm Intercept (an intercepted-signal readout with frequency / signal-strength header and clean / degraded / encrypted styling), or Comms Transcript (a multi-speaker transcript with per-callsign colors, optional redaction, and standard / intercepted / literary styling).

When to use
  • Showing an intercepted radio/comm exchange with a frequency header — Comm Intercept variants
  • A multi-speaker transcript log with callsign colors and redaction — Comms Transcript variants
Page types
  • editorial-article
  • dossier
  • blog
  • about
How to use

Pick the variant. Comm Intercept authors a Messages array (callsign / content / timestamp) plus an optional frequency and signal-strength header. Comms Transcript authors a Lines array (callsign / callsign color / message / timestamp / redacted flag) plus an optional header label. Both take a base accent color.

Pairs with
  • signal-log-header
  • signal-redacted
  • signal-personnel-card
Avoid when
  • A plain styled note or aside — use `signal-note`
  • A tabular data readout — use `signal-data-table`
Register in

editorial

signal-data-table

A Signal-styled tabular dataset — defined Headers plus an array of Rows of cells (any cell can be highlighted bold/bright), with an optional caption. Standard for inline tables, Breakout to widen, or Comparison for side-by-side columns.

When to use
  • Presenting rows-and-columns data: specs, manifests, comparison matrices
  • A reference table that needs an optional caption and per-cell emphasis
  • A side-by-side comparison of two or more options (Comparison variant)
Page types
  • dossier
  • docs
  • about
How to use

Set designVersion (STANDARD / BREAKOUT / COMPARISON) and an accentColor. Define Headers first, then Rows whose cell count matches the headers; toggle highlight on any cell to bold/brighten it. Use the breakout width field to widen beyond the article column. For label-value summary panels (not a true grid) use `signal-stats`; for sensor contact tables use `signal-sensor-readout`.

Pairs with
  • signal-stats
  • signal-progress-bar
  • signal-sensor-readout
Avoid when
  • A handful of label-value facts rather than a row/column grid — use `signal-stats`
  • Live sensor contacts with IFF/range/bearing — use `signal-sensor-readout`
  • Single-value progress meters — use `signal-progress-bar`
Register in

dossier

signal-divider

A Signal structural divider. Pick a variant: Phase Marker (a numbered badge + title — Standard / Colored / Compact), Chapter Divider (a decorative break — Ornamental / Numbered / Symbol), or Anchor Section (a named header that emits an in-page anchor `id` — Standard / Colored / Compact).

When to use
  • Marking a numbered phase or stage in body copy — Phase Marker variants
  • A decorative break between chapters or major sections — Chapter Divider variants
  • A section header that doubles as an in-page anchor target — Anchor Section variants
Page types
  • editorial-article
  • blog
  • dossier
  • about
How to use

Pick the variant. Phase / Chapter (numbered) author a Number + Title; Chapter (ornamental / symbol) need no fields. Anchor variants author an Anchor ID (used for in-page linking) + Title, with an optional Subtitle. All variants take an accent color. Title/Subtitle are hidden in the Compact phase/anchor variants.

Pairs with
  • signal-note
  • signal-key-facts
  • signal-objective-list
Avoid when
  • A full-width status banner — use `signal-banner`
  • A clickable link card or series nav — use `signal-nav`
Register in

editorial

signal-drop-cap

signal-footnotes

A numbered footnotes / references apparatus — an array of notes (number + text, with an optional category). Standard renders one flat numbered list; Categorized groups notes under headings like "Sources" or "Technical".

When to use
  • Closing an article or dossier section with numbered citations or references
  • Collecting source notes, technical caveats, and lore asides in one block
  • Grouping references by type — Sources vs. Technical vs. Lore (Categorized)
Page types
  • editorial-article
  • dossier
  • docs
  • blog
How to use

Set designVersion (STANDARD or CATEGORIZED). Add each note with its reference number and text; in CATEGORIZED also set the category so notes cluster under that heading. Numbers are authored, not auto-generated, so they must match the markers in your body copy. Place it near the end of the reading flow — it terminates a section rather than opening one. For inline source asides mid-copy use a `signal-note` callout instead.

Pairs with
  • signal-note
  • signal-cross-link
  • signal-series-nav
Follows
  • signal-note
Avoid when
  • A single inline source aside within body copy — use `signal-note`
  • Navigation between related entries — use `signal-cross-link` or `signal-series-nav`
Register in

editorial

signal-image-grid

A multi-image gallery — at least two uploaded images, each with optional caption and alt text, laid out as a 2-column, 3-column, asymmetric, or masonry grid with an optional group caption.

When to use
  • Showing a set of related visuals together — reference shots, environment plates, a photo array
  • A gallery where mixed aspect ratios read best as masonry or an asymmetric grid
  • Grouping captioned images under one shared group caption
Page types
  • dossier
  • portfolio
  • editorial-article
  • blog
How to use

Set designVersion (TWO_COL / THREE_COL / ASYMMETRIC / MASONRY) and a breakout width to widen the gallery beyond the article column. Add two or more images; per image set an optional caption and altText (altText falls back to the media alt field). MASONRY suits mixed aspect ratios; THREE_COL suits uniform thumbnails. For a single full-bleed figure use a standard image block instead.

Pairs with
  • signal-ship-card
  • signal-personnel-card
  • signal-note
Avoid when
  • A single hero or figure image — use a standalone image block
  • A spec card built around one ship render — use `signal-ship-card`
Register in

dossier

signal-log-header

A diegetic log-entry header — a masthead of metadata (log ID, date, location, ship, author) that frames a passage as an in-world log. Ship, Personal, or Mission framing.

When to use
  • Opening an in-world log entry, captain’s log, or after-action report
  • Stamping a narrative passage with date / ship / signatory metadata
  • Establishing diegetic context above body copy in a dossier
Page types
  • dossier
  • editorial-article
  • blog
How to use

Set designVersion (SHIP / PERSONAL / MISSION) and an accentColor. Fill the metadata you have — logId, date, location, ship, author; empty fields are omitted, so partial headers are fine. Place it at the top of the log passage as a masthead. Follow with body copy and optionally `signal-ambient-audio` for soundscape. For a mid-document divider between log entries use `signal-divider`.

Pairs with
  • signal-ambient-audio
  • signal-objective-list
  • signal-divider
Precedes
  • signal-objective-list
Avoid when
  • A standard editorial article headline — use a normal heading
  • A section break between entries rather than a header — use `signal-divider`
Register in

dossier

signal-map-legend

A key/legend — an optional title plus an array of entries, each a colored symbol and label. Horizontal renders an inline key strip; Sidebar stacks the entries in a column beside a map or diagram.

When to use
  • Explaining the colored symbols on an adjacent map, chart, or tactical diagram
  • A standalone color/symbol key for an image or figure
  • A sidebar key beside an asymmetric image grid or diagram (Sidebar variant)
Page types
  • dossier
  • docs
  • about
How to use

Set designVersion (HORIZONTAL or SIDEBAR) and add entries; each entry needs a label and a color, with an optional symbol character (e.g. "◆", "●"). Use SIDEBAR next to a map or `signal-image-grid` figure, HORIZONTAL as a caption-style strip beneath one. It only renders the key — supply the map/figure separately. For a tabular dataset use `signal-data-table`.

Pairs with
  • signal-image-grid
  • signal-sensor-readout
  • signal-data-table
Avoid when
  • Rows-and-columns data rather than a symbol key — use `signal-data-table`
  • Listing sensor contacts with IFF/range — use `signal-sensor-readout`
Register in

dossier

signal-nav

A Signal navigation card. Pick a variant: Cross-Link (a single link card to another system — Standard, or Enhanced with a strip of stat metrics) or Series Nav (a navigator across the parts of a multi-part series — Top Banner / Bottom Card / Sidebar).

When to use
  • Linking to a related operation, intel, codex, or external destination — Cross-Link variants
  • Navigating between the parts of a multi-part series — Series Nav variants
Page types
  • editorial-article
  • blog
  • dossier
  • landing
How to use

Pick the variant. Cross-Link authors a Title, optional Description, a URL, a link Type (badge color), and — in Enhanced — a strip of metric label/value pairs. Series Nav authors a Title plus a list of Parts (each a title + URL, with one marked as the current article). All variants take an accent color.

Pairs with
  • signal-divider
  • signal-key-facts
  • signal-stat-strip
Avoid when
  • A styled editorial aside — use `signal-note`
  • A full-width status banner — use `signal-banner`
Register in

marketing-landing

signal-note

A styled Signal editorial note. Pick a variant: Callout (tactical / info / lore / log boxes with an optional label), Aside (supplementary content floating into the right/left margin or inline), Author’s Note (an indented or margin-overlay editor note), or Epigraph (a centered / accent-border / verse literary quote with attribution).

When to use
  • Calling out a tactical, informational, lore, or log note in body copy — Callout variants
  • A margin or inline aside of supplementary content — Aside variants
  • An author / editor note alongside the article — Author’s Note variants
  • A literary epigraph or pull-quote with attribution — Epigraph variants
Page types
  • editorial-article
  • blog
  • dossier
  • about
How to use

Pick the variant. Callout / Aside / Author’s Note author rich-text Content + an optional Label (Callout and Author’s Note also take an accent color). Epigraph authors plain Text plus optional Attribution + Source and an accent color. Asides do not use an accent color.

Pairs with
  • signal-drop-cap
  • signal-footnotes
  • signal-epigraph
Avoid when
  • A full-width status banner — use `signal-banner`
  • Primary body copy — use a content block; Signal Note is a styled aside to it
Register in

editorial

signal-objective-list

A mission-objectives checklist — an optional title plus objectives, each carrying a status (complete / failed / in-progress / pending) and an optional sub-note. Standard lists them flat; Phased groups objectives under phase headings.

When to use
  • Tracking mission or quest objectives with per-item status
  • A staged plan whose steps group into phases (Phased variant)
  • Showing what was achieved vs. failed in an after-action report
Page types
  • dossier
  • docs
  • about
How to use

Set designVersion (STANDARD or PHASED), an optional title (e.g. "MISSION OBJECTIVES"), and an accentColor. Add each objective with its text and a status; add an optional note for detail. In PHASED set the phase label on each objective to cluster them under headings. Sits naturally below a `signal-log-header`. For expandable Q&A use `signal-accordion`; for percentage meters use `signal-progress-bar`.

Pairs with
  • signal-log-header
  • signal-progress-bar
  • signal-threat-panel
Follows
  • signal-log-header
Avoid when
  • Percentage / completion meters rather than a status checklist — use `signal-progress-bar`
  • Expandable question-and-answer panels — use `signal-accordion`
Register in

dossier

signal-personnel-card

A personnel roster — up to twelve people, each with name, optional callsign, role, avatar, and a status badge (active / deployed / standby / inactive). Inline renders compact rows; Grid renders a card grid.

When to use
  • Introducing a crew, team, or cast of characters with roles and status
  • A dramatis-personae or roster panel in a dossier
  • Showing who is active / deployed / standby on a mission
Page types
  • dossier
  • about
  • portfolio
How to use

Set designVersion (INLINE or GRID) and an accentColor. Add up to twelve people; each needs a name and may add callsign, role, an avatar upload, and a status. Use GRID for a visual card wall with avatars, INLINE for a denser list. For a single ship rather than a person use `signal-ship-card`; for a captioned image array use `signal-image-grid`.

Pairs with
  • signal-ship-card
  • signal-log-header
  • signal-image-grid
Avoid when
  • Profiling a vessel rather than a person — use `signal-ship-card`
  • A generic captioned image gallery — use `signal-image-grid`
Register in

dossier

signal-progress-bar

One or more 0–100 progress meters — each bar a label, a value, and a per-bar color. Single for one meter, Stacked for several labeled bars, or Segmented for a notched gauge.

When to use
  • Showing completion, capacity, or charge as a percentage meter
  • Comparing several proportional values as stacked labeled bars
  • A status gauge — hull integrity, fuel, mission completion
Page types
  • dossier
  • docs
  • about
How to use

Set designVersion (SINGLE / STACKED / SEGMENTED) and an accentColor. Add up to eight bars; each needs a label and a value 0–100, plus a per-bar color (cyan / green / amber / red / ghost) to encode state. Use SINGLE for one headline meter, STACKED to compare several. For label-value figures without a bar use `signal-stats`; for status-tracked checklist items use `signal-objective-list`.

Pairs with
  • signal-stats
  • signal-sensor-readout
  • signal-objective-list
Avoid when
  • Plain numeric facts without a proportional meter — use `signal-stats`
  • A rows/columns dataset — use `signal-data-table`
  • Status checklist items rather than percentages — use `signal-objective-list`
Register in

dossier

signal-redacted

signal-sensor-readout

A tactical sensor display — a titled list of contacts, each with a designation, type, IFF status (friendly / hostile / unknown), range, and bearing. Contacts mode for a target list; System mode for a systems readout.

When to use
  • A tactical "what’s on sensors" panel in a combat or patrol scene
  • Listing detected contacts with IFF, range, and bearing
  • A systems-status readout framed as instrument output (System variant)
Page types
  • dossier
  • editorial-article
How to use

Set designVersion (CONTACTS or SYSTEM), a title (e.g. "SENSOR CONTACTS — SECTOR 7G"), and an accentColor. Add each contact with a designation; type, IFF, range, and bearing are optional but make the readout convincing. IFF drives the contact’s color coding. For a threat-level assessment of the situation use `signal-threat-panel`; for a generic data grid use `signal-data-table`.

Pairs with
  • signal-threat-panel
  • signal-map-legend
  • signal-data-table
Avoid when
  • An overall threat assessment rather than a contact list — use `signal-threat-panel`
  • A generic rows/columns table — use `signal-data-table`
Register in

dossier

signal-ship-card

A ship spec card — name, class, manufacturer, an image, and a list of spec label/value pairs. Horizontal or Vertical layout, or Comparison to render a second ship side-by-side.

When to use
  • Profiling a single vessel with its key specifications
  • A side-by-side comparison of two ships (Comparison variant)
  • A spec sheet for any catalogued craft or vehicle in a dossier
Page types
  • dossier
  • portfolio
  • about
How to use

Set designVersion (HORIZONTAL / VERTICAL / COMPARISON) and an accentColor. Fill shipName (required) plus class, manufacturer, an image upload, and up to eight spec label/value rows. Choosing COMPARISON reveals the second-ship group for a side-by-side. For people rather than vessels use `signal-personnel-card`; for a generic spec table use `signal-data-table`.

Pairs with
  • signal-personnel-card
  • signal-data-table
  • signal-image-grid
Avoid when
  • Profiling crew or characters — use `signal-personnel-card`
  • A long flat spec table without the card framing — use `signal-data-table`
Register in

dossier

signal-spoiler

signal-stats

A Signal stats panel. Pick a variant: Key Facts (a sidebar / strip / compact panel of label-value pairs with status indicators), Metric Grid (a 4 / 3 / 2-column grid of metrics with per-metric accent colors, sublabels, and deltas), or Stat Strip (a horizontal standard / breakout / compact strip of stats with deltas and per-stat colors).

When to use
  • A summary panel of key facts beside an article — Key Facts variants
  • A grid of headline metrics with change indicators — Metric Grid variants
  • A compact horizontal band of stats — Stat Strip variants
Page types
  • dossier
  • landing
  • marketing
  • about
How to use

Pick the variant. Key Facts authors an optional Title plus a Facts array (label / value / status). Metric Grid authors a Metrics array (label / value / sublabel / delta / per-metric accent) and a breakout width. Stat Strip authors a Stats array (label / value / delta / per-stat color) and a breakout width. All variants take a base accent color.

Pairs with
  • signal-data-table
  • signal-progress-bar
  • signal-sensor-readout
Avoid when
  • A tabular dataset with rows/columns — use `signal-data-table`
  • A single progress meter — use `signal-progress-bar`
Register in

marketing-landing

signal-tabbed-content

A tabbed panel — a tab bar plus one rich-text content pane per tab, switching the visible pane on click. Underline or Pill tab styling.

When to use
  • Presenting parallel views of the same subject the reader switches between (Overview / Specs / Lore)
  • Saving vertical space by collapsing several content sections behind tabs
  • Organizing alternate readings where only one is shown at a time
Page types
  • dossier
  • docs
  • about
  • faq
How to use

Set designVersion (UNDERLINE or PILL) and an accentColor. Add a tab per view; each tab has a short label and a rich-text content body. Keep labels terse so the bar reads cleanly. Use this when views are mutually exclusive; if readers may want several open at once use `signal-accordion` instead. For a top-of-document jump nav use `signal-cross-link`.

Pairs with
  • signal-accordion
  • signal-note
  • signal-data-table
Avoid when
  • Readers may want multiple panels open at once — use `signal-accordion`
  • A short, always-visible aside — use `signal-note`
Register in

dossier

signal-threat-panel

A threat-assessment panel — a required threat level (critical / elevated / advisory), a title, optional rich-text details, and up to six status indicators (active / monitoring / resolved). Full or Compact presentation.

When to use
  • Flagging a tactical threat assessment with a graded level
  • A situation report whose severity drives prominent color coding
  • Tracking the state of several threat indicators in one panel
Page types
  • dossier
  • editorial-article
How to use

Set designVersion (FULL or COMPACT) and an accentColor. The threatLevel (CRITICAL / ELEVATED / ADVISORY) and a title are required; threatLevel drives the panel’s alarm styling. Add rich-text details and up to six indicators, each with a label and a status. Pairs with `signal-sensor-readout` to show the contacts behind the assessment. For a general status notice rather than a graded threat use `signal-system-alert`.

Pairs with
  • signal-sensor-readout
  • signal-objective-list
  • signal-system-alert
Avoid when
  • A general status / notice without a graded threat level — use `signal-system-alert`
  • A list of detected contacts — use `signal-sensor-readout`
Register in

dossier

Exports

  • @wabbit/tome-blocks-signal-theme
  • @wabbit/tome-blocks-signal-theme/render
  • @wabbit/tome-blocks-signal-theme/render/register
  • @wabbit/tome-blocks-signal-theme/demo
  • @wabbit/tome-blocks-signal-theme/meta

Changelog

v0.18.0patch

c3468b0: `register()` is now built with blocks-core's `createPackRegistrar`. Behaviour and signature are unchanged. The `@wabbit/tome-blocks-core` peer floor goes up to `>=0.18.0` because that is the first version exporting the helper.

  • c3468b0: `register()` is now built with blocks-core's `createPackRegistrar`. Behaviour and signature are unchanged. The `@wabbit/tome-blocks-core` peer floor goes up to `>=0.18.0` because that is the first version exporting the helper.
v0.17.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.
  • Updated dependencies [404d325] - @wabbit/tome-ui@0.13.1
v0.16.1patch

Updated dependencies [befde64] - @wabbit/tome-ui@0.13.0

  • Updated dependencies [befde64] - @wabbit/tome-ui@0.13.0
v0.16.0minor

b01ca1f: Raise the `react` / `react-dom` peer floor to `>=19.0.0` across the linked blocks family. Every pack advertised `react: >=18.0.0` while `@wabbit/tome-core`, `chrome`, `forms`, `dispatch`, `longform` and `readout` all require `>=19` — so the React 18 support the packs claimed was **unreachable in any real Tome stack**: no consumer could satisfy both halves of the graph. The advertised range was not a supported configuration, it was a range nobody could install into. Ruled 2026-09-01: the floor becomes the truth. Their `devDependencies` said the same thing from the other direction: `react` and `@types/react` pinned to `^18.0.0` while the root `pnpm.overrides` has pinned `@types/react` to `19.2.14` for months, so every pack has in fact been developed and tested against React 19 types the whole time. Those pins move to `^19.0.0` — a manifest correction, not a version change; the resolved tree is byte-identical. One coordinated bump for the family (these eleven are `linked` in `.changeset/config.json`, so they version together by design). Consumer impact: a consumer genuinely on React 18 can no longer install these packs. That consumer could not have had a working Tome install anyway — the kernel would have refused the same graph. Anyone on React 19 sees no change.

  • b01ca1f: Raise the `react` / `react-dom` peer floor to `>=19.0.0` across the linked blocks family. Every pack advertised `react: >=18.0.0` while `@wabbit/tome-core`, `chrome`, `forms`, `dispatch`, `longform` and `readout` all require `>=19` — so the React 18 support the packs claimed was **unreachable in any real Tome stack**: no consumer could satisfy both halves of the graph. The advertised range was not a supported configuration, it was a range nobody could install into. Ruled 2026-09-01: the floor becomes the truth. Their `devDependencies` said the same thing from the other direction: `react` and `@types/react` pinned to `^18.0.0` while the root `pnpm.overrides` has pinned `@types/react` to `19.2.14` for months, so every pack has in fact been developed and tested against React 19 types the whole time. Those pins move to `^19.0.0` — a manifest correction, not a version change; the resolved tree is byte-identical. One coordinated bump for the family (these eleven are `linked` in `.changeset/config.json`, so they version together by design). Consumer impact: a consumer genuinely on React 18 can no longer install these packs. That consumer could not have had a working Tome install anyway — the kernel would have refused the same graph. Anyone on React 19 sees no change.
  • 0836ef5: dist now raw-Node loadable: relative specifiers get explicit extensions post-build. `build` gains `&& node ../../scripts/fix-dist-extensions.mjs --strict` as its last step, joining the 13 packages that already ran it. tsup builds `bundle: false` and emits relative specifiers exactly as the TypeScript source wrote them — extensionless — which bundlers resolve and raw Node does not (ESM `ERR_MODULE_NOT_FOUND`; CJS worse, `require('./x')` finds the ESM `.js` twin and Node 22+ `require(esm)` then dies on that file's own extensionless import). Every consumer outside a bundler hit this: the payload CLI under plain node, `generate:types`, `generate:importmap`, ops scripts, codegen tools. No source changes, no API changes, and bundler consumers are unaffected — extensioned relative specifiers are universally resolvable. Two supporting changes made the wiring possible, both in repo scripts rather than package source. `fix-dist-extensions.mjs` now skips bundler-asset specifiers (`.css`, `.module.css`, `.scss`, fonts, images, shaders) by explicit extension allowlist instead of reporting them as unresolvable — that single gap is why the 13 prior adopters were exactly the 13 packages that ship no CSS, since `--strict` exited 1 on any package with a relative stylesheet import. Dotted MODULE names (`./config.meta`, `./x.variants`, `./y.demo`) are deliberately NOT treated as assets and still get `.js`/`.cjs` appended. `assert-node-loadable.mjs` gained the matching carve-outs so the new repo-wide CI gate reports real defects only: a resolution failure whose path lands under `node_modules` is a peer SKIP (next@15 has no exports map, so `next/image` fails as an absolute path), and a bundler-asset load failure is an environmental SKIP (CJS surfaces it as `SyntaxError: Unexpected token '.'` raised from inside the stylesheet). Verified before/after on four packages built one at a time: print 8 FAIL → 0, readout 22 FAIL → 0, ai 3 FAIL → 0, gamification 2 FAIL → 0 (its failure was the other signature — a `directory import` missing `/index`). cop was already clean on a fresh build, so the audit's "27 of 46 fail" figure includes at least one package whose local dist was merely stale.
  • 73081e6: Manifest metadata: `homepage`, `bugs`, `engines`. All 46 publishable manifests were missing the three fields a consumer sees before any code (2026-09-01 sale-readiness audit §6). Metadata only — no source, no build, no runtime change. - `homepage` deep-links to that package README on GitHub (`.../tree/main/packages/<dir>#readme`). Without it a registry page links to the monorepo root and the reader has to guess which of 46 folders they want. - `bugs.url` points at the repo issue tracker, so a paying customer has a place to report a defect that is not email. - `engines.node` is `>=22`, matching the root `engines` and `.nvmrc` set the same day. This is a real floor, not decoration: CI on Node 20 could not expand the glob the block packs use for `node --test`, and a package installed on Node 20 fails at a runtime the installer cannot connect back to the version. The forcing function ships with the change: `scripts/assert-manifest-metadata.mjs` (root `pnpm assert:manifest-metadata`, wired into `platform-discipline.yml` beside `assert:license-metadata`) fails when any publishable manifest lacks `description`, `repository.directory` matching its own folder, `homepage`, `bugs`, `engines.node` equal to the repo floor, `license`, `files` or `sideEffects`. It reported 138 violations before this change and 0 after.
  • 73081e6: README peer tables, and the gate that now requires them. Sixteen packages declared `peerDependencies` and documented them nowhere a reader could scan — in prose inside an install paragraph, in a transposed "compatibility matrix" with the peers as columns, or not at all. Docs only; no source, no manifest, no runtime change (the one manifest change in this PR, admin's `sonner` peer, has its own changeset). Each of the sixteen gains a `## Peer dependencies` section generated from its own `package.json` — `| Peer | Range | Required |`, one row per peer, the range verbatim, `no (optional)` read from `peerDependenciesMeta`, plus one sentence on what is a real `dependency` rather than a peer and why the optional ones are optional. The worst omissions this surfaced: `@wabbit/tome-core` documented 2 of its 13 peers and left out both `next` and `@payloadcms/richtext-lexical`, which are required; `@wabbit/tome-admin` listed 5 of 20; `@wabbit/tome-readout` and `@wabbit/tome-sc` listed none. Eight block packs carried a hand-typed compatibility table that had drifted a full React major — still `>=18` after the peer floor moved to `>=19.0.0` — and none of the eight listed `react-dom` at all. Those tables are retired in favour of the generated one, with a line saying what they used to claim so the next reader does not reinstate them. The forcing function ships with the fix: `scripts/assert-readme-contract.mjs` now FAILS a package that declares peers without a peer table (a markdown table whose header row names a Peer and a Range column — the existing `Optional?` and `Notes` third columns still pass, so the thirty already-conforming READMEs were not touched). It is deliberately shape-only, not row-level: asserting that each row agrees with the manifest is the Tier 2 generation work. Verified non-vacuous by breaking one table's header and watching the gate fail, then restoring it. `CONTRIBUTING.md`'s assert-script list — which said "five" while sixteen existed — and the three guides that describe this gate were corrected in the same pass.
  • Updated dependencies [57875ba]
  • Updated dependencies [b01ca1f]
  • Updated dependencies [0836ef5]
  • Updated dependencies [73081e6]
  • Updated dependencies [b01ca1f]
  • Updated dependencies [090e984]
  • Updated dependencies [73081e6] - @wabbit/tome-blocks-core@0.16.0 - @wabbit/tome-blocks-extras@0.16.0 - @wabbit/tome-ui@0.12.0
v0.15.12patch

54ff357: Republish with correctly-built artifacts, and close the hole that made it necessary. The 0.15.11 line shipped `defaultBreakout` in its version notes but **not in its tarballs**: source on main carried all 151 descriptors, the published `dist/` carried zero. `pnpm publish` does not build — it packs whatever is already in `dist/` — so a release ships whatever a previous, possibly unrelated, build left behind. Here the dist had been built from a branch that predated the metadata, and nothing in the pipeline compares artifact to source. Caught by grepping the _installed_ package in a consumer rather than trusting the version number. **Systemic fix:** `prepublishOnly: pnpm run build` added to all 41 publishable packages that lacked it (only `blocks-org-pack` had one — which is why it was the single package whose build ran during the previous publish). Every publish now rebuilds from source first, so a stale-dist release becomes impossible rather than merely unlikely. Same family as the two publish hazards already documented in this repo (`workspace:*` literals reaching the registry, and exact-pin dependencies forcing nested duplicate copies): the publish path had no guard that what ships matches what is committed.

  • 54ff357: Republish with correctly-built artifacts, and close the hole that made it necessary. The 0.15.11 line shipped `defaultBreakout` in its version notes but **not in its tarballs**: source on main carried all 151 descriptors, the published `dist/` carried zero. `pnpm publish` does not build — it packs whatever is already in `dist/` — so a release ships whatever a previous, possibly unrelated, build left behind. Here the dist had been built from a branch that predated the metadata, and nothing in the pipeline compares artifact to source. Caught by grepping the _installed_ package in a consumer rather than trusting the version number. **Systemic fix:** `prepublishOnly: pnpm run build` added to all 41 publishable packages that lacked it (only `blocks-org-pack` had one — which is why it was the single package whose build ran during the previous publish). Every publish now rebuilds from source first, so a stale-dist release becomes impossible rather than merely unlikely. Same family as the two publish hazards already documented in this repo (`workspace:*` literals reaching the registry, and exact-pin dependencies forcing nested duplicate copies): the publish path had no guard that what ships matches what is committed.
  • Updated dependencies [54ff357] - @wabbit/tome-blocks-extras@0.15.12
v0.15.11patch

1bebcdc: Populate `BlockMeta.defaultBreakout` across all nine block packs — 151 descriptors now declare their natural width on the page grid. The 2026-06-28 layout-grid + nesting contract (Decision 3 / Amendment A1) defined `defaultBreakout` as the per-block breakout POLICY co-located with the block, replacing a central hand-maintained table. No pack had ever filled it in, so every consumer fell through to `'article'` (the reading column) and a full-bleed hero previewed at prose width. Values are drawn from the canonical `@wabbit/tome-ui` `BreakoutWidthValue` vocabulary and assigned from each block's render CSS, not its name: - Root at a named grid line (`content-start / content-end`, `full-start / full-end`, `marginalia-right-*`, …) — `defaultBreakout` mirrors that exact line. - Root `1 / -1` + subgrid with an inner wrapper at `content-start / content-end` — a self-banding block: `'full-bleed'`. - Root and inner both `1 / -1` (width-agnostic) — assigned editorially: bands/heroes `'full-bleed'`, page sections `'content'`, cards and single-column components `'breakout-md'`, prose/inline components `'article'`. - Where a block already ships its own `breakoutWidthField({ defaultValue })`, `defaultBreakout` matches that value exactly rather than contradicting it. Distribution: `content` 52, `full-bleed` 41, `article` 33, `breakout-md` 23, `breakout-lg` 1, `marginalia-right` 1. Also declares `pinnedBand: true` on the three blocks that are unambiguously full-bleed bands whose own `breakoutWidth` field drives INNER content (`compareColumns`, `editorialSection`, `editorialSpread`), and `nestable: false` on 22 full-bleed heroes / band primitives / containers that carried no `nestable` declaration. Purely additive optional metadata: no descriptor field, block structure, or CSS changed, and no existing `nestable: true` was flipped, so the derived `layoutGrid` child allowlist is byte-identical (98 nestable blocks before and after).

  • 1bebcdc: Populate `BlockMeta.defaultBreakout` across all nine block packs — 151 descriptors now declare their natural width on the page grid. The 2026-06-28 layout-grid + nesting contract (Decision 3 / Amendment A1) defined `defaultBreakout` as the per-block breakout POLICY co-located with the block, replacing a central hand-maintained table. No pack had ever filled it in, so every consumer fell through to `'article'` (the reading column) and a full-bleed hero previewed at prose width. Values are drawn from the canonical `@wabbit/tome-ui` `BreakoutWidthValue` vocabulary and assigned from each block's render CSS, not its name: - Root at a named grid line (`content-start / content-end`, `full-start / full-end`, `marginalia-right-*`, …) — `defaultBreakout` mirrors that exact line. - Root `1 / -1` + subgrid with an inner wrapper at `content-start / content-end` — a self-banding block: `'full-bleed'`. - Root and inner both `1 / -1` (width-agnostic) — assigned editorially: bands/heroes `'full-bleed'`, page sections `'content'`, cards and single-column components `'breakout-md'`, prose/inline components `'article'`. - Where a block already ships its own `breakoutWidthField({ defaultValue })`, `defaultBreakout` matches that value exactly rather than contradicting it. Distribution: `content` 52, `full-bleed` 41, `article` 33, `breakout-md` 23, `breakout-lg` 1, `marginalia-right` 1. Also declares `pinnedBand: true` on the three blocks that are unambiguously full-bleed bands whose own `breakoutWidth` field drives INNER content (`compareColumns`, `editorialSection`, `editorialSpread`), and `nestable: false` on 22 full-bleed heroes / band primitives / containers that carried no `nestable` declaration. Purely additive optional metadata: no descriptor field, block structure, or CSS changed, and no existing `nestable: true` was flipped, so the derived `layoutGrid` child allowlist is byte-identical (98 nestable blocks before and after).
  • 48773ac: Fix a systemic, invisible-text readability defect across the block packs: a text colour and the surface under it were coming from sources a consumer can set independently, so the pair could split. Measured live on starter.wabbit.com with a canvas-based contrast parser walking every rendered text node: the worst pairs sat at **1.00–1.03:1** — near-black text on a near-black surface, copy that renders but cannot be read. Nothing in CI could see it, because every unit test and every route smoke passes with perfectly invisible text. This is the second time this bug family has shipped. The first sweep added `--tome-color-on-solid-dark` (blocks-lms-pack 0.12.1) and fixed chrome, lms-pack and catalog-pack; the themed packs were missed. This closes the rest and adds the tokens whose absence is why the misuse kept spreading. ## Three mechanisms, one root cause **1. Split pairs.** Blocks paired `--tome-color-card` — not a house token at all; the house name is `--tome-color-surface` — carrying a DARK literal fallback, against `--tome-color-foreground`, which every themed consumer does define, carrying a LIGHT one. A fallback pair is only safe when both sides fall back together. The same shape appeared as cross-family pairing (`surface`, the CARD family, paired with `foreground`, the PAGE family) and as clobbering: a band setting `color: background` on itself while its children hardcoded their own `color: foreground`, which wins. longform had a third variant — it read `--tome-color-muted-foreground` 32 times and `--tome-color-muted` 3 times, and **neither has ever been a house token**, so the entire muted tier silently fell through to `currentColor` and inherited whatever ink an ancestor happened to have. **2. Alpha-dimmed text.** `opacity: 0.4–0.9` on a label, and `color-mix(<colour> 30–70%, transparent)` as a `color:`, composite against whatever happens to be behind them, so the ratio is unknowable at author time. Worst measured: 1.46:1. Several were an ancestor `opacity` aimed at a rule that dimmed the real text in the same container along with it. **3. Status and brand colours used as text.** `warning`/`success`/`error`/ `destructive` are FILL colours, tuned to be painted as a badge with an ink on top. Used as `color:` the default amber measures 2.13:1 and the green 2.82:1. Brand hues have the mirror problem: `--tome-color-primary` as text is fine on the page (near-black by default, 5.5:1 even under the starter's oxide theme) but becomes 2.22:1 inside a band whose fill the consumer chooses. ## New in `@wabbit/tome-ui` - **`--tome-color-{success,warning,error,destructive,info}-text`** — the missing text-weight companions. Literals with inverted `[data-theme="dark"]` values, each pinned to clear 4.5:1 against both `--tome-color-background` and `--tome-color-surface`. `info` never had a fill token either, which is why packs reached for `primary`. Rule of thumb: `warning` paints a box, `warning-text` writes a word. - **`--muted-foreground` retuned** (`hsl(215 16% 47%)` → `hsl(215 20% 38%)` light, `65%` → `72%` dark). That token is `--tome-color-on-surface-muted`, the tier every pack uses for captions, labels, metadata and table headers, and at the old value it reached only 4.27:1 on `--card`. The entire secondary text tier platform-wide sat just under AA — which is also why packs kept reaching past it for something with more presence. Standalone default only. Because these are literals rather than Layer 1 aliases, `@wabbit/tome-cop` restates them: a pack that is dark without being `[data-theme="dark"]` would otherwise inherit the light values. ## Fix shape, per pack **dispatch, readout, blocks-signal-theme** are permanently dark by product identity. Each owns a pack-scoped surface/ink SET (`--dispatch-*`, `--readout-*`, `--signal-*`) with internally consistent dark defaults compiled into every block module as inline fallbacks. No rule in these packs reads a house surface or text token for a panel, so no consumer theming can split the pair. The house `surface-solid-dark`/`on-solid-dark` pair was rejected here for a stated reason: it is one flat pure-black surface with a single ink, and these packs need a layered palette. tome-cop drives all three sets so its theming still applies. signal-theme's accents split into three roles — identity fill, lightened on-panel text, and per-hue ink for accent fills — because one value cannot serve both a dark panel and a light article. **longform, content-writer, editorial-pack, marketing-starter, agency-essentials, extras** follow the ambient theme and are fixed with the house vocabulary: correct pairs (`surface`/`on-surface`, `background`/ `foreground`, `primary`/`on-primary`), the new `-text` weights for status copy, and solid ink steps in place of alpha. Painted bands publish their own ink as a local `--_on-band`, and brand/status text reads `var(--_on-band, <its normal one declaration per band with no combinatorial selectors. longform additionally derives `--_accent-ink` by mixing the injected tome-cop accent half-and-half with `--tome-color-foreground`, which keeps the hue while binding legibility to a pair the house guarantees, and inverts by itself in dark mode. Blocks that deliberately paint NOTHING and sit in the prose flow keep the house PAGE pair. Migrating those to pack ink would be the same bug pointing the other way — a near-white ink on a light article. ## A fourth mechanism, found on the second pass: cross-namespace `:root` emission `@wabbit/tome-cop` drives the three packs' surface/ink sets, and it declared those aliases inside its `:root, [data-tome-pack="cop"]` rule. `:root` there is load-bearing for the `--cop-*` namespace and justified in that file on collision-safety grounds — no other package can declare a `--cop-*` property. `--dispatch-*`, `--readout-*` and `--signal-*` are other packages' namespaces, so the argument does not carry, and the consequence was that **importing** tome-cop's stylesheet — without ever setting `[data-tome-pack="cop"]`, which is the documented opt-in — re-themed three packs the site never opted into. Both declarations sit at `:root`, cop loads last, cop wins. Measured on the starter block gallery, a light bone/ink theme: `--dispatch-surface`, `--readout-surface` and `--signal-panel` all computed to `hsl(0 0% 100%)`, identical to the consumer's `--card`, while the packs' on-dark accents kept painting on top — 1.5–1.9:1 across dispatch, readout and signal-theme. The accents were correct as authored; the panel beneath them had been replaced. Every cross-namespace alias in that file — §5.2.3–§5.2.6's `--readout-*` / `--dispatch-*` status aliases, all of §5.2.8, and §5.2.8b's surface/ink sets — now lives in a `[data-tome-pack="cop"]`-only rule. Outside a cop-themed subtree each pack falls back to its own literals, which are contrast-checked against its own surface. This also removes a second failure the first one was masking: cop's zinc `oklch(45% 0.01 0)` for `--readout-objective-pending` and `--readout-personnel-inactive` reads 2.6:1 against readout's own dark panel, where the pack's own `hsl(0 0% 54%)` reads 5.4:1. Scoping rule going forward: a theme pack may emit its OWN namespace at `:root`; anything that re-themes a namespace it does not own goes behind the pack attribute. ## A fifth mechanism, found on the third pass: ink flipped, surface never painted Four hero-shaped blocks flip to light ink the moment a background image is declared — the copy is meant to sit on a photo under a dark scrim — but none of them painted a surface an ancestor of that copy could pair against. extras' **StudyHero** and **CustomHero** (its `cop`/`sitrep` families) painted no surface at all; marketing-starter's **HighImpactHero** painted its plate on the absolutely positioned background LAYER, a sibling of the content rather than an ancestor of it. So the real backdrop under the glyph was the page: measured 1.00–1.06:1, and the same failure reaches any consumer whose asset is absent, transparent, letterboxed, or simply slow to load. A hero added without an image rendered invisible copy. Each now paints the plate on the section itself, defaulting to the theme-relative partner of the ink it already chose — the shape BlogHero, ChapterHero and TypographyHero were already using. It is painted unconditionally rather than behind a `has-image` flag (org-pack's CampaignBanner `data-has-banner` shape) because both states want the same colour: with a photo it is the plate underneath; without one it is the dark band the ink was designed for, so the degraded state is a legible dark hero instead of a blank one. Each band publishes its ink as `--_on-band`, which matters most in HighImpactHero, where the muted tier is a DARK ink chosen for the page and would otherwise be dark-on-dark inside the new plate. marketing-starter's **Faq** had the mirror of this: `.bg-dark` set `color` on the section, but `.headline` / `.intro` / `.question` / `.answer` and the `+`/`−` marker each re-declared their own, and a child declaration beats an inherited one. `.question` was an exact foreground-on-foreground render at 1.00:1. signal-theme's **SignalDataTable** caption is the one piece of text in that block that is NOT inside the painted panel, and it kept `--signal-ink-muted`, a light grey tuned for `--signal-panel` — 2.17:1 on a light article. It now uses the house muted tier, the same rule SignalImageGrid's captions and SignalFootnotes already follow: panel-painted text uses `--signal-*`, prose-flow text uses the house vocabulary that tracks the ambient theme. ## Also fixed: a third icon-name-as-text renderer agency-essentials' **Timeline** rendered `section.icon` as children, painting the authored names (`rocket`, `briefcase`, `globe`, `zap`) as literal text — bone on bone, 1.00:1, on its dark variant — even though the block's own authoring guidance says "use icon names your renderer maps to an icon component". Same house pattern as catalog-pack's CategoryStrip and extras' own icon-bearing blocks: mapped names render an icon at `size="1em"` so the slot's font-size owns sizing, unmapped name-shaped strings render nothing, and an authored emoji still renders as text. Rather than add a third copy of the name→component map, `resolveLucideIcon` is now exported from `@wabbit/tome-blocks-extras/render/shared` — the barrel that already exists for helpers a consuming pack needs, and the package that already owns the `lucide-react` peer. Timeline's marker chip also hardcoded the page background as its fill while its glyph inherits the band ink, so on the dark variant it was a light chip carrying light ink. ## Also fixed, and not a contrast issue dispatch's CommsTranscript rendered redacted lines as the real message text with `color: transparent` under a painted bar. Invisible to sighted readers, still announced by screen readers and still present in the copied DOM — the redacted content leaked to exactly the readers a redaction exists for. The renderers now emit no message text at all for a redacted line. Every reference to a newly added token carries a literal fallback. An undefined custom property makes the declaration invalid and the element inherits its ancestor's colour, which is the 1.0:1 failure mode itself.
  • Updated dependencies [1bebcdc]
  • Updated dependencies [48773ac] - @wabbit/tome-blocks-extras@0.15.11 - @wabbit/tome-ui@0.11.2
v0.15.9patch

71d3b09: Purge client-specific lore and Star Citizen universe references from all non-SC packages (content and labels only — no schema field names, slugs, or enum values changed). - **dispatch**: demo content rewritten as an incident-war-room / ops-bridge scenario (SEV-1 bridge traffic, failover runbooks, recovered security-report transcript) plus neutral original fiction for inherently fictional variants (Relay Station Aurelia personal log, SV Aurelia ship log). Config field-description examples de-lored (old client- and universe-specific labels → neutral equivalents). - **readout**: all 9 blocks' demo props rewritten as business-operations console data (deployment phases, sprint objectives, service status, perimeter traffic, on-call roster, infrastructure asset cards). Config examples de-lored. - **blocks-signal-theme**: demo props for the 33-block pack rewritten as an original search-and-rescue expedition serial ("Operation Long Wake", SV Aurelia, Meridian Reach) with zero client or SC references; config examples de-lored. Pack positioning (SC-tier bundling per OQ-4) unchanged. - **blocks-extras / blocks-content-writer**: Custom Hero and Post Hero meta descriptions stop name-dropping the client; "Callsign" field descriptions neutralized to "Author name or handle"; provenance comments neutralized. - **blocks-core**: BLOCK_CATALOG mirror entries refreshed for custom-hero and post-hero only; registry comment neutralized. - **blocks-gallery**: SourceBadge label for the `vngd` source value now renders "Legacy" (enum value unchanged). - **accounts / core / lms / ui / org / admin / motion / longform / cop / blocks**: internal provenance comments, shipped CSS comments, and consumer-visible field descriptions that named a specific client replaced with neutral "upstream" phrasing; longform package description de-lored. Historical CHANGELOG entries left untouched.

  • 71d3b09: Purge client-specific lore and Star Citizen universe references from all non-SC packages (content and labels only — no schema field names, slugs, or enum values changed). - **dispatch**: demo content rewritten as an incident-war-room / ops-bridge scenario (SEV-1 bridge traffic, failover runbooks, recovered security-report transcript) plus neutral original fiction for inherently fictional variants (Relay Station Aurelia personal log, SV Aurelia ship log). Config field-description examples de-lored (old client- and universe-specific labels → neutral equivalents). - **readout**: all 9 blocks' demo props rewritten as business-operations console data (deployment phases, sprint objectives, service status, perimeter traffic, on-call roster, infrastructure asset cards). Config examples de-lored. - **blocks-signal-theme**: demo props for the 33-block pack rewritten as an original search-and-rescue expedition serial ("Operation Long Wake", SV Aurelia, Meridian Reach) with zero client or SC references; config examples de-lored. Pack positioning (SC-tier bundling per OQ-4) unchanged. - **blocks-extras / blocks-content-writer**: Custom Hero and Post Hero meta descriptions stop name-dropping the client; "Callsign" field descriptions neutralized to "Author name or handle"; provenance comments neutralized. - **blocks-core**: BLOCK_CATALOG mirror entries refreshed for custom-hero and post-hero only; registry comment neutralized. - **blocks-gallery**: SourceBadge label for the `vngd` source value now renders "Legacy" (enum value unchanged). - **accounts / core / lms / ui / org / admin / motion / longform / cop / blocks**: internal provenance comments, shipped CSS comments, and consumer-visible field descriptions that named a specific client replaced with neutral "upstream" phrasing; longform package description de-lored. Historical CHANGELOG entries left untouched.
  • Updated dependencies [71d3b09] - @wabbit/tome-blocks-extras@0.15.9 - @wabbit/tome-blocks-core@0.15.9 - @wabbit/tome-ui@0.11.1
v0.15.4patch

Updated dependencies [0a070e0] - @wabbit/tome-ui@0.11.0

  • Updated dependencies [0a070e0] - @wabbit/tome-ui@0.11.0
v0.15.0minor

510036f: Route pack block links through the `LinkAdapter` instead of raw `<a href>`. `LinkAdapter` (0.14.0) shipped the seam; this connects it. **67 anchors across 55 renderer files in 10 packs** now render through `<TomeLink>`, so a consuming site that registers a link adapter gets its route transition on pack blocks — previously impossible by construction, since a pack cannot import the consumer's transition component and the consumer cannot reach into a pack's render tree. **New in `@wabbit/tome-blocks-core`: `<TomeLink>`**, a component form of `resolveLink()`. `resolveRichText()` / `resolveMedia()` are functions because they turn a data value into content; a link _wraps children_, and the function form forces multi-line JSX through a `children:` prop. `<TomeLink href={…}>…</TomeLink>` is a drop-in for the `<a>` it replaces. It delegates to `resolveLink()`, so there is exactly one resolution path, and it stays directive-free so RSC pack renderers can use it without becoming client components. **`LinkProps.href` is now `string | null | undefined`.** Block data routinely carries an optional URL, and the raw `<a href={undefined}>` these calls replaced was legal markup. Narrowing it to `string` would have forced ~10 non-null assertions across the packs and changed behavior at each. `NOOP_LINK_ADAPTER` normalises null to `undefined` so React omits the attribute — the unregistered path stays byte-identical to the pre-adapter markup. **Behaviour is unchanged for every consumer that has not registered a link adapter**, which is currently all of them: `resolveLink` falls back to a plain `<a>`. Deliberately left as raw `<a>`: - `EditorialFootnotes` — its `#fnref-*` anchors are in-page backlinks. Client-routing them would play a page transition for a jump within the same document. - `PricingPlans` / `PricingPlanCard` — these already accept an injectable anchor component, a more expressive consumer mechanism that predates the adapter. - `LogoSlider` — a self-closing, childless `target="_blank"` overlay anchor. Always external, so the adapter would hand it straight back to the browser. - `@wabbit/tome-longform` — it has zero runtime dependencies and does not peer on `blocks-core`. Adding that edge to the layer graph is its own decision, not a sweep side effect.

  • 510036f: Route pack block links through the `LinkAdapter` instead of raw `<a href>`. `LinkAdapter` (0.14.0) shipped the seam; this connects it. **67 anchors across 55 renderer files in 10 packs** now render through `<TomeLink>`, so a consuming site that registers a link adapter gets its route transition on pack blocks — previously impossible by construction, since a pack cannot import the consumer's transition component and the consumer cannot reach into a pack's render tree. **New in `@wabbit/tome-blocks-core`: `<TomeLink>`**, a component form of `resolveLink()`. `resolveRichText()` / `resolveMedia()` are functions because they turn a data value into content; a link _wraps children_, and the function form forces multi-line JSX through a `children:` prop. `<TomeLink href={…}>…</TomeLink>` is a drop-in for the `<a>` it replaces. It delegates to `resolveLink()`, so there is exactly one resolution path, and it stays directive-free so RSC pack renderers can use it without becoming client components. **`LinkProps.href` is now `string | null | undefined`.** Block data routinely carries an optional URL, and the raw `<a href={undefined}>` these calls replaced was legal markup. Narrowing it to `string` would have forced ~10 non-null assertions across the packs and changed behavior at each. `NOOP_LINK_ADAPTER` normalises null to `undefined` so React omits the attribute — the unregistered path stays byte-identical to the pre-adapter markup. **Behaviour is unchanged for every consumer that has not registered a link adapter**, which is currently all of them: `resolveLink` falls back to a plain `<a>`. Deliberately left as raw `<a>`: - `EditorialFootnotes` — its `#fnref-*` anchors are in-page backlinks. Client-routing them would play a page transition for a jump within the same document. - `PricingPlans` / `PricingPlanCard` — these already accept an injectable anchor component, a more expressive consumer mechanism that predates the adapter. - `LogoSlider` — a self-closing, childless `target="_blank"` overlay anchor. Always external, so the adapter would hand it straight back to the browser. - `@wabbit/tome-longform` — it has zero runtime dependencies and does not peer on `blocks-core`. Adding that edge to the layer graph is its own decision, not a sweep side effect.
  • Updated dependencies [510036f] - @wabbit/tome-blocks-core@0.15.0 - @wabbit/tome-blocks-extras@0.15.0
v0.14.0patch

Updated dependencies - @wabbit/tome-blocks-core@0.14.0 - @wabbit/tome-blocks-extras@0.14.0

  • Updated dependencies - @wabbit/tome-blocks-core@0.14.0 - @wabbit/tome-blocks-extras@0.14.0
v0.13.0patch

Updated dependencies [f4d55c9]

  • Updated dependencies [f4d55c9]
  • Updated dependencies [eb403d4] - @wabbit/tome-blocks-core@0.13.0 - @wabbit/tome-blocks-extras@0.13.0
v0.12.1patch

Updated dependencies - @wabbit/tome-ui@0.10.0

  • Updated dependencies - @wabbit/tome-ui@0.10.0
v0.11.2patch

Updated dependencies [e11d5a2] - @wabbit/tome-blocks-core@0.11.2 - @wabbit/tome-blocks-extras@0.11.2

  • Updated dependencies [e11d5a2] - @wabbit/tome-blocks-core@0.11.2 - @wabbit/tome-blocks-extras@0.11.2
v0.11.0patch

36e537a: Every package now declares an explicit `sideEffects` field (38 added; motion/engine/forms already correct). Registration-bearing modules (render files' `registerRenderer`, `blocks/*/index.ts` `defineBlock` self-registration, widget `register.ts` files, productHooks, permission self-registrations, print templates, chrome built-in variants) are listed so bundlers can tree-shake everything else WITHOUT dropping import-time registrations — previously the field was unset, which blocked cross-module tree-shaking through the barrels entirely. Never blanket `false` on a package with registration or CSS.

  • 36e537a: Every package now declares an explicit `sideEffects` field (38 added; motion/engine/forms already correct). Registration-bearing modules (render files' `registerRenderer`, `blocks/*/index.ts` `defineBlock` self-registration, widget `register.ts` files, productHooks, permission self-registrations, print templates, chrome built-in variants) are listed so bundlers can tree-shake everything else WITHOUT dropping import-time registrations — previously the field was unset, which blocked cross-module tree-shaking through the barrels entirely. Never blanket `false` on a package with registration or CSS.
  • 5f78397: The clientization migration: 127 render components across seven packs dropped `'use client'` — every file individually re-verified hook/handler/context-free before stripping; adapter-consuming static blocks converted to `resolveRichText`/`resolveMedia`. Exactly 20 of 155 renderers remain client, each for a verified reason (state/effects/motion, or a documented client-shell composition contract), enforced by the new `assert:rsc-boundaries` CI script (per-pack manifest; fails loudly if a directive creeps back or a count drifts). Every renderer-bearing pack now exports `./render/register` (`renderers` map + explicit `registerRenderers()`), aggregated by `@wabbit/tome-blocks`'s new `registerAllRenderers()` — the format-safe registration path for server component graphs, where the legacy import-time barrel registration never executes (that legacy path is unchanged and remains supported until the spec's deprecation trigger). `RenderBlock` is rewritten server-safe: directive-free, optional `components` prop (RenderBlocks parity) → registry fallback, dev warn-once naming both fixes on a miss; its docs state the explicit-registration prerequisite. Rendered output is byte-identical everywhere; behavior change only for consumers rendering migrated blocks in RSC WITHOUT a provider or registration — they get the documented warn + graceful degradation instead of silent client bundling.
  • Updated dependencies [26dfa07]
  • Updated dependencies [36e537a]
  • Updated dependencies [36e537a]
  • Updated dependencies [36e537a]
  • Updated dependencies [a93f478]
  • Updated dependencies [5f78397]
  • Updated dependencies [5f78397]
  • Updated dependencies [aef2725]
  • Updated dependencies [aef2725]
  • Updated dependencies [aef2725] - @wabbit/tome-blocks-core@0.11.0 - @wabbit/tome-blocks-extras@0.11.0 - @wabbit/tome-ui@0.9.9
v0.10.2patch

cd59894: Docs/metadata only — no code or block behavior changes. **signal-theme** — reclassified SC-tier under the `@wabbit/tome-sc` umbrella. `tier` stays `'addon'` (no new tier enum value — already the correct value per the blocks family's 0.7.0 tier-formalization release); ownership is expressed via an `'sc'` bundle tag (added alongside the existing `'star-citizen'` tag) and an updated bundle `description`/README section. This package remains independently installable on any editorial site — its non-SC usability is a bonus, not a design constraint. Also fixes a stale test assertion (`test/smoke.test.ts`) still expecting the pre-0.7.0 `tier: 'pro'` value. **sc-pack** — README no longer says "scaffold-only" (stale since the 5 blocks were fully authored in Wave 3): documents the real block inventory (`fleet-summary`, `signal-hero-sc`, `task-force-roster`, `op-briefing-panel`, `rsi-handle-card`), corrects the wrong slug list the old README carried, and notes each block is `_variant`-native with self-contained data (no dependency on `@wabbit/tome-sc`'s collection layer). Fixes two stale test assertions (`test/smoke.test.ts`, `test/v2-coverage.test.ts`) still expecting the pre-0.7.0 `tier: 'pro'` value instead of the shipped `'addon'`.

  • cd59894: Docs/metadata only — no code or block behavior changes. **signal-theme** — reclassified SC-tier under the `@wabbit/tome-sc` umbrella. `tier` stays `'addon'` (no new tier enum value — already the correct value per the blocks family's 0.7.0 tier-formalization release); ownership is expressed via an `'sc'` bundle tag (added alongside the existing `'star-citizen'` tag) and an updated bundle `description`/README section. This package remains independently installable on any editorial site — its non-SC usability is a bonus, not a design constraint. Also fixes a stale test assertion (`test/smoke.test.ts`) still expecting the pre-0.7.0 `tier: 'pro'` value. **sc-pack** — README no longer says "scaffold-only" (stale since the 5 blocks were fully authored in Wave 3): documents the real block inventory (`fleet-summary`, `signal-hero-sc`, `task-force-roster`, `op-briefing-panel`, `rsi-handle-card`), corrects the wrong slug list the old README carried, and notes each block is `_variant`-native with self-contained data (no dependency on `@wabbit/tome-sc`'s collection layer). Fixes two stale test assertions (`test/smoke.test.ts`, `test/v2-coverage.test.ts`) still expecting the pre-0.7.0 `tier: 'pro'` value instead of the shipped `'addon'`.
  • Updated dependencies [ec4b7bc] - @wabbit/tome-ui@0.9.8
v0.10.0patch

Updated dependencies - @wabbit/tome-blocks-core@0.10.0 - @wabbit/tome-blocks-extras@0.10.0

  • Updated dependencies - @wabbit/tome-blocks-core@0.10.0 - @wabbit/tome-blocks-extras@0.10.0
v0.9.5patch

Updated dependencies - @wabbit/tome-blocks-extras@0.9.5

  • Updated dependencies - @wabbit/tome-blocks-extras@0.9.5
v0.9.4patch

bed3f90: Docs-manifest emitter pipeline (W3 ship-readiness). `@wabbit/tome-blocks-core` now ships a standalone Node ESM CLI at `scripts/emit-docs-manifests.mjs` that emits per-package documentation manifests (index.json, packages/<slug>.json, changelog.json) by reading what packages already carry — READMEs, the payload-free `<pkg>/meta` block-usage barrels, package.json exports maps, and CHANGELOG.md. It is the docs-pipeline sibling of the gallery source extractor and is consumed by host sites at prebuild: `node node_modules/@wabbit/tome-blocks-core/scripts/emit-docs-manifests.mjs --output-dir <dir> --scope <scope.json>`. To let the emitter import block metadata uniformly without dragging Payload config into a build script, the `./meta` payload-free subpath (BlockMetaEntry[]) is extended to the remaining offered blocks packs — agency-essentials, catalog-pack, lms-pack, org-pack, and signal-theme — mirroring the existing editorial-pack / marketing-starter / content-writer / extras barrels. Each block's `BlockMeta` was relocated verbatim into a payload-free sibling meta module and re-imported by its block config; no meta values changed. Every supported-core package additionally adds `CHANGELOG.md` to its published `files` array so the next publish cascade ships changelogs the emitter can read from installed tarballs at prebuild.

  • bed3f90: Docs-manifest emitter pipeline (W3 ship-readiness). `@wabbit/tome-blocks-core` now ships a standalone Node ESM CLI at `scripts/emit-docs-manifests.mjs` that emits per-package documentation manifests (index.json, packages/<slug>.json, changelog.json) by reading what packages already carry — READMEs, the payload-free `<pkg>/meta` block-usage barrels, package.json exports maps, and CHANGELOG.md. It is the docs-pipeline sibling of the gallery source extractor and is consumed by host sites at prebuild: `node node_modules/@wabbit/tome-blocks-core/scripts/emit-docs-manifests.mjs --output-dir <dir> --scope <scope.json>`. To let the emitter import block metadata uniformly without dragging Payload config into a build script, the `./meta` payload-free subpath (BlockMetaEntry[]) is extended to the remaining offered blocks packs — agency-essentials, catalog-pack, lms-pack, org-pack, and signal-theme — mirroring the existing editorial-pack / marketing-starter / content-writer / extras barrels. Each block's `BlockMeta` was relocated verbatim into a payload-free sibling meta module and re-imported by its block config; no meta values changed. Every supported-core package additionally adds `CHANGELOG.md` to its published `files` array so the next publish cascade ships changelogs the emitter can read from installed tarballs at prebuild.
  • Updated dependencies [bed3f90] - @wabbit/tome-blocks-core@0.9.4 - @wabbit/tome-blocks-extras@0.9.4 - @wabbit/tome-ui@0.9.7
v0.9.3patch

Updated dependencies - @wabbit/tome-ui@0.9.6

  • Updated dependencies - @wabbit/tome-ui@0.9.6
v0.9.2patch

D3 convergence: all five signal blocks (data-table, metric-grid, image-grid, stat-strip, stats) resolve width via the canonical `resolveBreakout()` from `@wabbit/tome-ui`; local per-block `.breakout-*` CSS strategies removed; stat-strip/stats default `article`→`content`.

  • D3 convergence: all five signal blocks (data-table, metric-grid, image-grid, stat-strip, stats) resolve width via the canonical `resolveBreakout()` from `@wabbit/tome-ui`; local per-block `.breakout-*` CSS strategies removed; stat-strip/stats default `article`→`content`.
  • Updated dependencies
  • Updated dependencies - @wabbit/tome-blocks-core@0.9.2 - @wabbit/tome-ui@0.9.5 - @wabbit/tome-blocks-extras@0.9.2
v0.9.1patch

Updated dependencies [c07f3c8] - @wabbit/tome-blocks-extras@0.9.1

  • Updated dependencies [c07f3c8] - @wabbit/tome-blocks-extras@0.9.1
v0.9.0minor

c7d0afc: layoutGrid nesting capability audit + render hardening (Wave 2). Tags the `nestable` capability flag (and a `minColSpan` floor for internally-wide blocks) across the offered blocks in the core packs (extras, marketing, content, agency, editorial, signal), so the `layoutGrid` container's derived child allowlist — `blockRegistry.getNestableBlocks()` — is populated rather than empty. Excluded by design (left non-nestable): containers (`editorialSpread`, `editorialSection`, `split-view`, `stacking-wrapper`, `grid`, `layoutGrid`) to enforce the one-level depth cap; full-bleed heroes/banners (band-owners); and inline Lexical blocks (not block-level grid children). `minColSpan: 2` is set on the internally multi-column blocks (`card-grid`, `bento-section`, `content-two-column`, `signal-stats`, `signal-image-grid`, `signal-data-table`) so they cannot be crammed into a single-column cell. Also hardens `LayoutGrid`'s child↔span pairing: children are now flattened with null slots preserved (instead of `React.Children.toArray`, which drops nulls), so an unrenderable child can no longer shift every later child's span metadata onto the wrong block. Adds a dev-mode warning when the consumer's child count doesn't match the item count. `@wabbit/tome-blocks-core` is bumped to release the layoutGrid platform primitives merged earlier but never published (the registry's `0.8.0` predates that merge): `BlockMeta.nestable`/`minColSpan`, `withChildPlacement`, the reserved `_colSpan`/`_rowSpan`/`_order` field constants, and `blockRegistry.getNestableBlocks()`. Without this, a consumer wiring the layoutGrid allowlist would call a `getNestableBlocks` that its installed `blocks-core@0.8.0` does not have. Domain packs (lms, catalog, sc, org) are intentionally deferred — they compose into their own domain layouts and can opt in when a consumer needs them.

  • c7d0afc: layoutGrid nesting capability audit + render hardening (Wave 2). Tags the `nestable` capability flag (and a `minColSpan` floor for internally-wide blocks) across the offered blocks in the core packs (extras, marketing, content, agency, editorial, signal), so the `layoutGrid` container's derived child allowlist — `blockRegistry.getNestableBlocks()` — is populated rather than empty. Excluded by design (left non-nestable): containers (`editorialSpread`, `editorialSection`, `split-view`, `stacking-wrapper`, `grid`, `layoutGrid`) to enforce the one-level depth cap; full-bleed heroes/banners (band-owners); and inline Lexical blocks (not block-level grid children). `minColSpan: 2` is set on the internally multi-column blocks (`card-grid`, `bento-section`, `content-two-column`, `signal-stats`, `signal-image-grid`, `signal-data-table`) so they cannot be crammed into a single-column cell. Also hardens `LayoutGrid`'s child↔span pairing: children are now flattened with null slots preserved (instead of `React.Children.toArray`, which drops nulls), so an unrenderable child can no longer shift every later child's span metadata onto the wrong block. Adds a dev-mode warning when the consumer's child count doesn't match the item count. `@wabbit/tome-blocks-core` is bumped to release the layoutGrid platform primitives merged earlier but never published (the registry's `0.8.0` predates that merge): `BlockMeta.nestable`/`minColSpan`, `withChildPlacement`, the reserved `_colSpan`/`_rowSpan`/`_order` field constants, and `blockRegistry.getNestableBlocks()`. Without this, a consumer wiring the layoutGrid allowlist would call a `getNestableBlocks` that its installed `blocks-core@0.8.0` does not have. Domain packs (lms, catalog, sc, org) are intentionally deferred — they compose into their own domain layouts and can opt in when a consumer needs them.
  • Updated dependencies [c7d0afc] - @wabbit/tome-blocks-core@0.9.0 - @wabbit/tome-blocks-extras@0.9.0
v0.8.0minor

249b670: Batch 5 — signal-theme consolidation (2026-06-27 inserter/variant architecture). The signal pack is the largest collapse: ~33 blocks, almost all encoding their variant space in an inline `designVersion` select. This batch lifts that into the real `_variant` axis (designVersion deprecated, Decision 1) and merges thin themed wrappers into 6 variant parents — **16 source blocks → 6 parents** (offered set ~33 → ~23). - **New `signal-banner`** — merges `signal-classification-banner` + `signal-system-alert` into one block with 7 `_variant`s (declassified default / restricted / secret / hull-breach / quantum / comms / all-clear). - **New `signal-note`** — merges `signal-callout` + `signal-aside` + `signal-author-aside` + `signal-epigraph` (the headline "thin themed wrappers") into one block with 12 `_variant`s (tactical default / info / lore / log / aside-_ / author-_ / epigraph-\*). Also reconciles `signal-callout`'s dual-mechanism drift (it carried both an inline `designVersion` AND a `_variant` array — both folded into the single `_variant`, lossless). - **New `signal-stats`** ← key-facts + metric-grid + stat-strip (9 `_variant`s). **New `signal-comms`** ← comm-intercept + comms-transcript (6). **New `signal-divider`** ← phase-marker + chapter-divider + anchor-section (9). **New `signal-nav`** ← cross-link + series-nav (5). - Every parent maps `_variant` → the legacy `designVersion` value and dispatches to the existing render components, so visuals are preserved exactly; the Batch-0 `{variant→component}` registry is populated. Where two sources' render keyed on different field names (e.g. `title`/`heading`, `title`/`seriesTitle`), the render re-projects the shared field. Authored usage/intent on all six. - **Deprecated** the 16 source blocks (still registered + rendered for back-compat via the pack's new DEPRECATED_BLOCKS set, removed from the offered bundle). Instance migration → parent + `_variant` is deferred to a later release (a live migration run). - **Kept distinct** (genuinely different shapes): data-table, progress-bar, objective-list, map-legend, personnel-card, ship-card, log-header, image-grid, footnotes, ambient-audio, sensor-readout, threat-panel, accordion, tabbed-content. - **`signal-drop-cap` / `signal-spoiler` / `signal-redacted`** are slated for a SEPARATE workstream — re-modeling as Lexical inline marks, not blocks — so they remain offered for now. Ships in the linked family's 0.8.0 minor.

  • 249b670: Batch 5 — signal-theme consolidation (2026-06-27 inserter/variant architecture). The signal pack is the largest collapse: ~33 blocks, almost all encoding their variant space in an inline `designVersion` select. This batch lifts that into the real `_variant` axis (designVersion deprecated, Decision 1) and merges thin themed wrappers into 6 variant parents — **16 source blocks → 6 parents** (offered set ~33 → ~23). - **New `signal-banner`** — merges `signal-classification-banner` + `signal-system-alert` into one block with 7 `_variant`s (declassified default / restricted / secret / hull-breach / quantum / comms / all-clear). - **New `signal-note`** — merges `signal-callout` + `signal-aside` + `signal-author-aside` + `signal-epigraph` (the headline "thin themed wrappers") into one block with 12 `_variant`s (tactical default / info / lore / log / aside-_ / author-_ / epigraph-\*). Also reconciles `signal-callout`'s dual-mechanism drift (it carried both an inline `designVersion` AND a `_variant` array — both folded into the single `_variant`, lossless). - **New `signal-stats`** ← key-facts + metric-grid + stat-strip (9 `_variant`s). **New `signal-comms`** ← comm-intercept + comms-transcript (6). **New `signal-divider`** ← phase-marker + chapter-divider + anchor-section (9). **New `signal-nav`** ← cross-link + series-nav (5). - Every parent maps `_variant` → the legacy `designVersion` value and dispatches to the existing render components, so visuals are preserved exactly; the Batch-0 `{variant→component}` registry is populated. Where two sources' render keyed on different field names (e.g. `title`/`heading`, `title`/`seriesTitle`), the render re-projects the shared field. Authored usage/intent on all six. - **Deprecated** the 16 source blocks (still registered + rendered for back-compat via the pack's new DEPRECATED_BLOCKS set, removed from the offered bundle). Instance migration → parent + `_variant` is deferred to a later release (a live migration run). - **Kept distinct** (genuinely different shapes): data-table, progress-bar, objective-list, map-legend, personnel-card, ship-card, log-header, image-grid, footnotes, ambient-audio, sensor-readout, threat-panel, accordion, tabbed-content. - **`signal-drop-cap` / `signal-spoiler` / `signal-redacted`** are slated for a SEPARATE workstream — re-modeling as Lexical inline marks, not blocks — so they remain offered for now. Ships in the linked family's 0.8.0 minor.
  • Updated dependencies [249b670]
  • Updated dependencies [249b670]
  • Updated dependencies [249b670]
  • Updated dependencies [249b670] - @wabbit/tome-blocks-extras@0.8.0 - @wabbit/tome-blocks-core@0.8.0
v0.7.0patch

66c611c: B1 — block tiering formalization + blocks-extras free/paid split. - **blocks-core:** add `'addon'` to the `BundleMeta.tier` union (additive; existing `'pro'` values stay valid). - **blocks-extras:** register TWO bundles from one package (Option B) — a free `extras-primitives` sub-bundle (9 structural primitives) + the paid `extras` remainder (39 blocks, was 48). Mechanically additive: no import-path changes, all blocks still exported + registered, existing content keeps rendering, existing `extras` entitlements keep working (the primitives are now free to everyone). The tier-scope change is the only semantic shift. - **signal-theme + sc-pack:** tier `'pro'` → `'addon'` (sold independently of the tiered subscription track). - **blocks-gallery:** widen the `@wabbit/tome-blocks-core` peer to `^0.5.9 || ^0.6.0 || ^0.7.0` so the 0.7.0 bump doesn't force a spurious major (it's a types-only peer). Patch. Release note: the blocks family is `linked`, so this aligns the whole family to **0.7.0**. Minor (not major) is deliberate — 0.7.0 still gates explicit consumer adoption (`^0.6` does not auto-resolve 0.7.0), without declaring a symbolic 1.0.0 before the marketplace launch.

  • 66c611c: B1 — block tiering formalization + blocks-extras free/paid split. - **blocks-core:** add `'addon'` to the `BundleMeta.tier` union (additive; existing `'pro'` values stay valid). - **blocks-extras:** register TWO bundles from one package (Option B) — a free `extras-primitives` sub-bundle (9 structural primitives) + the paid `extras` remainder (39 blocks, was 48). Mechanically additive: no import-path changes, all blocks still exported + registered, existing content keeps rendering, existing `extras` entitlements keep working (the primitives are now free to everyone). The tier-scope change is the only semantic shift. - **signal-theme + sc-pack:** tier `'pro'` → `'addon'` (sold independently of the tiered subscription track). - **blocks-gallery:** widen the `@wabbit/tome-blocks-core` peer to `^0.5.9 || ^0.6.0 || ^0.7.0` so the 0.7.0 bump doesn't force a spurious major (it's a types-only peer). Patch. Release note: the blocks family is `linked`, so this aligns the whole family to **0.7.0**. Minor (not major) is deliberate — 0.7.0 still gates explicit consumer adoption (`^0.6` does not auto-resolve 0.7.0), without declaring a symbolic 1.0.0 before the marketplace launch.
  • Updated dependencies [28802fa]
  • Updated dependencies [66c611c]
  • Updated dependencies [8958d41] - @wabbit/tome-blocks-extras@0.7.0 - @wabbit/tome-blocks-core@0.7.0
v0.6.2patch

Updated dependencies [4b2f368] - @wabbit/tome-blocks-core@0.6.2 - @wabbit/tome-blocks-extras@0.6.2

  • Updated dependencies [4b2f368] - @wabbit/tome-blocks-core@0.6.2 - @wabbit/tome-blocks-extras@0.6.2
v0.6.1patch

f37fa00: feat(demo): add getDemoProps dispatchers to agency-essentials, marketing-starter, and signal-theme Each pack now ships a `demo.ts` barrel with a `getDemoProps(blockSlug, variant, ctx?)` dispatcher and per-block demo functions. The auto-gallery route in tome-starter can replace the `noopDemoProps` stub for all three bundles, eliminating the warning cards that appeared for every block × variant. - agency-essentials: 10 blocks × 4 variants each (about, contact, team-roster, gallery, timeline, stat, stat-bar, split-view, media, form) - marketing-starter: 8 blocks × 4-5 variants each (high-impact-hero, feature-hero, cta, logo-slider, pricing, testimonial, faq, banner) - signal-theme: 33 blocks (all signal-\* slugs); blocks with multi-variant configs covered (accordion: stacked/single, callout: tactical/lore) - DemoContext interface, individual block-level functions, and getDemoProps all re-exported from each pack barrel - richText fields intentionally omitted — GalleryRichTextProvider supplies the Lexical state at gallery render time - Relationship fields (pricing, testimonial) emit sentinel strings; gallery degrades gracefully Fixes risk R1 from the gallery gap audit. - @wabbit/tome-blocks-core@0.5.9

  • f37fa00: feat(demo): add getDemoProps dispatchers to agency-essentials, marketing-starter, and signal-theme Each pack now ships a `demo.ts` barrel with a `getDemoProps(blockSlug, variant, ctx?)` dispatcher and per-block demo functions. The auto-gallery route in tome-starter can replace the `noopDemoProps` stub for all three bundles, eliminating the warning cards that appeared for every block × variant. - agency-essentials: 10 blocks × 4 variants each (about, contact, team-roster, gallery, timeline, stat, stat-bar, split-view, media, form) - marketing-starter: 8 blocks × 4-5 variants each (high-impact-hero, feature-hero, cta, logo-slider, pricing, testimonial, faq, banner) - signal-theme: 33 blocks (all signal-\* slugs); blocks with multi-variant configs covered (accordion: stacked/single, callout: tactical/lore) - DemoContext interface, individual block-level functions, and getDemoProps all re-exported from each pack barrel - richText fields intentionally omitted — GalleryRichTextProvider supplies the Lexical state at gallery render time - Relationship fields (pricing, testimonial) emit sentinel strings; gallery degrades gracefully Fixes risk R1 from the gallery gap audit. - @wabbit/tome-blocks-core@0.5.9
v0.5.9patch

@wabbit/tome-blocks-core@0.5.9

  • @wabbit/tome-blocks-core@0.5.9
  • @wabbit/tome-blocks-extras@0.5.9
v0.5.7patch

@wabbit/tome-blocks-core@0.5.7

  • @wabbit/tome-blocks-core@0.5.7
  • @wabbit/tome-blocks-extras@0.5.7
v0.5.0minor

Linked cohort version alignment (no functional change in this package).

  • Linked cohort version alignment (no functional change in this package).
v0.4.2patch

Updated dependencies - @wabbit/tome-blocks-extras@0.4.2 - @wabbit/tome-blocks-core@0.4.2

  • Updated dependencies - @wabbit/tome-blocks-extras@0.4.2 - @wabbit/tome-blocks-core@0.4.2
v0.4.1patch

Updated dependencies - @wabbit/tome-blocks-extras@0.4.1 - @wabbit/tome-blocks-core@0.4.0

  • Updated dependencies - @wabbit/tome-blocks-extras@0.4.1 - @wabbit/tome-blocks-core@0.4.0
v0.4.0patch

Updated dependencies [b76f684]

  • Updated dependencies [b76f684]
  • Updated dependencies [90a694d] - @wabbit/tome-blocks-core@0.4.0 - @wabbit/tome-blocks-extras@0.4.0
v0.3.0minor

f2202cd: Sprint 3 blocks split — v0.2.0 Extracted the Tome blocks monolith (@wabbit/tome-blocks) into independently publishable bundle packages. Each bundle is independently installable, tree-shakeable, and testable in isolation. **New packages (all v0.2.0):** - `@wabbit/tome-blocks-core` — registries, defineBlock/defineBundle, variants, thumbnails, admin components (BlockPicker, VariantPicker) - `@wabbit/tome-blocks-marketing-starter` (free) — 8 marketing blocks: Hero, FeatureHero, CTA, LogoSlider, Pricing, Testimonial, FAQ, Banner - `@wabbit/tome-blocks-content-writer` (free) — 10 editorial blocks: Blog, Archive, PostHero, RelatedPosts, EditorialOpener, EditorialBridge, EditorialSidenote, EditorialFigure, EditorialColophon, EditorialFootnotes - `@wabbit/tome-blocks-agency-essentials` (starter) — 10 agency blocks: About, Contact/Form, Gallery, Media, SplitView, Stat, StatBar, TeamRoster, Timeline - `@wabbit/tome-blocks-editorial-pack` (pro) — 8 editorial blocks: DataHero, Feature, InfoPanel, MessagePanel, MetricStrip, SplitPanel, StatusBoard, TextReveal - `@wabbit/tome-blocks-signal-theme` (pro) — 33 Signal narrative blocks - `@wabbit/tome-blocks-lms-pack` (pro) — scaffold for LMS blocks (Wave 3) - `@wabbit/tome-blocks-catalog-pack` (pro) — scaffold for catalog/ecommerce blocks (Wave 3) - `@wabbit/tome-blocks-sc-pack` (niche) — scaffold for Star Citizen blocks (Wave 3) - `@wabbit/tome-blocks-extras` (pro) — 47 residual blocks (heroes, layout, content, marketing) **@wabbit/tome-blocks is now a meta-package** that re-exports all bundle packages and provides `registerAll(blockRegistry, bundleRegistry)` as a convenience function. **Render colocation:** All 113 render `.tsx` components migrated from the monolith into their owning bundle packages (`./render` subpath on each bundle). **Breaking changes (internal):** - `@wabbit/tome-blocks/blocks` and `@wabbit/tome-blocks/bundles` subpaths removed (were Sprint 2 shims) - `packages/blocks/src/render/` category index files removed (replaced by per-bundle `./render` subpaths) **Migration:** ```ts // Before (monolith singleton, all blocks loaded) import "@wabbit/tome-blocks"; // After (explicit registration, tree-shakeable) import { blockRegistry, bundleRegistry, } from "@wabbit/tome-blocks-core/registry"; import { register } from "@wabbit/tome-blocks-marketing-starter"; register(blockRegistry, bundleRegistry); // Or use the meta-package convenience function import { blockRegistry, bundleRegistry, registerAll, } from "@wabbit/tome-blocks"; registerAll(blockRegistry, bundleRegistry); ```

  • f2202cd: Sprint 3 blocks split — v0.2.0 Extracted the Tome blocks monolith (@wabbit/tome-blocks) into independently publishable bundle packages. Each bundle is independently installable, tree-shakeable, and testable in isolation. **New packages (all v0.2.0):** - `@wabbit/tome-blocks-core` — registries, defineBlock/defineBundle, variants, thumbnails, admin components (BlockPicker, VariantPicker) - `@wabbit/tome-blocks-marketing-starter` (free) — 8 marketing blocks: Hero, FeatureHero, CTA, LogoSlider, Pricing, Testimonial, FAQ, Banner - `@wabbit/tome-blocks-content-writer` (free) — 10 editorial blocks: Blog, Archive, PostHero, RelatedPosts, EditorialOpener, EditorialBridge, EditorialSidenote, EditorialFigure, EditorialColophon, EditorialFootnotes - `@wabbit/tome-blocks-agency-essentials` (starter) — 10 agency blocks: About, Contact/Form, Gallery, Media, SplitView, Stat, StatBar, TeamRoster, Timeline - `@wabbit/tome-blocks-editorial-pack` (pro) — 8 editorial blocks: DataHero, Feature, InfoPanel, MessagePanel, MetricStrip, SplitPanel, StatusBoard, TextReveal - `@wabbit/tome-blocks-signal-theme` (pro) — 33 Signal narrative blocks - `@wabbit/tome-blocks-lms-pack` (pro) — scaffold for LMS blocks (Wave 3) - `@wabbit/tome-blocks-catalog-pack` (pro) — scaffold for catalog/ecommerce blocks (Wave 3) - `@wabbit/tome-blocks-sc-pack` (niche) — scaffold for Star Citizen blocks (Wave 3) - `@wabbit/tome-blocks-extras` (pro) — 47 residual blocks (heroes, layout, content, marketing) **@wabbit/tome-blocks is now a meta-package** that re-exports all bundle packages and provides `registerAll(blockRegistry, bundleRegistry)` as a convenience function. **Render colocation:** All 113 render `.tsx` components migrated from the monolith into their owning bundle packages (`./render` subpath on each bundle). **Breaking changes (internal):** - `@wabbit/tome-blocks/blocks` and `@wabbit/tome-blocks/bundles` subpaths removed (were Sprint 2 shims) - `packages/blocks/src/render/` category index files removed (replaced by per-bundle `./render` subpaths) **Migration:** ```ts // Before (monolith singleton, all blocks loaded) import "@wabbit/tome-blocks"; // After (explicit registration, tree-shakeable) import { blockRegistry, bundleRegistry, } from "@wabbit/tome-blocks-core/registry"; import { register } from "@wabbit/tome-blocks-marketing-starter"; register(blockRegistry, bundleRegistry); // Or use the meta-package convenience function import { blockRegistry, bundleRegistry, registerAll, } from "@wabbit/tome-blocks"; registerAll(blockRegistry, bundleRegistry); ```
  • Updated dependencies [f2202cd] - @wabbit/tome-blocks-core@0.3.0