Documentation menu

Documentation / Blocks & themes / blocks-signal-theme

Blocks Signal Theme

Package
@wabbit/tome-blocks-signal-theme
Version
v0.49.4
npm install @wabbit/tome-blocks-signal-theme
First install from our registry?
  1. Get a registry token from your credentials page. You need a purchase that includes this package, or a Craft Library membership.

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

    @wabbit:registry=https://npm.wabbit.com/
    //npm.wabbit.com/:_authToken=YOUR_TOKEN
Blocks & themesStable

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

Overview

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

39 Signal-theme editorial blocks are registered: 23 offered (in the bundle descriptor, so the gallery and inserter present them) and 16 deprecated pre-consolidation slugs that stay registered so existing content keeps editing and rendering, but are no longer offered. The split is OFFERED_BLOCKS / DEPRECATED_BLOCKS in src/index.ts. Each deprecated slug keeps its field schema, and fifteen of them render as aliases of their successor's renderer (see the note under the block list).

Peer dependencies

@wabbit/tome-blocks-core and @wabbit/tome-blocks-extras are required peers — npm 7+ and pnpm install them automatically. @wabbit/tome-ui and @wabbit/tome-blocks-console are regular dependencies of this pack (not peers) and install automatically too.

If your site already uses the @wabbit/tome-blocks meta-package, you have this bundle: its registerAll() calls this pack's register() (all 39 blocks), and its registerAllRenderers() calls this pack's registerRenderers(). The meta barrel does not re-export the six consolidation-parent descriptors (signalBannerBlock and friends), so import those from this package directly.

SC-tier positioning

Signal was always SC-flavored — several blocks carry Star-Citizen-specific semantics (signal-ship-card holds ship name, class, manufacturer, image and a spec list, with an optional comparison ship; signal-personnel-card holds a roster of name/callsign/role/avatar/status entries; comm-intercept, classification-banner and threat-panel are SC operational-intel concepts). Rather than splitting those blocks into @wabbit/tome-blocks-sc-pack, signal-theme ships as a dependency of `@wabbit/tome-sc` as its Signal editorial pack, alongside @wabbit/tome-blocks-sc-pack and @wabbit/tome-cop. 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 include 'sc' alongside 'star-citizen' (matching blocks-sc-pack's tag set).
  • This package remains independently installable on any editorial site, SC or not. The generic editorial DNA without SC-specific semantics went into @wabbit/tome-longform, dispatch and readout instead.
  • Installing @wabbit/tome-sc installs this package (it is a regular dependency there), but it does not wire the blocks for you. createSCPlatform accepts a blocks.signalTheme flag (default true) but returns only { collections }; adding the block descriptors to a blocks field and the renderers to your render path is a consumer-side step, exactly as in the Quickstart below.

Block list

Payload slug is the blockType Payload stores and the key in the renderers map. For the six consolidation parents it is camelCase and differs from the kebab-case meta.slug (the registry, gallery, @wabbit/tome-blocks-signal-theme/meta and getDemoProps key); for every other block the two are the same.

SlugPayload slugNameStatusVariantsNotes
signal-bannersignalBannerSignal Banneroffereddeclassified, restricted, secret, hull-breach, quantum, comms, all-clearConsolidation parent — full-width status banner: classification level or ship system alert. Supersedes signal-classification-banner, signal-system-alert
signal-commssignalCommsSignal Commsofferedclean, degraded, encrypted, transcript-standard, transcript-intercepted, transcript-literaryConsolidation parent — intercepted-communications or multi-speaker comms transcript. Supersedes signal-comm-intercept, signal-comms-transcript
signal-dividersignalDividerSignal Dividerofferedphase-standard, phase-colored, phase-compact, chapter-ornamental, chapter-numbered, chapter-symbol, anchor-standard, anchor-colored, anchor-compactConsolidation parent — numbered phase marker, decorative chapter divider, or named anchor-section header. Supersedes signal-phase-marker, signal-chapter-divider, signal-anchor-section
signal-navsignalNavSignal Navofferedcrosslink-standard, crosslink-enhanced, series-banner, series-card, series-sidebarConsolidation parent — cross-system link card (optional stat metrics) or multi-part series navigator. Supersedes signal-cross-link, signal-series-nav
signal-notesignalNoteSignal Noteofferedtactical, info, lore, log, aside-right, aside-left, aside-inline, author-indented, author-overlay, epigraph-centered, epigraph-border, epigraph-verseConsolidation parent — tactical/info/lore/log callout, floating or inline aside, author/editor note, or epigraph. Supersedes signal-callout, signal-aside, signal-author-aside, signal-epigraph
signal-statssignalStatsSignal Statsofferedfacts-sidebar, facts-strip, facts-compact, metric-4col, metric-3col, metric-2col, stat-standard, stat-breakout, stat-compactConsolidation parent — key-facts panel, metric grid, or horizontal stat strip. Supersedes signal-key-facts, signal-metric-grid, signal-stat-strip
signal-accordionsameSignal Accordionoffereddefault, singleExpandable <details> panels. single forces one-open-at-a-time; default defers to the in-block designVersion select (STACKED independent, SINGLE, or FAQ). Client component
signal-ambient-audiosameSignal Ambient Audiooffered—Background audio toggle
signal-data-tablesameSignal Data Tableoffered—Structured data table
signal-drop-capsameSignal Drop Capoffered—Large decorative drop-cap opener
signal-footnotessameSignal Footnotesoffered—Collected footnotes with back-links
signal-image-gridsameSignal Image Gridoffered—Multi-image grid
signal-log-headersameSignal Log Headeroffered—Mission/operation log header
signal-map-legendsameSignal Map Legendoffered—Map key/legend panel
signal-objective-listsameSignal Objective Listoffered—Mission objective checklist
signal-personnel-cardsameSignal Personnel Cardoffered—Personnel/crew roster card
signal-progress-barsameSignal Progress Baroffered—Progress indicator
signal-redactedsameSignal Redactedoffered—Redacted/censored text
signal-sensor-readoutsameSignal Sensor Readoutoffered—Sensor data display
signal-ship-cardsameSignal Ship Cardoffered—Ship specification card
signal-spoilersameSignal Spoileroffered—Collapsible spoiler reveal. Client component
signal-tabbed-contentsameSignal Tabbed Contentoffered—Tabbed panel layout. Client component
signal-threat-panelsameSignal Threat Paneloffered—Threat assessment panel
signal-anchor-sectionsameSignal Anchor Sectiondeprecated → signal-divider—Named section anchor for in-page navigation
signal-asidesameSignal Asidedeprecated → signal-note—Inline aside annotation
signal-author-asidesameSignal Author Asidedeprecated → signal-note—Author/editor note card
signal-calloutsameSignal Calloutdeprecated → signal-notedefault, loreHighlighted callout box
signal-chapter-dividersameSignal Chapter Dividerdeprecated → signal-divider—Visual chapter break
signal-classification-bannersameSignal Classification Bannerdeprecated → signal-banner—Document classification header
signal-comm-interceptsameSignal Comm Interceptdeprecated → signal-comms—Intercepted transmission UI
signal-comms-transcriptsameSignal Comms Transcriptdeprecated → signal-comms—Communication log transcript
signal-cross-linksameSignal Cross-System Linkdeprecated → signal-nav—Cross-document reference link
signal-epigraphsameSignal Epigraphdeprecated → signal-note—Introductory quote
signal-key-factssameSignal Key Factsdeprecated → signal-stats—Key-fact summary panel
signal-metric-gridsameSignal Metric Griddeprecated → signal-stats—Grid of metrics
signal-phase-markersameSignal Phase Markerdeprecated → signal-divider—Phase progression marker
signal-series-navsameSignal Series Navdeprecated → signal-nav—Series navigation links
signal-stat-stripsameSignal Stat Stripdeprecated → signal-stats—Horizontal stat row
signal-system-alertsameSignal System Alertdeprecated → signal-banner—System/status alert banner

Deprecation is expressed through the bundle split, not a JSDoc @deprecated tag; the descriptors are still exported. Moving existing instances to the successor block plus its _variant is a manual content migration.

One implementation per look. Each consolidation parent dispatches to one renderer per lane (for signal-banner: SignalClassificationBanner for the classification variants, SignalSystemAlert for the alert variants), and that renderer is the only implementation of its look. Fifteen deprecated slugs are registered by their parent's module as aliases of that lane: signal-classification-banner and signal-system-alert in SignalBanner; signal-comm-intercept and signal-comms-transcript in SignalComms; signal-phase-marker, signal-chapter-divider and signal-anchor-section in SignalDivider; signal-cross-link and signal-series-nav in SignalNav; signal-aside, signal-author-aside and signal-epigraph in SignalNote; signal-key-facts, signal-metric-grid and signal-stat-strip in SignalStats. An alias renders the stored block unchanged: its designVersion is already the value the parent maps the matching _variant onto (a signal-system-alert set to HULL_BREACH renders exactly as signal-banner set to hull-breach), and an unset or unrecognised value renders as it always has. The renderers map keys these slugs to the aliases (SignalClassificationBannerAlias and so on, exported from the parent modules but not from @wabbit/tome-blocks-signal-theme/render). signal-callout keeps its own registration: it writes its own _variant (default or lore) onto its root as data-variant, while signal-note's callout variants write theirs (tactical, info, lore, log), so the two outputs differ and folding the slug would change one of them. The lane components (SignalClassificationBanner and the rest) stay exported from @wabbit/tome-blocks-signal-theme/render with the same props.

Only signal-accordion and signal-callout ship SVG gallery thumbnails; the other blocks ship none.

Peer dependencies

PeerRangeRequired
@payloadcms/richtext-lexical>=3.67.0yes
payload>=3.67.0yes
react>=19.0.0yes
react-dom>=19.0.0yes
@wabbit/tome-blocks-core>=0.22.0 <1.0.0yes
@wabbit/tome-blocks-extras>=0.16.3 <1.0.0yes

@wabbit/tome-blocks-extras is a peer because three renderers (SignalAccordion, SignalSpoiler, SignalTabbedContent) read rich text through its @wabbit/tome-blocks-signal-theme/adapters/richText hook — see the rich-text gotcha below.

Quickstart — add blocks to an existing Payload site

The canonical walkthrough, with the reasons behind each step, is @wabbit/tome-blocks-core's README section Add Tome blocks to an existing Payload project (in @wabbit/tome-blocks-core). For this pack:

  1. Tokens, once in your global CSS. Skip it and the blocks render as bare HTML:
@import '@wabbit/tome-blocks-core/styles.css';
  1. Add descriptors to a blocks field. Each .block(config?) returns a Payload Block; blocks with uploads (signal-ship-card, signal-personnel-card, signal-image-grid) accept { mediaCollection } (default media):
// payload.config.ts
import { signalBannerBlock, signalNoteBlock, signalShipCardBlock } from '@wabbit/tome-blocks-signal-theme'

{
  name: 'layout',
  type: 'blocks',
  blocks: [...existingBlocks, signalBannerBlock.block(), signalNoteBlock.block(), signalShipCardBlock.block({ mediaCollection: 'media' })],
}
  1. Render through blocks-core's RenderBlocks with the tome-ui grid:
// src/blocks/RenderBlocks.tsx
import { renderers as signalRenderers } from '@wabbit/tome-blocks-signal-theme/render/register'
import { adaptRenderersForPayload, RenderBlocks } from '@wabbit/tome-blocks-core/render'
import gridStyles from '@wabbit/tome-ui/grid'

const components = { ...adaptRenderersForPayload(signalRenderers) }

export function Layout({ blocks }: { blocks: LayoutBlock[] }) {
  return <RenderBlocks blocks={blocks} components={components} gridClassName={gridStyles.grid} />
}
  1. Mount a rich-text provider for the three client blocks, from a 'use client' module that wraps your page tree:
'use client'
// src/providers/SignalRichText.tsx
import type { ReactNode } from 'react'
import { RichTextAdapterProvider, DEFAULT_RICH_TEXT_ADAPTER } from '@wabbit/tome-blocks-core/adapters'

export function SignalRichText({ children }: { children: ReactNode }) {
  return <RichTextAdapterProvider value={DEFAULT_RICH_TEXT_ADAPTER}>{children}</RichTextAdapterProvider>
}

Gotchas that bite:

  • Prop shape. Every renderer is React.FC<BlockRenderProps<T>> and destructures { block, className, index }, with the block's fields under block. adaptRenderersForPayload bridges that to the spread-props shape most Payload sites use; RenderBlock from @wabbit/tome-blocks-core calls the nested shape directly.
  • The grid. gridClassName is optional: without it RenderBlocks uses the global grid class from @wabbit/tome-blocks-core/styles.css, so keep that import in place or the blocks will sit flush against the viewport edge.
  • Rich text in the three client blocks. SignalAccordion, SignalSpoiler and SignalTabbedContent call useRichTextAdapter(), which reads only the React context. With no <RichTextAdapterProvider> above them their rich-text bodies render as an empty data-rich-text-stub element — panel labels show, content does not. Step 4 fixes it (or pass your own Lexical adapter as value). The other rich-text blocks (signal-note and its deprecated sources, signal-threat-panel) go through blocks-core's resolveRichText(), which falls back to a built-in Lexical renderer and needs no provider.
  • Populated uploads. signal-ship-card, signal-personnel-card and signal-image-grid need their upload fields populated (query at depth >= 1); an unpopulated ID renders no image.
  • Links. signal-cross-link and signal-series-nav (and the signal-nav variants built on them) render links through blocks-core's TomeLink, so any link adapter registered with registerBlockAdapters applies.
  • Plain Node. Both @wabbit/tome-blocks-signal-theme/render and @wabbit/tome-blocks-signal-theme/render/register import the renderer modules, whose .tome-css stylesheets ship precompiled as JS modules, so they load under plain Node (seed scripts, tests) with no next.config plugin. Keep them out of payload.config.ts so React stays out of your config; the root (.), @wabbit/tome-blocks-signal-theme/meta and @wabbit/tome-blocks-signal-theme/demo import no React render code and are safe anywhere.

Registering into your own registries instead (for example to drive blocks from a registry):

// 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() }],
    },
  ],
})

register() is idempotent and never touches the blocks-core singleton registries. It registers all 39 blocks; the bundle descriptor lists only the 23 offered slugs.

Public API

ExportSubpathDescription
39 block descriptors (signalAccordionBlock, signalBannerBlock, signalShipCardBlock, …, one per row in the block list) + register(blockRegistry, bundleRegistry) + every @wabbit/tome-blocks-signal-theme/demo export.Payload block descriptors, bundle registration, and the demo-props helpers re-exported
One render component per block (SignalAccordion, SignalShipCard, SignalPersonnelCard, …)@wabbit/tome-blocks-signal-theme/renderLegacy barrel: each component registers itself in the blocks-core render registry as an import side effect. Prefer @wabbit/tome-blocks-signal-theme/render/register
renderers map (39 entries, keyed by Payload slug) + registerRenderers()@wabbit/tome-blocks-signal-theme/render/registerExplicit registration: pass renderers to RenderBlocks/RenderBlock's components prop, or call the idempotent registerRenderers() at startup to fill the shared registry for registry-based rendering
getDemoProps(blockSlug, variant, ctx?), one getSignal<Name>DemoProps(variant, ctx?) per block, DemoContext (type: placeholderMediaId?, placeholderHref?)@wabbit/tome-blocks-signal-theme/demoPayload-free sample data for a gallery route. getDemoProps is keyed by kebab-case meta.slug and returns null for slugs it does not own
signalThemeBlockMeta (BlockMetaEntry[]), signalThemeDeprecatedBlockMeta (BlockMetaEntry[]), BlockMetaEntry (type: { meta, variants? })@wabbit/tome-blocks-signal-theme/metaPayload-free metadata for the 23 offered blocks and, separately, the 16 deprecated ones (for a surface that has to describe stored content). Together they equal the registered descriptors, in registration order (pnpm assert:pack-meta-parity checks it); variants is set wherever the descriptor registers them

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. Earlier releases paired --tome-color-surface (the card family) with --tome-color-foreground (the page family); those are independently themable house tokens, so a consumer that set them to different lightnesses — entirely legal — split the pair, and 15 components measured 1.66–2.46:1 on the starter block gallery.

Panel blocks paint an operational dark surface and own both halves of every pair through the --signal-* set, each read with the default below as its var() fallback. No --tome-color-* surface or text token is read for panel text.

TokenDefaultRole
--signal-panelhsl(220 14% 9%)base panel surface
--signal-panel-raisedhsl(220 13% 14%)headers, insets, tracks, nested cards
--signal-linehsl(220 10% 28%)panel chrome
--signal-inkhsl(220 12% 92%)primary text — 15.0:1 on panel, 13.2:1 on raised
--signal-ink-mutedhsl(220 9% 70%)secondary text — 8.4:1 / 7.3:1
--signal-ink-fainthsl(220 8% 56%)reserved metadata tone (5.4:1 / 4.7:1); no shipped block reads it yet

Prose blocks (AmbientAudio, AnchorSection, ChapterDivider, DropCap, Epigraph, Footnotes, ImageGrid, plus the INLINE/BLOCK_PARA variants of Redacted, the label of Spoiler, and the caption under DataTable) paint no panel. They sit in the article and use the house page tokens --tome-color-foreground / --tome-color-on-surface-muted / --tome-color-border. Do not migrate these to `--signal-ink` — a near-white ink on a light article is the same bug pointing the other way. ClassificationBanner is neither: it paints its own --signal-accent fill with --signal-on-accent text.

Accents carry three roles per hue (red, blue, gold, cyan, amber, green, and a neutral ghost) for the same reason one value cannot serve both surfaces. A block sets these three on its own root from its accentColor field, each routed through a per-hue name (next table) with the default as fallback:

TokenUsed for
--signal-accentidentity color — fills, rules, borders, dots; legible mid-tone for prose contexts
--signal-accent-on-panelthe same hue lightened to clear 4.5:1 as TEXT on --signal-panel
--signal-on-accentthe ink that sits ON a --signal-accent fill, chosen per hue

Per-hue accent tokens. Nothing declares these; set one to re-color that hue everywhere it is used.

Hue--signal-accent-<hue>--signal-accent-<hue>-on-panel--signal-on-accent-<hue>
redhsl(0 72% 50%)hsl(0 80% 68%)hsl(0 0% 100%)
bluehsl(215 65% 45%)hsl(215 75% 65%)hsl(0 0% 100%)
goldhsl(45 80% 50%)hsl(45 80% 58%)hsl(220 14% 9%)
cyanhsl(185 70% 45%)hsl(185 70% 55%)hsl(220 14% 9%)
amberhsl(38 80% 50%)hsl(38 80% 58%)hsl(220 14% 9%)
greenhsl(152 55% 40%)hsl(152 55% 52%)hsl(220 14% 9%)
ghosthsl(220 8% 52%)hsl(220 8% 62%)hsl(220 14% 9%)

The -on-panel names also color hue-keyed parts that are not a block accent: the metric-grid card tops, stat-strip values, progress-bar fills, map-legend symbols and cross-link type badges.

Status, redaction, banner and threat tokens. Like every token here they are fallbacks only: no variable is declared at :root or anywhere else, and a block reads the default until a site sets one.

TokenDefaultRole
--signal-status-okhsl(152 55% 52%)complete, active, friendly, success
--signal-status-warnhsl(38 80% 58%)in progress, standby, unknown, warning, monitoring
--signal-status-failhsl(0 80% 68%)failed, hostile, destructive
--signal-status-infohsl(185 70% 55%)informational key-fact status
--signal-status-deployedhsl(215 75% 65%)personnel card deployed status
--signal-status-idle--signal-ink-mutedpending objective, inactive personnel, resolved indicator
--signal-threat-criticalhsl(0 80% 68%)threat panel, CRITICAL border and badge
--signal-threat-elevatedhsl(38 80% 58%)threat panel, ELEVATED
--signal-threat-advisoryhsl(185 70% 55%)threat panel, ADVISORY
--signal-banner-restrictedhsl(45 80% 50%)classification banner RESTRICTED fill
--signal-banner-restricted-inkhsl(0 0% 10%)text on the RESTRICTED fill
--signal-banner-secrethsl(0 72% 50%)classification banner SECRET fill
--signal-banner-secret-inkhsl(0 0% 100%)text on the SECRET fill
--signal-banner-declassifiedhsl(152 55% 52%)DECLASSIFIED text, and its 12% tint over the panel
--signal-banner-breachhsl(0 80% 68%)system alert HULL_BREACH
--signal-banner-quantumhsl(215 75% 65%)system alert QUANTUM
--signal-banner-commshsl(38 80% 58%)system alert COMMS
--signal-banner-clearhsl(152 55% 52%)system alert ALL_CLEAR
--signal-redact-classifiedhsl(0 72% 42%)redaction level, prose-flow ramp (on a light article)
--signal-redact-restrictedhsl(38 90% 32%)prose-flow ramp
--signal-redact-redactedhsl(220 8% 38%)prose-flow ramp
--signal-redact-expungedhsl(220 10% 22%)prose-flow ramp
--signal-redact-inkhsl(0 0% 100%)label ink on the prose-flow ramp
--signal-redact-classified-on-panelhsl(0 80% 68%)redaction level, on-panel ramp (BLOCK_PANEL, IMAGE)
--signal-redact-restricted-on-panelhsl(38 80% 58%)on-panel ramp
--signal-redact-redacted-on-panel--signal-ink-mutedon-panel ramp
--signal-redact-expunged-on-panel--signal-inkon-panel ramp
--signal-redact-ink-on-panelhsl(220 14% 9%)label ink on the on-panel ramp
--signal-ink-on-light-fillhsl(220 14% 9%)text on a bright accent fill (cross-link type badges)

Where a default is another token (--signal-ink-muted, --signal-ink), the block reads var(--signal-<name>, var(--signal-ink-muted, hsl(220 9% 70%))), so a theme that sets only the surface and ink set still reaches it.

Override as a SET, never individually. @wabbit/tome-cop's @wabbit/tome-blocks-signal-theme/tokens.css overrides the whole set together, scoped to [data-tome-pack="cop"]; so should you. Redefining --signal-panel alone re-creates the split by hand.

Shared console roles. Between each signal token and its default sits a platform role shared with @wabbit/tome-readout and @wabbit/tome-dispatch: var(--signal-panel, var(--tome-console-surface, hsl(220 14% 9%))). The signal token comes first, so anything that sets it (a site, the cop theme) still wins; nothing declares the roles by default, so the defaults above still apply. A site that wants light panels in its light theme imports @wabbit/tome-ui/console.css and sets data-tome-console="theme" on <html> or any wrapper; the role list and the light values are in tome-ui's README. The mapping: panel, raised, line, ink and muted ink read the roles of the same names (surface, surface-raised, line, ink, ink-muted); -on-panel hues read --tome-console-accent-<hue>-on-surface, --signal-accent-<hue> reads --tome-console-accent-<hue>, --signal-on-accent-<hue> reads --tome-console-on-accent-<hue>; status, threat and alert tokens read --tome-console-status-ok / -warn / -fail / -info (deployed and quantum read the blue accent role); the RESTRICTED and SECRET fills read --tome-console-fill-restricted / -fill-secret and their on-fill inks; --signal-ink-on-light-fill and --signal-redact-ink-on-panel read --tome-console-ink-inverse; the on-panel redaction ramp reads the red and amber accent roles. The prose-flow redaction ramp sits on the article, not a panel, and reads no role. Composition roles let the light set match the approved board; unset, each reproduces today's frame. --tome-console-rule-width and -rule-on turn the top edge of the threat, sensor, objective and comms panels into a single 2px hue rule (today's 1px neutral top edge when unset); -hairline-width outlines metric cards and tactical callouts; -status-edge and -status-bar-width give the threat panel a neutral outline; -tint-strength removes the washes behind the threat badge, the system alert, the DECLASSIFIED band and the personnel badges; -badge-outline-width outlines those badges instead; -band-rule-width frames the RESTRICTED and DECLASSIFIED bands with rules in their ink (the light set's fill-restricted is the panel surface, so RESTRICTED is unfilled; SECRET stays filled); -dim-opacity removes the dim on redacted comms rows and degraded or encrypted intercepts. Threat indicators and the sensor readout's SYSTEM cards carry a status word (the field's option label) that is always in the markup and visually hidden unless --tome-console-status-word-display is set (the light set shows it); objective items carry theirs for assistive technology, always visually hidden. The comms transcript's red and blue callsigns use the pack's on-panel values (hsl(0 80% 68%), hsl(215 75% 65%)), since the mid-tones they used failed as text on the dark panel.

Shared render cores

Eight renderers are skins over the render cores in @wabbit/tome-blocks-console, which @wabbit/tome-dispatch renders its matching blocks through as well: signal-comm-intercept, signal-comms-transcript, signal-classification-banner, signal-log-header, signal-redacted, signal-system-alert, signal-ambient-audio and signal-threat-panel (and the signal-comms and signal-banner parents, which dispatch to them). The core renders the markup; each renderer keeps its own stylesheet, whose class names match the core's parts, and passes it as the skin. Stored fields and values are unchanged. Three behaviors follow the cores: the classification banner is a note (role="note"), not a page banner landmark; only a HULL_BREACH system alert is announced (role="alert"), the other variants are notes; and the ambient audio label is readable to assistive technology, with only the waveform hidden. ENCRYPTED comm intercepts still print their message text, dimmed. A threat indicator whose status is active shows a red dot (--signal-status-fail).

Fifteen more renderers are skins over the cores this pack shares with @wabbit/tome-longform, the long-form reading kit: signal-accordion, signal-anchor-section, signal-aside, signal-author-aside, signal-callout, signal-chapter-divider, signal-cross-link, signal-data-table, signal-epigraph, signal-footnotes, signal-image-grid, signal-key-facts, signal-series-nav, signal-spoiler and signal-tabbed-content (and the signal-note, signal-divider, signal-nav and signal-stats parents that dispatch to them). signal-drop-cap stays this pack's own renderer. Their look, stored fields and values are unchanged; what follows the cores is for assistive technology and the keyboard. The tabs move with the arrow keys, Home and End, only the selected tab is in the tab order, and each panel is named by its tab. The spoiler button's spoken name contains its visible words ("Click to reveal spoiler content", "[REDACTED]: reveal spoiler content"). A NUMBERED chapter divider's title is a level-2 heading and the divider is no longer a separator (its contents were not read inside one); ORNAMENTAL and SYMBOL stay separators. Data table header cells are column headers and the caption names the table. Key facts with a status carry the status word (the field's option label), visually hidden. Footnotes carry id="fn-<number>" so the text can link to them. In a SINGLE accordion, only a click on a row's heading opens or closes it, so a link inside an open panel works. FAQ accordions still open several rows at once.

Nine more renderers are skins over the readout-family cores in the same package, which @wabbit/tome-readout renders its matching blocks through: signal-ship-card, signal-personnel-card, signal-metric-grid, signal-objective-list, signal-phase-marker, signal-progress-bar, signal-stat-strip, signal-map-legend and signal-sensor-readout (and the signal-stats and signal-divider parents, which dispatch to the metric grid, stat strip and phase marker). Their markup, stylesheets, stored fields and look are unchanged; every optional part those cores offer stays off here. Where a core names a part differently, the renderer maps it onto the class that already styled it (the ship card's shipName and shipClass, the personnel card's callsign, the sensor readout's iff classes).

Server / client posture

Three renderers carry 'use client', all for interaction state: SignalAccordion (open/close), SignalSpoiler (reveal), SignalTabbedContent (active tab). Each reads rich text through the adapter hook and renders a client core from @wabbit/tome-blocks-console, which holds the state. The other 36 are plain components that render on the server. scripts/assert-rsc-boundaries.mjs pins the count at 3.

registerRenderers() is the server-safe way to fill the shared registry: the legacy @wabbit/tome-blocks-signal-theme/render barrel's side-effect registrations do not run when it is only imported into a Server Component graph, so a registry-based RenderBlock on the server would resolve nothing. Passing the renderers map as components avoids the registry entirely. Either render subpath also loads under plain Node (see the Plain Node gotcha above).

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 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 bundle tier, tagged 'sc' alongside the existing 'star-citizen' tag.
  • 39 registered → 23 offered / 16 deprecated — the inserter/variant consolidation introduced 6 consolidation-parent blocks whose _variant axis absorbs the old blocks' in-block designVersion selects, and narrowed what the gallery/inserter offers while keeping the 16 source slugs registered so existing content keeps rendering. signal-drop-cap, signal-spoiler and signal-redacted stay offered pending a separate move to Lexical inline marks.
  • Generic editorial DNA was extracted elsewhere, not genericized here — the non-SC parts of this family's ancestry went into @wabbit/tome-longform/tome-dispatch/tome-readout instead of stripping signal-theme's own SC-specific schemas.
  • Only state-bearing renderers are client — 3 of 39, enforced by scripts/assert-rsc-boundaries.mjs.

Block reference

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

Pick the Single Expand variant for one-open-at-a-time panels, or keep Default and set designVersion (SINGLE / STACKED / FAQ); set 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 cataloged 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

Import path
@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

0.49.x

2 fixes (0.49.4)
  • 0.49.4

    Admin labels, field descriptions, demo content and messages now use American spellings.

  • 0.49.4
    Updated dependencies [deea397]
    • @wabbit/tome-blocks-console@0.1.3
    • @wabbit/tome-ui@0.22.3

0.43.x

2 fixes (0.43.2)
  • 0.43.2
    Gallery and preview demo props no longer invent site paths.

    A demo link is now the caller's placeholderHref or #, so rendered demos stop emitting real <a href>s to pages that exist on no site. README: subpath and file mentions are written package-qualified (for example @wabbit/tome-x/render) or as code text, so crawlers no longer resolve them as site URLs.

  • 0.43.2
    Updated dependencies [d51ad89]
    • @wabbit/tome-ui@0.22.1
    • @wabbit/tome-blocks-console@0.1.2

0.42.x

1 fix (0.42.1)
  • 0.42.1
    Updated dependencies [9e7b353]
    • @wabbit/tome-ui@0.22.0

0.38.x

1 fix (0.38.0)
  • 0.38.0
    Updated dependencies [a679cc1]
    • @wabbit/tome-ui@0.21.0
    • @wabbit/tome-blocks-console@0.1.2

0.37.x

2 fixes (0.37.0 – 0.37.1)
  • 0.37.1
    Block descriptions read as plain descriptions of what each block shows, not developer notes.

    53 block and variant descriptions carried package names, "Fully static — no peer required", data-variant, slots and hydration details. These are what a visitor reads in a block catalog, so each now says in plain words what the block shows and when to use it.

    The developer facts aren't lost: each one moves into the block's usage.howToUse (or, where a block has none, a code comment above its description), and the 20 that howToUse already covered are simply dropped from the description. CTA's with-form variant no longer claims to render lead-capture fields: it says it leaves room for a form the site supplies, which is what it does.

  • 0.37.0
    Updated dependencies [d69161c]
    • @wabbit/tome-ui@0.20.0
    • @wabbit/tome-blocks-console@0.1.2

0.36.x

1 fix (0.36.3)
  • 0.36.3
    Eighteen block layouts no longer run their text to the window edge: content sits on the page grid's content lines.

    These blocks placed their content at 1 / -1, which only matched the content column when the host wrapped blocks at 2 / -2. On a host that leaves defaultColumnSpan unset, the text sat at the window edge.

    Full-bleed bands stay full-bleed, and their content now sits on content-start / content-end. Affected: editorial-pack's feature walk-strip, message-panel, split-panel (its band now wraps an inner .splitInner) and status-board; extras' bento-section, card-grid, content-pro bento, skill-showcase and section-header statement-hero and ruled-split; marketing-starter's banner with no grid placement, cta door-strip, exhibit gallery-wall, and the Full Width layout of feature-hero and cta.

    In signal-theme, signal-note's aside-left, aside-right and author-overlay sit on the marginalia tracks at 80rem and wider on the page grid; aside-left used to be pulled past the left window edge.

0.35.x

1 fix (0.35.0)
  • 0.35.0
    Updated dependencies [f7940ba]
    • @wabbit/tome-ui@0.19.0
    • @wabbit/tome-blocks-console@0.1.2

0.31.x

2 fixes (0.31.0)
  • 0.31.0
    Payload-free ./meta entries now match what each pack registers, and @wabbit/tome-dispatch, @wabbit/tome-longform and @wabbit/tome-readout add a ./demo entry.

    ./demo (dispatch, longform, readout): the root barrel's demo props (getDemoProps, the per-block getters, DemoContext) on their own subpath, with no Payload import, so a client bundle such as a block gallery can use them without pulling in the block configs. Additive; the root barrel still exports them.

    These packages ship raw Payload Block configs rather than defineBlock descriptors, so they have no ./meta entry.

    ./meta (extras, signal-theme): new extrasDeprecatedBlockMeta and signalThemeDeprecatedBlockMeta lists describe the 16 deprecated blocks each pack still registers for stored content but no longer offers. extrasBlockMeta and signalThemeBlockMeta are unchanged: they still list only the offered blocks.

    The deprecated signal-theme blocks' metadata moved from inline defineBlock arguments into payload-free <block>.meta.ts files; the registered descriptors are unchanged.

    ./meta (extras, marketing-starter): six motion blocks (image-marquee, marquee, showcase, logo-slider, pricing-plans, testimonial) now declare requiredCapabilities: ['motion'] in their metadata. defineBlock already added it at registration, so the registered descriptors are unchanged, but the ./meta entries lacked it and a gallery reading them could not tell these blocks need a motion provider.

  • 0.31.0
    Updated dependencies [1767d0d]
    • @wabbit/tome-ui@0.18.0
    • @wabbit/tome-blocks-console@0.1.2

0.28.x

14 fixes (0.28.0 – 0.28.5)
  • 0.28.5
    Published packages now contain compiled JavaScript and type declarations under a one-line license banner, and no longer include source maps.

    What you install: one compiled .js (ESM) and .cjs (CommonJS) file per source module, its .d.ts / .d.cts declarations, and the stylesheets, fonts and other assets a package already shipped. Every JavaScript module opens with a comment naming the package and its license: /*! @wabbit/<package> — © Wabbit, LLC. Wabbit Tome Commercial License (see LICENSE.md). Not for redistribution. */.

    The .map files and the sourceMappingURL comments that pointed at them are gone, which roughly halves the size of each tarball.

    Debugging: the code is still unbundled and unminified, one readable file per module, so a stack trace points at real code with real names. Line numbers in a stack trace are one higher than before, because of the banner line.

    A 'use client' directive stays the first statement of its module (the banner is a comment above it), so React Server Component boundaries are unchanged.

    No API change, no runtime behavior change, and nothing to do on upgrade.

    In @wabbit/tome-blocks-gallery, the source snapshots extractGallerySource writes from an installed pack leave out the license banner line, so a component or config snapshot starts at the code and a paid block's preview shows its first 15 lines of real code.

  • 0.28.5
    Updated dependencies [775f90a]
    • @wabbit/tome-blocks-console@0.1.2
    • @wabbit/tome-ui@0.17.1
  • 0.28.4
    Fifteen deprecated slugs now render through their successor block's renderer, so each look has one implementation; their output, stored fields and exports are unchanged.

    signal-classification-banner and signal-system-alert are registered by SignalBanner; signal-comm-intercept and signal-comms-transcript by SignalComms; signal-phase-marker, signal-chapter-divider and signal-anchor-section by SignalDivider; signal-cross-link and signal-series-nav by SignalNav; signal-aside, signal-author-aside and signal-epigraph by SignalNote; signal-key-facts, signal-metric-grid and signal-stat-strip by SignalStats. Each is an alias that renders the parent's lane for that slug with the stored block as is, so a document set to a designVersion renders exactly as the parent set to the matching _variant, and an unset value renders as before.

    The renderers map from ./render/register keys these slugs to the aliases. Every slug stays a registered block with its own field schema, demo props and catalog entry, and every renderer component is still exported from ./render with the same props.

    signal-callout keeps its own renderer: it writes its own _variant as data-variant, which differs from what signal-note's callout variants write. No consumer action is needed.

  • 0.28.3
    Fifteen editorial renderers now render through shared cores in @wabbit/tome-blocks-console; their look, stored fields and values are unchanged.

    The renderers are signal-accordion, signal-anchor-section, signal-aside, signal-author-aside, signal-callout, signal-chapter-divider, signal-cross-link, signal-data-table, signal-epigraph, signal-footnotes, signal-image-grid, signal-key-facts, signal-series-nav, signal-spoiler and signal-tabbed-content, and the parents that dispatch to them. signal-drop-cap keeps its own renderer.

    Each keeps its stylesheet, passed to the core as the skin. What changes is for assistive technology and the keyboard:

    • Tabs: only the selected tab is in the tab order; the arrow keys, Home and End move between tabs; each panel is named by its tab.
    • Spoiler: the reveal button's spoken name now contains its visible words ("Click to reveal spoiler content", "[REDACTED]: reveal spoiler content").
    • Chapter divider NUMBERED: the title is a level-2 heading, styled exactly as the text it replaces, and the divider is no longer a separator (a separator's contents are not read). ORNAMENTAL and SYMBOL stay separators.
    • Data table: header cells are column headers, and the caption names the table.
    • Key facts: a fact with a status carries the status word (its option label), visually hidden.
    • Footnotes: each note carries id="fn-<number>" so text can link to it.
    • Accordion SINGLE: only a click on a row's heading opens or closes it, so a click or a link inside an open panel no longer closes the row.

    No consumer action is needed.

  • 0.28.3
    Nine renderers (ship card, personnel card, metric grid, objective list, phase marker, progress bar, stat strip, map legend, sensor readout) now render through the shared cores in @wabbit/tome-blocks-console, which this pack already depends on.

    Their markup, stylesheets, stored fields and look are unchanged, and so is the signal-stats and signal-divider parents' output where they dispatch to them. No consumer action is needed.

  • 0.28.3
    Visible change: an active indicator on the threat panel now shows a red dot (--signal-status-fail) instead of a green one, so an active threat reads as a threat.

    Eight renderers (comm intercept, comms transcript, classification banner, log header, redacted, system alert, ambient audio, threat panel) now render through the shared cores in @wabbit/tome-blocks-console, a new regular dependency. Their markup, stylesheets and stored fields are otherwise unchanged.

    Three behaviors for assistive technology change with the cores: the classification banner is a note (role="note") rather than a page banner landmark; only HULL_BREACH system alerts are announced (role="alert"), the other variants are notes; and the ambient audio label is readable, with only the waveform hidden. The active status word, visible only where a site shows status words, is red to match the dot.

    No consumer action is needed.

  • 0.28.3

    Updated dependencies [bd092c2]

  • 0.28.3

    Updated dependencies [19a540c]

  • 0.28.3
    Updated dependencies [67cdd90]
    • @wabbit/tome-blocks-console@0.1.1
  • 0.28.2
    Signal panels can follow a light theme through the opt-in @wabbit/tome-ui/console.css; the comms transcript's red and blue callsigns are now readable on the dark panel.

    One default changes on purpose: those two callsign colors are lighter, because the old ones failed as text on the dark panel (details at the end).

    Every read is now var(--signal-<name>, var(--tome-console-<role>, <today's value>)). Nothing declares the roles by default, so every block computes the color it did before, and a --signal-* value set by a site or by @wabbit/tome-cop still wins.

    Composition roles let the light set match the approved board: a single 2px hue rule on the threat, sensor, objective and comms panels (today's 1px neutral top edge when unset), hairlines round metric cards and callouts, a neutral threat outline, no washes behind the threat badge, system alert, DECLASSIFIED band and personnel badges, outlined badges, an unfilled RESTRICTED band framed by rules (SECRET stays filled), and no dimming on redacted or degraded lines. Each is zero-width, today's percentage or today's opacity when unset.

    The mapping is in the README.

    Threat indicators and the sensor readout's SYSTEM cards now carry a status word (the field's option label), and objective items carry theirs for screen readers. The word is always in the markup and visually hidden by default (the clip pattern, so the layout does not change); threat and sensor words show when --tome-console-status-word-display is set.

    Deliberate default change: the comms transcript's red and blue callsign colors were hsl(0 72% 50%) (3.71:1 on the dark panel) and hsl(215 65% 45%) (3.23:1). They now use the pack's on-panel values, hsl(0 80% 68%) (6.07:1) and hsl(215 75% 65%) (6.36:1).

    Transcripts using gold, cyan, amber, green or ghost callsigns render as before.

  • 0.28.2
    Updated dependencies [d432a85]
    • @wabbit/tome-ui@0.17.0
  • 0.28.1
    Signal blocks now read their accent, status, redaction, banner and threat colors through named --signal-* variables, so a site can re-color them; nothing changes until you set one.

    Every color keeps today's value as the var() fallback, and no variable is declared at :root. The seven accent hues are applied through one shared helper instead of a pasted .accent-<hue> rule in each of 27 stylesheets; each hue's three roles resolve to the same values as before.

    The new variable names are listed in the README token tables.

  • 0.28.0
    The sensor readout block's contacts table now scrolls inside its own keyboard-reachable region instead of widening the page on phones.

    The region is labeled with the block title (or Sensor contacts). The grid layout's card minimum also caps at the container width.

  • 0.28.0

    Blocks now carry the neutral Tome source tag instead of the legacy source tag, so the gallery lists them as Tome blocks; stored content is unchanged.

0.27.x

2 fixes (0.27.0)
  • 0.27.0
    The pack works on a stock Next.js site: its per-block stylesheets now ship precompiled, so the site needs no next.config plugin.

    Each renderer's .tome-css stylesheet is compiled when the pack is built, into a JS module next to it (<Name>.tome-css.js / .cjs), and the renderers import that. A site no longer has to wrap next.config with withTomeBlockStyles to use the pack, and ./render and ./render/register now load under plain Node, so seed scripts, tests and the Payload CLI can import them.

    Each block's CSS is still inlined only on pages that render the block. A site that already uses withTomeBlockStyles needs no change.

    Scoped class names change once, because they are now keyed on the package rather than on where it is installed. The raw .tome-css files stay in the package as readable source.

  • 0.27.0
    Updated dependencies [c14a133]
    • @wabbit/tome-ui@0.16.0

0.26.x

1 fix (0.26.0)
  • 0.26.0
    Updated dependencies [8c84e70]
    • @wabbit/tome-ui@0.15.0

0.23.xMinor

  • Breaking: block stylesheets are now per-block (`.tome-css`). Each block's CSS ships only on pages that render it, instead of in every page's CSS bundle.

    The 33 stylesheets moved from X.module.css to X.tome-css, and each renderer renders <BlockStyles sheet={styles} /> from @wabbit/tome-blocks-core/block-styles.

    Required in the consuming site: wrap next.config with withTomeBlockStyles (@wabbit/tome-blocks-core/next, blocks-core 0.22.0 or later); without it the .tome-css imports fail to build. See the blocks-core README, "Per-block stylesheets".

    • The @wabbit/tome-blocks-core peer range is now >=0.22.0 <1.0.0.
    • SignalAccordion, SignalSpoiler and SignalTabbedContent are client components; each is now a server-safe wrapper (X.tsx) around X.client.tsx. Exported names and props are unchanged.
    • Block CSS now loads after all bundled CSS. A site-level rule that overrode one of this pack's classes at equal specificity, and won only by loading later, no longer wins.

0.20.x

2 fixes (0.20.0)
  • 0.20.0
    Signal aside and author aside now get their wide-screen outdent.

    They read the undeclared --tome-space-xxxl and now read --tome-space-3xl. The ambient-audio and progress-bar --tome-motion-normal timings now resolve because @wabbit/tome-ui declares that token.

  • 0.20.0
    Updated dependencies [9f6b52c]
    • @wabbit/tome-ui@0.14.0

0.18.x

4 fixes (0.18.0 – 0.18.3)
  • 0.18.3

    CSS files are now copied to dist/ by a post-build script instead of a tsup onSuccess hook; no behavior change, and the published dist/ is identical.

  • 0.18.3
    signal-accordion's Single Expand variant now always opens one panel at a time, and signal-callout's Lore variant always renders the Lore treatment.

    Before, both variants only set data-variant; the look still came from the in-block designVersion select, so picking a variant changed nothing visible. The renderers now map _variant onto designVersion the way signal-note does: single → SINGLE, lore → LORE.

    The default variant (now labeled "Default") still follows designVersion, so documents on the default variant render as before. A saved document on single or lore whose designVersion disagreed now renders the variant it names.

    The designVersion select is hidden in the admin while a forcing variant is picked. data-variant is still emitted.

  • 0.18.1
    The gallery now shows signal-accordion's two variants, and the accordion and callout descriptions name their registered variants.

    signal-accordion's variants moved to a payload-free signal-accordion.variants.ts so signalThemeBlockMeta can list them. The package description and bundle description now give the real count: 39 registered, 23 offered.

  • 0.18.0
    register() is now built with blocks-core's createPackRegistrar.

    Behavior 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.

0.17.xMinor

  • 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

0.16.xMinor

  • 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.

  • 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.

  • 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.

  • 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 favor 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
1 fix (0.16.1)
  • 0.16.1
    Updated dependencies [befde64]
    • @wabbit/tome-ui@0.13.0

0.15.xMinor

  • 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 normalizes null to undefined so React omits the attribute — the unregistered path stays byte-identical to the pre-adapter markup.

    Behavior 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
9 fixes (0.15.4 – 0.15.12)
  • 0.15.12
    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.

  • 0.15.12
    Updated dependencies [54ff357]
    • @wabbit/tome-blocks-extras@0.15.12
  • 0.15.11
    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).

  • 0.15.11
    Fix a systemic, invisible-text readability defect across the block packs: a text color 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 colors used as text. warning/success/error/ destructive are FILL colors, 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

  • 0.15.11

    Updated dependencies [1bebcdc]

  • 0.15.11
    Updated dependencies [48773ac]
    • @wabbit/tome-blocks-extras@0.15.11
    • @wabbit/tome-ui@0.11.2
  • 0.15.9
    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.
  • 0.15.9
    Updated dependencies [71d3b09]
    • @wabbit/tome-blocks-extras@0.15.9
    • @wabbit/tome-blocks-core@0.15.9
    • @wabbit/tome-ui@0.11.1
  • 0.15.4
    Updated dependencies [0a070e0]
    • @wabbit/tome-ui@0.11.0

0.14.x

1 fix (0.14.0)
  • 0.14.0
    Updated dependencies
    • @wabbit/tome-blocks-core@0.14.0
    • @wabbit/tome-blocks-extras@0.14.0

0.13.x

2 fixes (0.13.0)
  • 0.13.0

    Updated dependencies [f4d55c9]

  • 0.13.0
    Updated dependencies [eb403d4]
    • @wabbit/tome-blocks-core@0.13.0
    • @wabbit/tome-blocks-extras@0.13.0

0.12.x

1 fix (0.12.1)
  • 0.12.1
    Updated dependencies
    • @wabbit/tome-ui@0.10.0

0.11.x

13 fixes (0.11.0 – 0.11.2)
  • 0.11.2
    Updated dependencies [e11d5a2]
    • @wabbit/tome-blocks-core@0.11.2
    • @wabbit/tome-blocks-extras@0.11.2
  • 0.11.0
    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.

  • 0.11.0
    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.

  • 0.11.0

    Updated dependencies [26dfa07]

  • 0.11.0

    Updated dependencies [36e537a]

  • 0.11.0

    Updated dependencies [36e537a]

  • 0.11.0

    Updated dependencies [36e537a]

  • 0.11.0

    Updated dependencies [a93f478]

  • 0.11.0

    Updated dependencies [5f78397]

  • 0.11.0

    Updated dependencies [5f78397]

  • 0.11.0

    Updated dependencies [aef2725]

  • 0.11.0

    Updated dependencies [aef2725]

  • 0.11.0
    Updated dependencies [aef2725]
    • @wabbit/tome-blocks-core@0.11.0
    • @wabbit/tome-blocks-extras@0.11.0
    • @wabbit/tome-ui@0.9.9

0.10.x

3 fixes (0.10.0 – 0.10.2)
  • 0.10.2
    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'.

  • 0.10.2
    Updated dependencies [ec4b7bc]
    • @wabbit/tome-ui@0.9.8
  • 0.10.0
    Updated dependencies
    • @wabbit/tome-blocks-core@0.10.0
    • @wabbit/tome-blocks-extras@0.10.0

0.9.xMinor

  • 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
8 fixes (0.9.1 – 0.9.5)
  • 0.9.5
    Updated dependencies
    • @wabbit/tome-blocks-extras@0.9.5
  • 0.9.4
    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.

  • 0.9.4
    Updated dependencies [bed3f90]
    • @wabbit/tome-blocks-core@0.9.4
    • @wabbit/tome-blocks-extras@0.9.4
    • @wabbit/tome-ui@0.9.7
  • 0.9.3
    Updated dependencies
    • @wabbit/tome-ui@0.9.6
  • 0.9.2

    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.

  • 0.9.2

    Updated dependencies

  • 0.9.2
    Updated dependencies
    • @wabbit/tome-blocks-core@0.9.2
    • @wabbit/tome-ui@0.9.5
    • @wabbit/tome-blocks-extras@0.9.2
  • 0.9.1
    Updated dependencies [c07f3c8]
    • @wabbit/tome-blocks-extras@0.9.1

0.8.xMinor

  • 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 _variants (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 _variants (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 _variants). 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

0.7.x

4 fixes (0.7.0)
  • 0.7.0
    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.

  • 0.7.0

    Updated dependencies [28802fa]

  • 0.7.0

    Updated dependencies [66c611c]

  • 0.7.0
    Updated dependencies [8958d41]
    • @wabbit/tome-blocks-extras@0.7.0
    • @wabbit/tome-blocks-core@0.7.0

0.6.x

2 fixes (0.6.1 – 0.6.2)
  • 0.6.2
    Updated dependencies [4b2f368]
    • @wabbit/tome-blocks-core@0.6.2
    • @wabbit/tome-blocks-extras@0.6.2
  • 0.6.1
    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

0.5.xMinor

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

4 fixes (0.5.7 – 0.5.9)
  • 0.5.9

    @wabbit/tome-blocks-core@0.5.9

  • 0.5.9

    @wabbit/tome-blocks-extras@0.5.9

  • 0.5.7

    @wabbit/tome-blocks-core@0.5.7

  • 0.5.7

    @wabbit/tome-blocks-extras@0.5.7

0.4.x

4 fixes (0.4.0 – 0.4.2)
  • 0.4.2
    Updated dependencies
    • @wabbit/tome-blocks-extras@0.4.2
    • @wabbit/tome-blocks-core@0.4.2
  • 0.4.1
    Updated dependencies
    • @wabbit/tome-blocks-extras@0.4.1
    • @wabbit/tome-blocks-core@0.4.0
  • 0.4.0

    Updated dependencies [b76f684]

  • 0.4.0
    Updated dependencies [90a694d]
    • @wabbit/tome-blocks-core@0.4.0
    • @wabbit/tome-blocks-extras@0.4.0

0.3.xMinor

  • 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:

    // 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

Full changelog across all packages