Changelog

brand.magicblocks.ai · changelog

What changed.

Per-package release notes for @magicblocksai/ui and @magicblocksai/css, kept in lockstep with the published npm versions. CSS bumps mirror UI bumps whenever the shared _shared.css changes — sometimes a UI patch ships without a CSS bump (e.g. a hook-only fix).

@magicblocksai/ui changelog

Format: Keep a Changelog · SemVer.


v4.13.0 — 2026-06-11

Agent Builder redesign Phase 4 — Templates + close-out (spec §4.8/§10; the four-phase redesign is complete). Lockstep with @magicblocksai/css@4.13.0. Additive — minor, no breaking changes.

Added

  • <TemplateCard> (§18.21) — one template in the new-agent gallery: name, pitch, the journey shape as a mini block chain, what it ships with; selected accent ring, blank dashed Start-blank variant, "MagicBlocks"/"Yours" badge.
  • <TemplateGallery> (§18.21) — the creation-sheet grid (auto-fill, single-select via consumer state). Copy-on-create.
  • <BlockLibraryDrawer> (§18.21) — the journey rail's block-template drawer composing <Drawer>: the MagicBlocks starter set + "Your blocks", per-row Insert. Copy-on-insert; exports BlockTemplate.
  • §18.22 Channels tab — composition merging Channels + Design & Go Live + Contact Transfer ("where it talks, and where it hands off"), with the greyed-unavailable-channel pattern.
  • §18.23 Settings tab — composition: Guardrails first, conversation defaults, about, DangerZoneBlock (Pause · Duplicate-as-draft · Delete).
  • docs/building-the-agent-builder.md — the app-team guide tying all four phases (4.10–4.13) into per-surface recipes.

Changed

  • §17.12 retitled "Connections & tools — Tools & MCP" (id unchanged) — the shared-infrastructure home, formerly Library; the four shelves now live on the Agents HQ (§18.19).

v4.12.0 — 2026-06-11

Agent Builder redesign Phase 3 — Sage (spec §4.7/§10). Lockstep with @magicblocksai/css@4.12.0. Additive — minor, no breaking changes.

Added

  • <ProposalCard> (§17.17) — Sage's unit of change: pending (Accept · Adjust · Dismiss), applied (reversible, Undo), error (explains why nothing changed); plain-language title + summary, the exact change behind a closed-by-default "Show exactly what changed" disclosure, .proposal-old / .proposal-new for old → new text. Exports ProposalCardState.
  • SageDrawer proposal upgrade (opt-in) — the proposal message variant gains state / summary / diff / onUndo; setting any of them renders the row through <ProposalCard> (with onEditonAdjust). Legacy proposal messages keep the original .sage-proposal markup byte-for-byte.
  • §17.18 Sage dock — the builder's right dock: Test + Sage in one column (.sage-dock chrome), the Sage tab streaming turns + proposal cards over the shipped .sage-* classes.
  • docs/sage-proposal-contract.md — the NextGen wiring contract: agents.proposeDraftPatch returning { summary, detail?, patch, risk? } (JSON-Patch ops over the workflow document), the four hard rules (draft-only; substantive waits / low-risk applies with Undo; Recent-changes entry with inverse patch; validation gates every apply), UI mapping table, worked example.

v4.11.0 — 2026-06-11

Agent Builder redesign Phase 2 — Studio (spec §10). Lockstep with @magicblocksai/css@4.11.0. Additive — minor, no breaking changes.

Added

  • <LeadsToStrip> (§18.20) — the block editor's orientation strip: "Leads to → Discovery, once every key fact here is collected", with Reached from revealed on hover/focus. A block with no local route says "no local route yet — anytime actions still apply".
  • <KeyFactBehaviourLine> (§18.20) — a key fact's asked-vs-listened behaviour as a sentence ("Asked in Greeting · listened for everywhere"), with the ask-when rule inline.
  • RailBlockItem start / hint — the Start pill on the journey's entry block, and a muted coaching line ("0 jobs · needs work") replacing the stats on empty blocks.
  • JourneyGraphEdge.variant: "dashed" — the anytime-actions layer on the journey map; the attribute is emitted only when dashed, so existing trace-view DOM is unchanged. Plus a first proper docs/JourneyGraph.md page.
  • ActionList title / description — retitle the workbench head ("Anytime actions") and add the framing line ("Rules that watch every block").

Chapter

  • §18.20 Journey studio — the redesigned Journey tab: journey-only rail (List ⇄ Map toggle, blocks with start/hint, Add block · Block library, pinned Key facts + Anytime actions rows), the promoted map view with dashed anytime edges + selection card, and the two new primitives. §18.11/§18.12 descs cross-reference the new naming.

v4.10.0 — 2026-06-11

Agent Builder redesign Phase 1 — Frame (spec: docs/superpowers/specs/2026-06-11-agent-builder-redesign-design.md). Lockstep with @magicblocksai/css@4.10.0. Additive — minor, no breaking changes.

Added

  • <UsedByChip> (§18.18/18.19) — usage indicator for the Agents HQ shared shelves: "Used by N agent(s)" in blue, calm neutral "Not used yet" at zero. Wraps <Chip>.
  • <SetupProgressChip> (§18.18) — "Setup 5 of 6 — connect a phone number" for the Overview status band; green "Setup complete" when done. Wraps <Chip>.
  • <GoLiveCard> (§18.18/18.9) — the Overview card answering "how do people reach this agent, and how do I go live?": channel rows with live / not-yet states and a deep-link action into the agent's Channels tab. Exports GoLiveChannel.
  • <RecentChangesList> (§18.18/18.9) — the Overview's accountability card: human and Sage edits to the working copy, timestamped; Sage entries (by: "sage") carry an Undo. Exports RecentChange.
  • WorkspaceShell actions[].accent (§15.27) — accent-tinted launcher treatment for a bottom-cluster action (the pinned Ask Sage entry on the slim rail). Renders .ws-nav-icon.is-accent.

Chapter compositions (no new API)

  • §18.18 Agent frameAgentBuilderHead + the five-tab LinkTabs row (Overview · Journey · Knowledge · Channels · Settings) via the new .ab-frame-tabs class; "View conversations" joins the head actions, testing moves to the right dock.
  • §18.19 Agents HQ shelves — the HQ tab row (All agents · Personas · Snippets · Forms · Goals) over DataTable shelf compositions with UsedByChip usage cells; Snippets debut as the paste-anywhere {{token}} entity; empty-shelf state.
  • §18.9 Overview — the live agent's home — status band + SetupProgressChip, GoLiveCard + identity card, KPI strip, "Questions it couldn't answer" with *Teach it*, Guardrails glance, RecentChangesList.

v4.9.0 — 2026-06-10

KnowledgeLane.kind aligned to the retrieval-policy vocabulary. Lockstep with @magicblocksai/css@4.9.0. Additive — minor, no breaking changes.

Changed

  • <KnowledgeLane> kind now uses always | semantic | conditional — matching the DB retrievalPolicy and AttachLanePicker's lane values, so there's one vocabulary across the Knowledge components (no more kind relevant/topic vs policy semantic/conditional mismatch). Plain display names are unchanged. The previous kind="relevant" (→ semantic) and kind="topic" (→ conditional) are accepted as deprecated aliases — normalised internally to byte-identical output — and will be removed in the next major. Lane CSS classes renamed accordingly (.knowledge-lane-relevant.knowledge-lane-semantic, .knowledge-lane-topic.knowledge-lane-conditional).

v4.8.1 — 2026-06-10

No-op lockstep bump with @magicblocksai/css@4.8.1 — a KnowledgeLane border fix lives in the CSS package; no @magicblocksai/ui API or rendered-DOM change.

v4.8.0 — 2026-06-10

Knowledge editor primitives (Phase A of the Knowledge redesign) — §17.13–17.16. Lockstep with @magicblocksai/css@4.8.0. Additive — minor, no breaking changes.

Added

  • <TopicChip> (§17.13) — maps the 12 conditional-retrieval topics (PlaybookChunkLabel) to plain labels (Pricing, Objections, …) in the kit's green tone. Wraps <Chip>. Also exports KNOWLEDGE_TOPICS + the KnowledgeTopic type.
  • <KnowledgeItemRow> (§17.13) — one row in the collection editor: a source icon (manual / website / spreadsheet / file) + title + an adaptive one-line content preview (collapses to a single line when omitted, e.g. short "Always" facts) + optional <TopicChip> + updated time. Composes <SourceRow>.
  • <KnowledgeLane> (§17.14) — a tinted lane header (dot + plain name + helper + count) over KnowledgeItemRows. Three lanes — Always (accent), When it's relevant (info), When a topic comes up (success) — mapping to the always / semantic / conditional retrieval policies.
  • <AddKnowledgeMenu> (§17.15) — the "Add knowledge" dropdown: write it yourself / import from a website / upload a spreadsheet / upload a file. Composes <Menu>; onChoose fires the chosen path.
  • <AttachLanePicker> (§17.16) — attach a collection to an agent by picking which lanes (Checkbox) and which topics (FilterChipGroup) it gets, with a plain-language summary ("certain parts, out of those 3 things"). Controllable via value / defaultValue / onValueChange.

Plain language throughout (no RAG / semantic / intent jargon in UI copy); kit tokens + SVG icons only. NextGen wires the backend (scraping, CSV, Q&A generation, retrieval). Spec: docs/superpowers/specs/2026-06-09-knowledge-redesign-design.md.

v4.7.0 — 2026-06-08

<WorkspaceShell> now owns mobile navigation — zero wiring. Lockstep with @magicblocksai/css@4.7.0. Additive — minor, no breaking changes.

Added

  • <WorkspaceShell> mobile drawer (zero-wiring). At ≤960px the rail folds into the off-canvas <AppShell> drawer and the shell renders its own hamburger (.ws-mobile-trigger) to reveal it — previously a consumer had to hand-wire mobileNavOpen + a trigger, so a drop-in usage silently lost its nav on mobile (the rail was display:none with nothing to open it). Tapping a nav item / action / settings closes the drawer; scrim + Esc close it too. Desktop is unchanged (trigger hidden ≥961px), preserving the sidebar-first, no-top-bar identity. New defaultMobileNavOpen prop; mobileNavOpen/onMobileNavOpenChange (inherited from <AppShell>) now drive the shell-owned state for controlled use.

v4.6.0 — 2026-06-08

Skeleton loading-placeholder primitives + one canonical shimmer kit-wide. Lockstep with @magicblocksai/css@4.6.0. Additive — minor, no breaking changes.

Added

  • <Skeleton> / <SkeletonText> / <SkeletonAvatar> (§9.8) — loading-placeholder primitives. <Skeleton> is one shimmer block (width / height as number→px or CSS length, radius sm|md|pill|full); <SkeletonText lines> renders shimmer rows with a shortened last line; <SkeletonAvatar size> is a round shimmer. Token-driven gradient, prefers-reduced-motion-safe, aria-hidden. Resolves the Next-Gen "universal loading-UX sweep" request — consumers compose shaped fallbacks without re-deriving the shimmer.

Changed

  • Internal: DataTable, ChatTranscript, and SourceCard skeleton states migrated onto the shared skel-shimmer keyframe — one shimmer treatment kit-wide. No public API change; rendered DOM unchanged (ChatTranscript's loading bubbles adopt the gradient sweep in place of the prior opacity pulse).

v4.5.0 — 2026-06-07

Sessions live takeover console (Phase 2) + overview mobile cards. Lockstep with @magicblocksai/css@4.5.0. Additive — minor, no breaking changes.

Added

  • <TakeoverConsole> (§19.9) — the live human-takeover console (<SessionPage mode="live">'s body). Two control states: ai_driving (a "Take over" CTA) and human_in_control (a guardrails-off <ChatComposer> — "Guardrails off · sending as a human" — plus a <JourneyBlockPicker> hand-back). Composes ChatTranscript · ChatComposer · JourneyBlockPicker · KeyFactGrid; the transcript is a slot, and an operator-context sidebar carries the contact, key facts so far, a "Sage suggests" draft, and the journey position.
  • <JourneyBlockPicker> — hand-back target menu (resume where the AI paused · jump to a Journey Block · End — no further automation). A thin compose over <DropdownMenu>.
  • <SessionPage mode="live"> (§16.28) — live mode renders a console slot (a <TakeoverConsole>) under the shared hero, in place of the summary band + transcript. New mode + console props.

Changed

  • ChatMessagefrom gains "operator": an accent-tinted, right-aligned bubble with a "human" tag, for replies a person sent during a takeover (the contact stays from="user"). Additive union member; existing roles unchanged.
  • <SessionPage>keyFacts / memories / handover are now optional (default []) so live sessions needn't supply summary data. Non-breaking (looser).
  • <SessionsOverviewPage> — the session table reflows to stacked cards on mobile (≤640px) with a full-width row action; desktop unchanged.

v4.4.0 — 2026-06-07

Sessions read-path — the operator Sessions experience (Phase 1). Lockstep with @magicblocksai/css@4.4.0. Additive — minor, no breaking changes.

Added

  • <SessionsOverviewPage> (§16.29) — the top-level Sessions table: the Contacts-page chrome (.list-screen + .tbl + .kpi-delta-tile) plus a date-range, a stat strip, six state tabs (All · Live · Human takeover · Needs attention · Follow-up queued · Completed), a filter slot (compose <FilterPopover> + <FilterChipGroup> per dimension), and rows with Human-takeover / Needs-attention flags + Take over / Review actions.
  • <SessionPage> (§16.28) — a completed session, summary-led: hero (identity · channel · agent · Goals chips · stats) → <SessionSummaryBand> → a collapsible transcript slot (a composed <ChatTranscript>) + <MemoriesDialog>.
  • <SessionSummaryBand> (§16.27) — the completed-session summary header; composes KeyFactGrid + MemoryList + HandoverOutcome.
  • <MemoriesDialog> (§16.26) — the expand-memories popup; composes Modal + MemoryList.
  • <HandoverOutcome> (§16.25) — webhooks fired + handover target, with CSS-drawn glyphs.

Changed

  • Memory gains optional type / sourceQuote / crossSession — rendered only when present, so existing <MemoryList> usage is unchanged.

v4.3.1 — 2026-06-06

<PipelineBar> restyle; lockstep with @magicblocksai/css@4.3.1. Patch — no API change.

Changed

  • <PipelineBar> now renders a thin segmented bar — a .pip-seg per stage with the label below, unified with <LifecycleBar>: past stages ink-washed, the current stage accent + glow, future hairline (replacing the chunky filled-cell pill). Props are unchanged (stages / current / onStageClick / per-stage metric + click-to-move all intact) — only the rendered markup + look change.

v4.3.0 — 2026-06-06

No @magicblocksai/ui changes — lockstep no-op bump with @magicblocksai/css@4.3.0, which ships the §16.15 Contacts-list .ct-* chrome + the .tbl-check select-column primitive as operator CSS (app-team R4).


v4.2.0 — 2026-06-06

Three additive primitives for the MagicBlocks app team (NextGen); lockstep with @magicblocksai/css@4.2.0. Minor — new exports / props / types, no breaking change.

Added

  • <SystemThemeIcon> — a half-filled-circle "system / auto appearance" glyph, the companion to <SunIcon> / <MoonIcon> for a System / Light / Dark theme control. (The one deliberate, documented exception to the icon family's monoline rule: the right half is filled with currentColor so the glyph depicts both themes — still monochrome.)
  • <WorkspaceSwitcher badge> + the <WorkspaceShell> workspace.badge slot — an unread / attention indicator on the workspace avatar (true = dot, a value = count), mirroring WorkspaceNavIcon.badge. When set, the avatar is wrapped in a non-clipping .ws-switcher-avatar-frame so the corner badge escapes the avatar's overflow:hidden.
  • change_stage + change_segment action types — two new ActionType members, their Action-union variants (ChangeStageAction / ChangeSegmentAction), and config-less actionRegistry entries (so they appear as <ActionPicker> rows). The stage / segment pickers are workspace-owned — the host app renders them; no stage or segment data lives in the kit (config-less, like do_not_respond).

v4.1.2 — 2026-06-05

Keyboard accessibility for the kit's pointer-driven interactions; lockstep with @magicblocksai/css@4.1.2. Patch — no API change.

Changed

  • <DataTable tableKey> — column resize and reorder are now keyboard-operable. The resize grip is a focusable role="separator" splitter (←/→, Shift = larger step, Home/End = min/max); headers grab with Space, then ←/→ to move, Esc to cancel. Mouse drag unchanged.
  • <Checklist reorderable> — keyboard reorder (Space grab · ↑/↓ move · Esc cancel), guarded so it never collides with the row's tick checkbox or more-button.
  • <AgentBuilderShell> — the rail resizer gains aria-valuenow/min/max + Home/End, completing the W3C window-splitter pattern (←/→ already worked).

The audit confirmed <Kanban>, <Carousel>, <SortableList> / <SortableHandle>, and <RailBlockItem> were already keyboard-accessible. No new exports, props, or variants.


v4.1.1 — 2026-06-05

Contact-page polish; lockstep with @magicblocksai/css@4.1.1. Patch — no API change.

Changed

  • <Sparkline> / <ScoreRing> read thinner, kit-wide — lighter default stroke weights (CSS-only; no markup change). Applies everywhere they're used, including <EngagementScore>.
  • <ContactDetailPage> — the Memories tab now wraps the memories and key-facts columns in .cd-cards (matching the Overview cards), and the AI-summary card's sparkle icon is sized correctly (was previously ballooning).

v4.1.0 — 2026-06-04

The AI-native contact page. Additive minor; lockstep with @magicblocksai/css@4.1.0. A new <ContactDetailPage> plus the seven contact-page primitives it composes (chapters 16.18–16.24) — industry-agnostic chrome + agent-captured intelligence.

Added

  • <ContactDetailPage> — the page: a warm identity hero + lifecycle journey bar + universal stat strip, then tabs over Overview / Sessions / Memories / Notes. Composes the primitives below + DropdownMenu (share / opt-out / delete). Renders the reference data with zero props (like the other *Pages).
  • <LifecycleBar> + LIFECYCLE_STAGES — segmented lead lifecycle (Engage → Qualify → Follow-up → Converted → Re-engage).
  • <QuietHoursIndicator> — read-only SMS-safe send window with a live "now" marker (10DLC / quiet-hours compliance).
  • <MemoryList> — free-form, agent-summarised memories.
  • <KeyFactGrid> — structured agent-captured facts with provenance (grid / list layouts).
  • <EngagementScore> — composes ScoreRing + Sparkline + a "computed from…" basis line.
  • <SessionCard> — outcome-led session card with a sentiment rail, metric strip, channel chip (Webchat / SMS / Voice / Email) + collapsible transcript.
  • <TriggerAgentDialog> — channel-aware outbound agent picker (composes Modal + Button).

Notes

  • Chapter 16.16 ("Contact detail composition") is elevated to demo the real <ContactDetailPage> (hydrated live on the kit site); its chapter-private .cd-* CSS is retired in favour of the shipped _shared.css operator family.
  • All seven primitives ship four surfaces; declarative leaves carry active markup-equivalence fixtures, stateful/portal ones skip per precedent.

v4.0.0 — 2026-06-04

Explainability consolidation — complete. Breaking major; lockstep with @magicblocksai/css@4.0.0. The unified set (<Trace>, <SourcePassage>, <SourceCard>) shipped additively across 3.4.0–3.6.1; 4.0.0 cleans up the superseded names. See docs/MIGRATING-TO-4.md in the repo.

Breaking

  • <CompactTrace> removed (+ CompactTraceProps / CompactTracePhase / CompactTraceStatus). Use <Trace> — it's layout="compact" by default, and layout="timeline" for the full robot-head. Types: TraceProps / TracePhase / TraceStatus.
  • <EvidenceQuote> is now a thin alias of <SourcePassage> — it renders .source-passage markup (mapping sourcecite), not the old .evidence-quote DOM. The component + source prop still work; restyle against .source-passage* or switch to <SourcePassage>.

Deprecated (still exported, unchanged — no action required)

  • <TraceTimeline><Trace layout="timeline"> (TraceTimeline is the engine Trace renders for the timeline layout).
  • <TraceItemCard><SourceCard> (TraceItemCard is the lightweight card Trace renders for kind:"card" items).

Notes

  • <JourneyGraph> is unchanged + first-class.
  • No CSS removed.evidence-quote* ships on as a styling back-compat alias; <Trace> renders the existing .compact-trace* / .trace-timeline* class families. @magicblocksai/css@4.0.0 is a lockstep no-op.

v3.6.1 — 2026-06-04

Fix — restore the <Trace> implementation missing from the 3.5.0 / 3.6.0 tags. A release-staging error (an aborted git add on an already-renamed file) left CompactTrace.tsx (the <Trace> rename + layout="timeline"), index.ts (the Trace barrel exports) and TraceTimeline.tsx (the @deprecated tag) out of the 3.5.0 and 3.6.0 tags — so those published packages had no Trace export despite their CHANGELOGs announcing it. This patch lands the actual code.

If you installed 3.5.0 or 3.6.0, update to 3.6.1 to get <Trace> / layout="timeline". <CompactTrace> kept working throughout (the 3.4.0 code under the old name); <JourneyGraph> edge hover (3.6.0) was unaffected.


v3.6.0 — 2026-06-04

<JourneyGraph> — edge hover detail. Additive minor; lockstep with @magicblocksai/css@3.6.0. Closes the Next Gen app-team Flow-map request.

Added

  • JourneyGraphEdge.title?: string — the transition's rule in full (the short label stays a summary). Rendered as a native SVG <title> tooltip; a titled edge also becomes keyboard-focusable + aria-labelled, with a wide invisible hit-area so the thin line is easy to hover, and highlights on hover / focus. Untitled edges render unchanged.

v3.5.0 — 2026-06-04

Explainability consolidation, part 2 — <Trace>, the unified reasoning-trace view. Additive minor; lockstep with @magicblocksai/css@3.5.0. Nothing is removed — every prior name still works.

Added

  • <Trace> (+ TraceProps / TracePhase / TraceStatus) — the single reasoning-trace view, on one TraceEvent[] spine, with two layouts: - layout="compact" (default) — the nested-disclosure trace for chat-sized surfaces (this is the former <CompactTrace>). - layout="timeline" — the full "robot head": a rich, always-expanded vertical timeline for a full operator pane, rendered via <TraceTimeline> over the same events. One component now covers both surfaces.

Deprecated

  • <CompactTrace> (+ CompactTraceProps / CompactTracePhase / CompactTraceStatus) → renamed to <Trace> (it's layout="compact"). Still exported as an alias; switch imports to Trace.
  • <TraceTimeline> → use <Trace layout="timeline"> (which renders it). Unchanged and still exported.

Notes

  • Additive — no exports removed, no defaults changed. <Trace> renders the existing .compact-trace* / .trace-timeline* class families (no new CSS).
  • The future 4.0.0 major is reserved for *removing* the deprecated names once consumers have migrated to <Trace>.

v3.4.0 — 2026-06-04

Explainability consolidation, part 1 — the knowledge unit + a trace that renders it. Additive minor; lockstep with @magicblocksai/css@3.4.0. The explainability components begin converging onto one TraceEvent[] spine and one vocabulary. Nothing is removed or renamed in this release — the breaking cleanup (rename CompactTraceTrace, retire TraceTimeline/JourneyGraph, re-chapter §20) lands in 4.0.0.

Added

  • <SourcePassage> + SourcePassageProps — the quoted source / evidence / citation passage: a verbatim quote with a mono cite line (source · relevance · freshness · optional link), <mark> highlight, six tones. Supersedes <EvidenceQuote>. (chapter 20.8)
  • <SourceCard> + SourceCardProps — the knowledge card: a label · value · meta head that expands to a source body, supplied eagerly (details) or loaded on first open (loadDetails) and cached. Owns the collapsed → loading → loaded → error+retry lifecycle. Supersedes <TraceItemCard>. (chapter 20.9)
  • createAsyncReveal / useAsyncReveal (+ AsyncRevealController / AsyncRevealSnapshot / AsyncRevealState / CreateAsyncRevealOptions / UseAsyncRevealResult) — the load-on-open primitive powering <SourceCard> and any consumer disclosure that fetches on first open. Pure controller + useSyncExternalStore hook; idle/loading/loaded/error + retry, cached after the first load.
  • <CompactTrace> — knowledge on demand + width follows open: - source item kind on TraceEvent.items (+ TraceSourceRef): carries a lightweight reference; <CompactTrace> renders each via a load-on-open <SourceCard>. - resolveSource?: (ref) => Promise<ReactNode> — loads each source item's body on first open, so a trace shows the knowledge it used without carrying every passage inline. - defaultAllPhasesOpen? — start with every phase open (the timeline-style preset). - Width follows open — closed, the trace is a discrete resting pill that hugs its content (so it doesn't overwhelm a chat / session window); open, it goes full-width.

Deprecated

  • <EvidenceQuote> → use <SourcePassage> (migrate sourcecite). Still exported, render unchanged; becomes a thin alias in 4.0.0.
  • <TraceItemCard> → use <SourceCard> (props carry over; adds the async loadDetails body). Still exported, render unchanged; alias in 4.0.0.

Notes

  • <TraceTimeline> gains the source item kind + TraceSourceRef types only (render unchanged; an unknown item kind falls back to a bullet row, so it degrades gracefully). It is not deprecated in this release.

v3.3.1 — 2026-06-04

Lockstep no-op with @magicblocksai/css@3.3.1 (a .compact-trace CSS refinement: the detail / knowledge cards now flow into a responsive grid so <CompactTrace> reads well at any width — chat panel to full pane). No UI source change.


v3.3.0 — 2026-06-04

<CompactTrace> — condensed reasoning trace for chat-sized surfaces. A nested-disclosure variant of <TraceTimeline> (chapter 20) for chat-testing windows, embeds, and side panels: top (closable to a resting pill) → phases → steps → inline full-reveal detail, with timing at every level. Composes the disclosure mechanics + the timing helpers + <TraceItemCard>. Additive minor; lockstep with @magicblocksai/css@3.3.0.

Added

  • <CompactTrace> + CompactTraceProps / CompactTracePhase / CompactTraceStatus. Takes the same TraceEvent[] as <TraceTimeline>; groups events by the new phase field. No phase on any event → a flat two-level list (top → steps), so short traces stay simple.
  • TraceEvent.phase? — additive. <TraceTimeline> ignores it, so one TraceEvent[] feeds both components.
  • groupTracePhases / isFlatTrace / TracePhaseRun — the phase grouping helpers (contiguous-run), exported for pre-grouping.
  • formatAbsolute / formatDelta are now exported (the shared trace timing formatters).

Notes

  • <TraceTimeline> is unchanged beyond the additive phase? field. The .compact-trace* styles ship in @magicblocksai/css@3.3.0.

v3.2.0 — 2026-06-03

WorkspaceShell adoption blockers — router-aware + sidebar-first. The four gaps NextGen was holding the shell swap on. Fully additive — every default is unchanged. Lockstep with @magicblocksai/css@3.2.0. App-team Round 20.

Added

  • linkComponent on <WorkspaceShell> (and on <WorkspaceNavIcon> / <WorkspaceSwitcher> / <WorkspaceUser>) — a router-aware link injected and threaded to every rail link, so an SPA navigates client-side instead of full-page-reloading. Active state stays the consumer's active prop (the kit never computes routes). New exported type RailLinkComponent. (A — blocker)
  • user.menu — turn the user cluster into a menu trigger (sign-out, account) for imperative actions an href can't express. Composes the kit <Menu> (opens upward); { divider: true } separates sections. New exported type WorkspaceUserMenuItem. (B)
  • actions on <WorkspaceShell> + onClick/badge on nav items — keep ⌘K / Ask-Sage / notifications in the rail (sidebar-first, no top bar). <WorkspaceNavIcon badge> renders a dot (or small count). New exported type WorkspaceActionDef. (C)
  • loading on <WorkspaceShell> — a top-of-main nav-progress sliver for a rail-only shell (composes <RouteProgress>). (D)

Notes

  • .app-shell-main gains position: relative (a positioning context for the rail-only loading sliver). No visual change; only matters for absolutely- positioned descendants, which should anchor to the main column anyway.
  • Every change is opt-in via a new optional prop — existing <WorkspaceShell> / rail-primitive usage renders byte-identically (markup-equivalence unchanged).

Fixed

  • <Logomark> / <Logo> now render the real brand artwork. The glyph was a 2-quadrant pink+yellow approximation (6 polygons, off-brand hexes); it is now the canonical four-quadrant spark (pink · yellow · green · blue, 12 polygons from 02-icon). The wordmark was plain <span>magicblocks</span> display-font text; it is now the designed "magicblocks" logotype, inlined as a currentColor SVG so it follows the lockup colour (variant="dark" → paper). Component API unchanged — <Logomark> / <Logo size variant> are drop-in; only the rendered SVG changed. Anything composing <Logo> (WorkspaceShell, SidebarHead, TopNav brand slots) inherits the fix.

Docs patch (lockstep with @magicblocksai/css@3.1.1, no CSS change).

Docs

  • WorkspaceShell.md — new "Composing real NextGen screens" section: worked, typecheck-verified examples for every screen shape (dashboard · list · master-detail · full-bleed agent builder).

> **Brand-kit *site* polish (not this package):** the old-chapter-URL redirects > were upgraded from meta-refresh stubs to Cloudflare _redirects 301s, and the > shell chapter's stale source comments were tidied to the new numbering.


v3.1.0 — 2026-06-03

NextGen WorkspaceShell + the inner-section contract. A strong, beautiful, decoupled operator app shell, built from a consistent primitive kit. Fully additive — nothing removed, no defaults changed. Lockstep with @magicblocksai/css@3.1.0. App-team Round 19.

Added — the shell

  • <WorkspaceShell> — batteries-included shell composing the bare <AppShell> with a fully-assembled collapsible rail (brand · workspace switcher · grouped nav · settings · theme · user) + an optional compact top bar. Owns the collapse toggle; sidebar-first (the rail carries settings/theme/profile, so the top bar is opt-in). The inner section is just children — any view.
  • The rail primitive kit (promoted from chapter-private chrome): <SidebarHead>, <WorkspaceSwitcher>, <WorkspaceNav> + <WorkspaceNavSection>, <WorkspaceUser>, <WorkspaceThemeToggle> (in-rail light/dark).
  • <WorkspaceBar> — the kit's first operator top bar (optional; marketing <TopNav> stays separate).

Added — the inner-section contract

  • <SectionView> — the thin consistent frame (pinned header · scrollable gutter'd body · optional footer) every standard section composes.
  • <SectionHeader> — the canonical section header (back · eyebrow · title · subtitle · actions · optional tabs). The default standard-section header.

Reconciliation (guidance only — no breaking changes)

  • <SectionHeader> is the canonical default; <PageHeader> (editable title + metadata), <RoomHeader> (content hero), <RoomTabs> (standalone tabs) are kept for their distinct capabilities, with "when to use which" docs.
  • <DashboardShell> doc rescoped as an *embedded marketing preview* (not the app shell — use <WorkspaceShell>).
  • <AgentBuilderShell> documented as one inner section mounted in <WorkspaceShell> — it does not own the app chrome.

> Bare <AppShell> is unchanged — Spark CRM + the marketing website that > consume it directly are unaffected.

> **Brand-kit *site* note (not this package):** brand.magicblocks.ai chapters > were renumbered into a clean bucket-contiguous order (Operator → 15–24); old > chapter URLs redirect to the new ones. The chapter HTML isn't shipped in this > package, so it's a site edition, not a package change.


v3.0.0 — 2026-06-03

Lockstep major with @magicblocksai/css@3.0.0 — the warm surface token ladder. No TSX/API change in this package; the breaking change is in the shared stylesheet (body / AppShell light-mode page canvas lightens --warm-3--warm-1; --bg insets shift --warm-3--warm-2). See the css changelog for the full token table + migration. Dark mode is unchanged.

> Why a major with no code change? The kit's css + ui packages are lockstep-versioned, and the css change alters a default (page background), which is breaking for anyone inheriting it. The ui version tracks css so resolvers stay aligned.

Component behaviour

  • <AppShell> main column now defaults to the recessed warm canvas (--bg-canvas = --warm-1) instead of paper. pageWash is unchanged as an API (now a no-op in light, still meaningful in dark). Consumers who set pageWash for the recessed look can drop it.
  • <AgentBuilderShell> canvas reads --warm-1; the edit pane stays --warm-2 — the white-cards-on-heavy-warm surface mix (Round 17 #1/#4 root cause) is resolved.

v2.4.0 — 2026-06-03

App-team Round 17 — agent-builder shell polish (empathy pass). Lockstep with @magicblocksai/css@2.4.0. Operator-scoped — no token or page-background change; the marketing website is unaffected.

Added

  • <AgentBuilderHead density="default" | "compact"> — compact tightens padding + shrinks the back button / action pills so the topbar reads ~44px instead of ~64px (frees space above the fold). Default unchanged.

Changed

  • .ab-job rests transparent on-pane (hairline outline) instead of a --bg-paper white fill — harmonises with the warm pane + the re-skinned rooms. The --bg-paper lift returns on hover / drag as a grab cue.
  • Journey rail group sits flush on the rail surface — the soft accent-wash gradient behind the blocks was removed (it read as a stray panel). .ab-rail-block name left-align made explicit (consistent with .ab-rail-item).

> The app-wide warm surface token ladder (root cause of "pane reads heavy" / white-card jobs) is not in this release — it re-points global --bg + the body background, which the marketing website inherits. Held for a separate decision (see APP-FIXES-LOG Round 17).

v2.3.0 — 2026-06-01

App-team Round 16. Lockstep with @magicblocksai/css@2.3.0.

Added

  • <ActionPicker layout="grid" | "dropdown"> — a "dropdown" layout that swaps the 4-col card grid for a searchable, descriptive <Combobox>: type to filter, each option reads icon · name · one-line description (from the action registry). For long / growing action sets. Default stays "grid" (back-compat); off-channel types are omitted from the dropdown (the grid still shows them disabled). Composes the kit's <Combobox> renderOption — no new primitive.

v2.2.0 — 2026-06-01

App-team Round 15. Lockstep with @magicblocksai/css@2.2.0.

Added

  • <ActionList> items take an optional meta (ActionListItem.meta?: ReactNode) — a dim secondary line under the name (e.g. a trigger summary like "Fires every turn" / "2 conditions"), so the agent-builder's action list shows *what fires when* without clicking each row. Mirrors SourceRow's meta; single line, ellipsised; omitted when absent (rows without meta render unchanged).

v2.1.1 — 2026-06-01

Follow-up fixes (app-team Round 14). Lockstep with @magicblocksai/css@2.1.1.

Fixed

  • <TimeSeriesChart> SSR hydration #418 (definitive). The default x-axis formatter no longer calls toLocaleDateString at all — that's ICU/locale/timezone-dependent, so a minimal-ICU Node server can format differently than the browser even pinned to en-US/UTC (the 2.0.1 mitigation). It now builds "Mon D" from a fixed month table + getUTCMonth()/getUTCDate() — identical on server and client. Pass xFormat for locale-aware labels.

Changed

  • <ChatComposer> disabled-state helper text lifts from faint → soft (--fg-soft) so the "why is this off" message stays legible while the input row is dimmed.

v2.1.0 — 2026-06-01

App-team backlog sweep (Round 13). Lockstep with @magicblocksai/css@2.1.0.

Added

  • <DashboardTile maxHeight> — cap the body for long feeds (latest sessions / missing knowledge). The body scrolls (overflow-y: auto) while the header + footer stay put. Accepts a number (px) or any CSS length.

> The rest of the Next Gen backlog was already shipped in earlier releases (RoomTabs, RoomHeader, SubNav, SourceRow, PageHeader backLink/metadata, DashboardNavGroup mode, SageDrawer onExpand/headerActions, KpiTile density/href, Sparkline tone, LifecyclePill custom, Button/Badge asChild, Card fill="danger", Tabs variant="text", the full lucide-replacement icon set) — no further changes needed. OptionCardGrid is covered by the existing <RadioCardList columns>.

v2.0.1 — 2026-06-01

Bug fixes from the Next Gen agent-builder (app-team Round 12). Lockstep with @magicblocksai/css@2.0.1.

Fixed

  • <ChatMessage> no longer collapses to ~1 character wide in a narrow container (e.g. a ~340px test rail). The grid bubble track is now minmax(0, 1fr) so text wraps at the container width instead of the bubble's min-content.
  • <AgentBuilderHead version={…}> accepts a block-level node (e.g. <VersionSwitcher>) without invalid SSR nesting. A string/number still renders in the .ab-version pill; a node renders inline as-is (no <span> wrapper → no <span><div> hydration error #418).
  • <TimeSeriesChart> default x-axis formatter is now SSR-deterministic (pinned en-US + timeZone: "UTC"), fixing the server/client hydration mismatch from the ambient-locale toLocaleDateString. Pass formatX for locale-aware labels.

Changed

  • Agent-builder rail glyphs bumped 14px → 16px (.ab-rail-item + .ab-rail-group glyphs) to read better next to the 13px labels.
  • Reverted the v1.72.0 builder-pane (.ab-pane) warm gradient back to a flat --bg fill — surface backgrounds stay flat (operator preference).

v2.0.0 — 2026-06-01

Breaking: the <AppShell> sidebar now defaults to paper/white. Lockstep with @magicblocksai/css@2.0.0.

Changed (breaking)

  • <AppShell sidebarTone> default flipped "warm""paper". Every AppShell sidebar is now white/paper by default (a crisp rail against the warm main column). The warm-cream pin (the v1.x look) is now opt-in.
  • <DashboardShell> sidebar (.dash-side) now defaults to paper/white too (was pinned warm-cream), so app + dashboard chrome share one uniform white-rail scheme. Pin it warm again via the --dash-side-bg CSS var.

Migration

  • Want to keep the v1.x warm-cream sidebar? Pass sidebarTone="warm": ``tsx <AppShell sidebarTone="warm" sidebar={<MyNav />}>…</AppShell> ``
  • No code change needed to adopt white — it's the new default.
  • --app-shell-side-bg still overrides either tone with a custom colour.
  • Unaffected: <DashboardShell> / .dash-side is a separate primitive and keeps its warm sidebar.

v1.72.0 — 2026-06-01

Agent builder polish — paper sidebar option + cleaner active-block treatment. Lockstep with @magicblocksai/css@1.72.0.

Added

  • <AppShell sidebarTone>"warm" (default, back-compatible — the warm-cream pin) or "paper" (white/paper rail against the warm main column). Emits data-sidebar-tone="paper" only when set to "paper", so existing AppShell usages render unchanged. Overridable per-instance via the --app-shell-side-bg CSS var. App-team request 0.

Changed

  • The agent builder's active rail block (.ab-rail-block.is-active) drops its box-shadow halo for a clean accent border + soft fill (no glow).
  • The builder pane (.ab-pane) gains a gentle top-down warm wash (echoing the Journey group's accent gradient) instead of a flat fill.

v1.71.0 — 2026-06-01

Agent builder follow-up — <JobCard>s reorder within the Jobs-to-Do tab. No TSX change (a JobCard is already a valid <SortableList> renderRow body, exactly like <RailBlockItem>); the drag affordance was a missing-CSS gap, now shipped in @magicblocksai/css@1.71.0. Lockstep bump.

Changed

  • Docs + chapter 18.7 demos now wrap the pane's <JobCard>s in <SortableList> to show whole-row drag-reorder (the ·· handle is the affordance).

v1.70.0 — 2026-06-01

App-team Round 8 — the agent builder shell family (chapter 18.7). The rail + pane builder layout — ~100 chapter-private ab-* rules with no shipped component — is now packaged as 11 composable primitives. Additive. Lockstep with @magicblocksai/css@1.70.0.

Added

  • <AgentBuilderShell> — the two-pane builder layout (head + rail slots over a pane child) with a drag-to-resize boundary (--ab-rail-w, clamped 240–360px; drop-in uncontrolled, or controlled via railWidth / onRailWidthChange; ←/→ on the focused handle). Mount inside <AppShell>; rail + pane only (the testing chat lives outside). Compound member AgentBuilderShell.Pane (AgentBuilderPane) — the edit pane with head / foot slots.
  • <AgentBuilderHead> — builder topbar: back · name + type chip · version + unsaved meta · trailing Share / Test / Save pills.
  • <BuilderRail> · <RailGroup> · <RailItem> · <RailBlockItem> — the rail scroll container, collapsible sections, leaf nav rows, and draggable Journey-block rows. RailBlockItem is the renderRow body of a <SortableList> (drag-reorder is the already-shipped primitive).
  • <PaneHeader> · <PaneTabs> / <PaneTab> · <JobCard> — pane header (collapse · glyph + title + badge · description · tabs), underline-active tab strip (roving tabindex; drop-in or controlled), and general / collect job rows.

Notes

  • DOM byte-verified by three composition fixtures (AgentBuilderHead, BuilderRail, AgentBuilderShell.Pane) that transitively render every leaf; the resizer shell + leaves carry documented skip-fixtures.
  • Polish: keyboard :focus-visible rings on every interactive builder control (a11y; DOM-neutral).

v1.69.0 — 2026-05-31

App-team Round 7 — actions wizard config system Phase 3: recursive sub-actions, the Run Task panel, and the wizard's Goal step. The wizard is now complete end-to-end. Additive (the unknown sub-action placeholders become typed Action references). Lockstep with @magicblocksai/css@1.69.0.

Added

  • <SubActionField> — the recursive nested-action editor. A button's tap action and a form's submit action are themselves Actions, edited by a restricted <ActionPicker> + a nested <ActionConfigPanel>. The allow-list excludes add_buttons, so the recursion always terminates. Exports SUB_ACTION_TYPES (button set) + FORM_SUBMIT_ACTIONS (form set — no nested forms; allows Human Takeover). <ActionPicker> gains an allow?: ActionType[] prop.
  • <RunTaskActionConfig> — Run Task graduates from declarative fields to a custom panel: a Select | Custom toggle (saved prompt from context.prompts xor custom instructions) + a functions <MultiSelect> (context.functions). RunTaskAction gains promptId? / functionIds?.
  • <GoalStep> — the wizard's Step 3: a goal <Select> (context.goals) + a conditional $ value (<Input type="number">). New GoalConfig type.
  • Nested-action dataButtonsAction.buttons[].action?: Action and FormsAction.submitAction?: Action (were unknown placeholders). ActionConfigContext gains prompts / functions / goals.

Changed

  • Add Buttons / Add Forms panels now edit their nested sub-actions (each button row expands to a <SubActionField>; forms gain an on-submit <SubActionField>).
  • Run Task is now a registry Panel (was declarative fields).

Notes

  • Conditions (Step 2) needs no new component — <QueryBuilder> / <ConditionRow> (chapter 18.1) are the conditions builder, demoed at 18.12 Demo 2. Step 2 + Step 3 complete the 3-step wizard.
  • Grounded in v1's director-action-content.tsx / experience-buttons.tsx / select-task.tsx / director-goal.tsx. The new components compose stateful primitives and ship fixture-less per kit precedent; four-surface is met. Deferred: multiple form-submit actions (the kit ships a single submitAction); Switch Journey SMS-sender provisioning.

v1.68.0 — 2026-05-30

App-team Round 6 — actions wizard config system Phase 2: every remaining action type now has a working config surface. Four declarative types, four custom panels, plus the config-less Do not respond. Additive (the 8 placeholder types become fully-typed variants; PendingAction is retained as a deprecated alias). Lockstep with @magicblocksai/css@1.68.0.

Added

  • Four custom config panels for the conditional/composed action types: - <SwitchJourneyActionConfig> — block ↔ channel cascade (<Select> block picker with a "Current Block" sentinel · a Switch-Channel <Switch> revealing a channel <Select> and, for SMS, a sender <Select>) + a follow-up section (Send message / Auto + <MessageField>) shown only while staying on the current block. - <ButtonsActionConfig> — two <MessageField>s (before / after) bracketing an inline button-label repeater (add / remove, ≤50 chars). - <FormsActionConfig> — form <Select> + before / submit / privacy <MessageField>s. - <HumanTakeoverActionConfig> — follow-up <Select> (None / Slack) revealing a Slack connection <Select> + Channel ID <Input>; a Staff-Availability <Select> (default "Always"); transfer + out-of-hours <MessageField>s.
  • number field kindActionFieldSpec gains number<Input type="number"> (Embed height; reusable for delays / limits). The four declarative types (End Chat, Opt-Out, Run Task, Embed) wire purely through the registry's fields — no bespoke components.
  • Typed variantsSwitchJourneyAction, EndChatAction, RunTaskAction, ButtonsAction (+ ActionButtonItem), FormsAction, OptOutAction, EmbedAction, HumanTakeoverAction (+ SwitchFollowUpKind, HumanTakeoverFollowUp), and SelectOption are now exported. ActionConfigContext gains journeyBlocks / switchChannels / smsSenders / forms / slackConnections / availabilityProfiles.

Changed

  • The agent-builder registry now wires all 13 action types (was: 4). Adding a typical type remains a one-entry change.

Deprecated

  • PendingAction — every action type now has its own variant. Retained as a type alias of the real variants so existing imports resolve; prefer the concrete types.

Notes

  • Field-for-field grounded in v1's experience/action.ts. The recursive per-button / form-submit sub-actions (v1 actionButton.action / form.submitActions), Run Task's prompt-library reference + functions, and Conditions/Goal componentisation stay deferred to Phase 3. The four new panels compose stateful primitives (<MessageField> / repeater / conditional reveals) and ship fixture-less per kit precedent; four-surface (chapter HTML + _shared.css + TSX + React tab) is met.

v1.67.0 — 2026-05-30

App-team Round 5 — agent-builder actions wizard config system (Phase 1). The Actions wizard (chapter 18.12) is componentised, and selecting an action now reveals its config panel — driven by a declarative action registry. Additive; lockstep with @magicblocksai/css@1.67.0.

Added

  • Schema-driven action config. A declarative actionRegistry (exported) is the single source of truth for the agent-builder's 13 action types. <ActionConfigPanel action onChange context> renders the selected action's config — a custom Panel if the type declares one, else its declarative fields (each via <ActionField> → the matching kit primitive), else the type's description. Adding a typical action type is a one-entry change. Exported types: Action (+ variants), ActionType, ActionFieldSpec, ActionTypeSpec, ActionRegistry, ActionConfigContext, MessageRef, CalendarProvider.
  • <ActionWizard> — the 2-column wizard shell (actions list · pane), slot-based + presentational.
  • <ActionList> — the actions mini-column (count · rows · New Action).
  • <WizardSteps> — numbered Action · Conditions · Goal step indicator (active / done / todo).
  • <ActionPicker> — registry-driven action-type tile grid + detail card; disables off-channel types ("Website only").
  • <ActionConfigPanel> + <ActionField> — the generic config renderer (FieldSpec<Input> / <Select> / <Switch> / <DurationField> / <MessageField>, with declarative visibleWhen).
  • <MessageField> — snippet-or-custom message editor (Select | Custom). The kit's most-reused config row.
  • <CalendarActionConfig> — the custom Add Calendar panel: provider + embed type + account/event, plus the MagicBlocks→Calendly autofill <FieldMapper> with locked Name/Email rows.

Phase 1 wires Auto, Add Link, Send Message, Add Calendar; the remaining nine types carry label/icon/description and render their description until their config ships in a later batch. Chapter 18.12 reworked onto the new components.

Notes

  • Markup-equivalence fixtures for the new interactive/composition components are deferred (verified via the chapter demo + browser); the four-surface rule — chapter HTML + _shared.css + TSX + React tab — is met for all eight.

v1.66.0 — 2026-05-30

App-team Round 4 — agent-builder shell unify. Chapter 18.7's agent-builder shell now runs on the canonical <AppShell> instead of a bespoke duplicate, and the collapsed icon-band it pioneered is promoted into the kit. Additive; lockstep with @magicblocksai/css@1.66.0.

Added

  • <WorkspaceNavIcon> — the 36×36 workspace-sidebar nav item for <AppShell>'s collapsed icon-band. icon + label (drives the collapsed-mode slide-out tooltip + aria-label + the expanded text), optional count, active (accent leading-stripe + aria-current="page"); renders <a> when href is set, else <button>. Declarative — no 'use client'. Demoed in chapter 13.5.

Changed

  • <AppShell sidebarMode> — the collapsed mode now renders the full icon-band visual (56–64px rail, centred 36×36 items, slide-out tooltips, accent active-stripe, expanded labelled rail), promoted from the chapter-18.7 reference. Opt-in + back-compat: every existing <AppShell> without sidebarMode is byte-for-byte unchanged (all new rules key off [data-sidebar-mode]). New default sidebarWidthCollapsed="56px" matches the reference look. The sidebar header / workspace-switcher / user-menu chrome (.app-shell-side-head, .ws-switcher*, .ws-user*) ship as documented shared CSS you compose into the sidebar slot; the collapse toggle stays consumer-owned. Chapter 13.5 gains a collapsed-shell demo.

Notes

  • The agent-builder shell (chapter 18.7) is rebuilt on <AppShell sidebarMode="collapsed"> + <Logo> + <WorkspaceNavIcon> — its bespoke workspace-rail is gone, and the placeholder logo is now the real <Logomark>. Its outer-shell React tab is now real; the inner builder pane stays a vanilla demo pending the deferred BuilderShell family (.scratch/app-screens-component-gaps.md).

v1.65.0 — 2026-05-30

App-team Round 3 — the P2/P3 audit + design-question backlog from docs/brand-kit-requests.md, plus three agent-builder-room primitives. All additive; lockstep with @magicblocksai/css@1.65.0.

Added

  • <RoomHeader> (#22) — the three-line room hero (eyebrow · display heading · lede) that opens every agent-builder room. Thin composition of <Eyebrow> + <Heading display> + <Lede>; level sets the heading rank (default 2, since rooms sit under the page <h1>); title is a ReactNode so it can carry an <em> flourish. No actions slot — page actions live on <RoomTabs trailing>. Chapter 13.20. Declarative (no 'use client').
  • <SourceRow> (#25) — resource row: leading tinted <IconChip> + name + metadata sub-line + status slot + trailing slot. The canonical "list of connected sources you can see the health of and toggle" layout — knowledge collections, MCP tools, channels. Presentational: drop a <Chip> into status, a <Switch> into trailing. Chapter 7.32.

Changed

  • <RadioCardList layout="grid"> (#23) — new layout ("list" | "grid", default "list") + columns (default 2). The grid variant arranges the existing native-radio cards in an N-column grid via a --rcl-cols custom property. Resolves the app team's "OptionCardGrid" ask as a variant of the existing primitive — same radio semantics, same keyboard contract — rather than a parallel component.
  • <ChatTranscript centerEmptyState> (#10) — new boolean; vertically centres the empty state in the scroll viewport, for welcome / zero-history scenes.
  • <Sparkline tone> (#12) — new tone ("success" | "danger" | "info" | "warning"); tints the line via a data-tone hook, so a sparkline carries semantic colour without a per-instance color.
  • <LifecyclePill custom> (#14) — new custom={{ label, tone }} escape hatch for workspace-defined stages outside the 17-value taxonomy (tone maps to the kit's existing badge/pill tints). value is now optional — supply one of value / custom.
  • <KpiTile href> (#15) — new href; when set, the whole tile renders as an <a> drill-down with .is-clickable hover/focus affordances. forwardRef retargeted to HTMLElement to cover both the <a> and <div> branches.
  • <TimeSeriesChart defaultPalette="brand4"> (#16) — new defaultPalette; "brand4" auto-assigns the four brand series colours (pink · yellow · blue · green) by index to any series without an explicit color.
  • <ChatComposer> disabled (#9) — only the input row dims now; helperText stays at full opacity and readable (pointer-events: none still covers the whole composer). CSS-only.

Audited — no change

  • <Avatar> dark mode (#7) — uses an explicit tone, not a name-hash palette; no hash-vs-dark-ground clash exists to fix.
  • <DataTable striped> in <SectionCard> (#8) — the v1.29.0 SectionCard chrome-strip already removes the inner radius, so striped tints don't clip the card corners.
  • <Heading> <em> accent (#24) — confirmed: the accent italic renders correctly inside headings.

Deferred

  • Tabs cross-route deep-link styling (#11) · DashboardTile sticky/expandable (#13) — later round.

v1.64.0 — 2026-05-29

Accessibility batch (website BRAND_KIT_REQUESTS.md R033/R035; lockstep with @magicblocksai/css@1.64.0).

Changed

  • <RevenueCalculator> (R033) — the four conversion-rate range sliders now associate <label><input> via htmlFor/id. No API change.
  • <SiteFooter> (R035) — column titles render as <h2> (was <h5>), fixing a heading-order skip on marketing pages. Styling unchanged; consumers who restyled footer headings via an h5 element selector should switch to h2.

R034 + R036 are CSS-only — see @magicblocksai/css@1.64.0.


v1.63.0 — 2026-05-26

Per-section LEGO. Four cross-page primitives extracted from the Platform-Tour Operator pages — the smaller composable bricks that sit inside the page-shell pieces shipped in v1.62.0. All additive.

Data display (composed in chapter 7.10)

  • New <StatTile label value delta direction> — the kit's canonical KPI tile (chapter-07 .stat-tile). Three variants: default (24px value), big (36px), moment (ink-on-accent for the one wow number per page). Optional spark slot for an absolutely-positioned sparkline. Sibling to <StatCard> (chapter-05 .stat-card shoulder card) — reach for <StatTile> for KPI strips, <StatCard> for the card-on-warm chrome.

Agent surfaces (composed in chapter 18.8 agents-list cards)

  • New <AgentChannelStrip label> + <AgentChannelIcon active title> — multi-channel icon chip strip. Indicates which channels (web / SMS / email / voice / WhatsApp) an agent supports, a contact has been reached on, or a campaign touches. .is-on chips switch to the accent-soft / accent fill. The underlying CSS (.ag-card-channels, .ag-channel-icon) promoted from chapter-18-inline to shared.

Settings surfaces (composed in chapter 25.7 settings/credits)

  • New <PackageGrid label> + <PackageOption selected> — radio-style "pick one amount / tier / size" grid. Six columns desktop, three tablet. Originally the top-up amount picker; generalises to plan tiers, file-size limits, retention windows. CSS (.st-package-grid, .st-package) promoted from chapter-25-inline to shared.

Trace / RAG surfaces (composed inside <TraceTimeline>, now exposed standalone for citation cards)

  • New <TraceItemCard tone label value meta details defaultOpen> — the expandable card from the trace-item family, previously rendered privately inside <TraceTimeline>. Exposed as a standalone export for citation cards inline in knowledge-base pages, retrieval-debug surfaces, and "what was matched?" affordances outside of a timeline. Read-only when details omitted; interactive <button aria-expanded> when provided.

Three React snippet tabs rewritten to demonstrate composition with the new pieces: 7.10 (StatTile), 18.8 (AgentChannelStrip + the v1.62.0 ListScreen* pieces together), 25.7 (PackageGrid). Each fixture for the layout-glue components skips markup-equivalence with documented rationale — same pattern as v1.62.0.

Two CSS surface-region updates require surface-manifest.txt regen (handled in-commit): the agent-channel-strip + package-grid blocks both land in the operator surface.


v1.62.0 — 2026-05-26

Page-shell LEGO. Eight layout primitives extracted from the Platform-Tour Operator pages (Contacts list, Campaigns list, Library, Agents list, Settings / Credits, Channels / Chat Appearances). All additive.

List-screen primitive (composed in chapters 14.15, 14.17, 15.12, 18.8, 19.7)

  • New <ListScreen frame> — the page-shell container with the kit-wide .list-screen-frame outer card + inner .list-screen flex column. frame={false} opts out for nesting inside another framed surface.
  • New <ListScreenHead> — title + description + actions row. Takes title / description / actions props or a children escape hatch.
  • New <ListScreenKpiStrip> — 4-up KPI grid container. Body is whatever KPI shape the section uses (<KpiTile>, <StatTile>, custom cards).
  • New <ListScreenTabs> + <ListScreenTabCount> — the status-filter tab row plus the count chip rendered inside each tab label. Consumers pass raw <button> children for routing / click control.
  • New <ListScreenFoot centered> — page footer. Default layout is pagination (justify-between + top border); centered switches to the "Showing N of N" summary used by card-stack lists.

Sub-nav primitive (composed in chapters 13.19, 15.12, 25.7)

  • New <SubNav> — vertical icon+label column container. Flex-column layout; parent owns the surface chrome (bg / padding / border).
  • New <SubNavItem glyph count active href> — single nav row. Optional 18px glyph slot, optional count chip, active accent-soft fill with a 3px accent left-stripe.
  • New <SubNavLabel> — uppercase mono caption above a cluster of related <SubNavItem>s.

Each fixture skips markup-equivalence with a layout-primitive rationale — these are composed inside larger chapter demos rather than rendered standalone (same pattern as <BillingPage>, <WorkspaceMembersPage>, <SettingsShell>).


v1.61.0 — 2026-05-25

App team Round 2. All changes are additive.

Brand marks (chapter 1.10)

  • New <Logomark> — the canonical 4-colour spark glyph (SVG inlined from 06-brand-assets/magicblocks-social-avatar-square-512.svg).
  • New <Logo> — Logomark + "magicblocks" wordmark lockup; size (sm / md / lg) + variant (default / dark).

Shell + cross-route

  • <AppShell sidebarMode> — controlled ("expanded" / "collapsed"). When set, takes precedence over sideWidth; the kit propagates the value via useSidebarMode() context.
  • <AppShell sidebarWidthExpanded> / sidebarWidthCollapsed — string CSS lengths (defaults "200px" / "64px").
  • <DashboardNavGroup mode> — optional override (auto-defaults from context). In collapsed mode hides the label and stacks items icon + small label.
  • New useSidebarMode() + SidebarMode from the main barrel.
  • New <RoomTabs> (chapter 6.16) — top-of-page section tabs with per-tab icon + count chip + trailing slot + optional sticky. Roving tabindex + arrow-key activation.

Component extensions

  • <PageHeader backLink>{ label, href } link above the eyebrow.
  • <KpiTile density="dense"> — fits 5+ tiles in a row at narrower widths.
  • <Tabs variant="text"> — underline tab list only (no panels).
  • <Card fill="ink"> — dark editorial surface.
  • <FilterChipGroup required> — blocks empty-selection in single-select.

Icons — 22 new (chapter 22.3):

  • 18 P1: Bell, Sun, Moon, MessageCircleQuestion, ExternalLink, Heart, MoreVertical, Download, Upload, Globe, MessageSquarePlus, Flask, TrendingDown, UserCog, ArrowRight, Smartphone, MessageSquare, Code.
  • 4 P3 chevrons: ChevronDown, ChevronUp, ChevronLeft, ChevronRight.

P3 components

  • New <IconChip> (chapter 7.30) — tinted icon-in-square primitive with six tones.
  • New <RateCard> (chapter 7.31) — metric + tonal progress card with optional 4 px tinted bar.

Lockstep with @magicblocksai/css@1.61.0.


v1.60.0 — 2026-05-24

App team Round 1 — first round serving the v2 / Next-Gen app. All changes are additive.

  • New <RouteProgress> (chapter 13) — indeterminate route-load bar with tone / position / height props and a prefers-reduced- motion fallback.
  • New <LinkTabs> (chapter 6) — cross-route tab variant. Framework- agnostic via a linkAs prop; pass RR7's NavLink (or equivalent) to get automatic active-state styling (kit styles .tab.active and .tab[aria-current="page"]).
  • New <Slot> lib primitive — Radix-style composition. Merges className (composes), event handlers (chained slot→child), refs (composed). Powers the new asChild props on <Button> and <Badge>; available to consumers authoring their own asChild-shaped components.
  • <Button asChild> + <Badge asChild> — wrap a router <Link> and inherit the component's styling. Lets <Button asChild><Link …/> keep prefetch / middle-click / cmd-click.
  • <SageDrawer> — new onExpand callback (kit renders an Expand icon button when set) + headerActions slot (consumer-owned, beside the wordmark).
  • <PageHeader metadata> — optional horizontal metadata strip rendered below the summary.
  • <Card fill="danger"> — soft danger-zone surface (extends the existing paper / sunk / warm enum).
  • New <ExpandIcon> — drawn for <SageDrawer onExpand>. The broader 26-icon backlog from the app-team request list ships in a dedicated future round.
  • Onboards the MagicBlocks app team (v2 / Next-Gen) as the kit's 4th named consumer — close-out log at APP-FIXES-LOG.md.

Lockstep with @magicblocksai/css@1.60.0.


v1.59.2 — 2026-05-23

  • R001 (reopened) — the ESM build now emits one file per component instead of a single bundled dist/index.js. "sideEffects": false (1.59.1) was inert against a single-module bundle; with real module boundaries restored, a consumer importing a subset of the barrel tree-shakes the rest away — a Button-only import drops from ~1 MB to ~71 KB. CJS output is unchanged (single-file). No public API change. Closes Spark R001.

Lockstep with @magicblocksai/css@1.59.2.


v1.59.1 — 2026-05-22

  • R001 — declare @magicblocksai/ui side-effect-free ("sideEffects": false in package.json). The package has no import-time side effects — no CSS imports, no global mutation, pure re-export barrels — so a consumer's bundler can now tree-shake unused exports instead of pulling the whole component set when importing from the barrel. No code or API change. Closes Spark request R001.

Lockstep with @magicblocksai/css@1.59.1.


v1.59.0 — 2026-05-22

RichTextEditor v2.

  • Add <RichTextEditorPro> — a TipTap-backed rich text editor with a / block palette, a generic @ mention hook, {{ }} merge tags, known-platform video embeds, and inline media (paste / drop / upload). Exported from the new @magicblocksai/ui/editor subpath so consumers importing @magicblocksai/ui never bundle TipTap.
  • Add <RichTextContent> — a light, TipTap-free, server-renderable read-only renderer for stored rich-text HTML.
  • The HTML sanitiser is now a shared lib primitive, rewritten on node-html-parser so it sanitises identically in the browser and on the server. <RichTextEditor> uses it — behaviour unchanged.
  • Closes Spark request R15-1.

Lockstep with @magicblocksai/css@1.59.0.


v1.58.0 — 2026-05-22

  • R001 / R002 — 11 new navigation icons for CRM sidebars: TagIcon, PercentIcon, FilterIcon, GaugeIcon, TrendingUpIcon, MailboxIcon, ChecklistIcon, LeadsIcon, OverviewIcon, AgentsIcon, ConversationsIcon.
  • Version bumped in lockstep with @magicblocksai/css 1.58.0.

v1.57.0 — 2026-05-20

Lockstep release — no @magicblocksai/ui code changes.

The substance of this version is @magicblocksai/css 1.57.0 (R030): _shared.css is now partitioned into per-surface CSS bundles (/core, /marketing, /operator, /embed) alongside the existing full-kit /tokens and default exports. Consumers who want only the CSS relevant to their surface can import the matching subpath instead of the full bundle.

Lockstep with @magicblocksai/css@1.57.0.


v1.56.0 — 2026-05-20

Round D · Plan 2 — Visualisations.

  • Add <JourneyGraph> — agent reasoning visualisation (chapter 20.6, Explainability). Node-and-edge SVG; controllable selection; consumer-supplied geometry; render-prop detail slot. Escape key deselects.
  • Add <Evaluations> — scored-rubric display (chapter 15.10, AI Surfaces). Criterion list with pass/fail/warn status, score readouts, per-criterion notes, controllable expansion. Summary auto-derived from criteria when not supplied.
  • Add <ReviewerInbox> — human-in-the-loop review queue (chapter 14.14, Pipeline & CRM). Filter chips + controllable selection + per-row Approve/Reject/Edit affordances (keyboard-accessible). ↑/↓ keyboard nav via useKeyboardListNav.

Closes Round D component shipping (six components total across Plans D1 + D2).


v1.55.0 — 2026-05-20

Round D · Plan 1 — Chapter 23 trio backfill.

  • Add <VoicePlayer> — audio playback chrome for voice agents (chapter 23.2). Compact + expanded variants; controllable playback state; honours prefers-reduced-motion for autoplay.
  • Add <WidgetPersonaSwitcher> — visitor-facing persona switcher (chapter 23.3). Cards + header variants; collapses to vertical stack at narrow widths.
  • Add <ChannelSandbox> — multi-channel widget preview shell (chapter 23.1). Tab strip over preview pane; Web (composes <WidgetShell>) / SMS (3 bubble row) / Voice (composes <VoicePlayer expanded>) channels.

Replaces the Phase-4 wireframe placeholders in chapter 23 with shipping TSX. Planned-props tables published in earlier versions honoured verbatim. Chapter 23 eyebrow + lede + side-rail entries refreshed to reflect the trio now shipping.


[1.54.0] — 2026-05-19

Minor — Round C Plan 4: Integrations cluster. Five new operator-facing components forming chapter 26 (Integrations). Closes Round C — the operator-chrome arc — alongside the kit-website v3.0 edition mark.

Added

  • <IntegrationCard> — single integration tile: logo + name + description + status pill (Connected / Disconnected / Error / Pending) + per-card action. Declarative.
  • <IntegrationsGrid> — wrapper with optional category filter chips above a responsive grid of <IntegrationCard> children. Uses useControllableState for filter state.
  • <WebhookConsole> — tabular layout of recent webhook deliveries: timestamp + endpoint + event + status (2xx/4xx/5xx colour-coded) + duration.
  • <OAuthCallbackBanner> — post-auth result banner with three intents (success / failure / pending) using --success-* / --error-* / --warning-* tokens.
  • <IntegrationsPage> — page-shaped wrapper composing the above with optional banner at top, integrations grid in the middle, and webhook console at the bottom.

Chapter

  • Chapter 26 — Integrations (new). Operator bucket. Five sections (26.1–26.5). Page-nav routes 25 Workspace → 26 Integrations → 17 Chat Widget — closes back to the Embed bucket. Chapter 17's page-nav prev rerouted from chapter 21 to chapter 26.

Quality bar

Every new component visually verified at 1280/720/480/360px viewports in both light + dark modes, with keyboard navigation + focus rings + ARIA semantics confirmed before commit.

Lockstep with @magicblocksai/css@1.54.0.


[1.53.0] — 2026-05-19

Minor — Round C Plan 3: Workspace cluster. Six new team-administration components forming chapter 25 (Workspace), plus a new exported role taxonomy.

Added

  • ROLE_TAXONOMY + RoleKey + RoleTint — exported constants from lib/roles.ts. Defines the kit's canonical role set (admin / editor / billing / viewer) with semantic tint mapping. Pattern matches WIDGET_SCHEMES (chapter 21) and HAPPA_STAGES (lib/happa.ts).
  • <RoleBadge> — role pill primitive. Renders <span class="role-badge role-badge--{tint}">{label}</span>. Declarative; takes a single role prop and looks up label + tint from ROLE_TAXONOMY.
  • <MemberRow> — team-member row inside <ul class="member-list">. Avatar + name+email stack + RoleBadge + lastActive + actions. Mobile reflow at ≤480px.
  • <InviteRow> — pending-invite row with email + role + invited-by meta + sent-at + Resend/Revoke action buttons.
  • <AuditLogEntry> — audit-log entry with actor + verb + target + timestamp + optional collapsible before/after diff panel. Local useState for expand toggle.
  • <TeamHeaderBlock> — page header for team pages: eyebrow + title (with inline member-count badge) + Invite CTA + optional filter rail.
  • <WorkspaceMembersPage> — page-shaped wrapper composing TeamHeaderBlock + member list + invite section + audit-log section. Drop-in for the common workspace-members shape.

Chapter

  • Chapter 25 — Workspace (new). Operator bucket. Six sections (25.1–25.6). Page-nav routes 24 Billing → 25 Workspace → 26 Integrations (chapter 26 reserved for Plan C4).

Quality bar

Every new component visually verified at 1280/720/480/360px viewports in both light + dark modes, with keyboard navigation + focus rings + ARIA semantics confirmed before commit.

Lockstep with @magicblocksai/css@1.53.0.


[1.52.0] — 2026-05-19

Minor — Round C Plan 2: Billing cluster. Six new operator-facing components forming chapter 24 (Billing), the kit's first new chapter since the v2.0 unification tag.

Added

  • <PlanCard> — plan-tier card with name + price + features + CTA. Three intensities (muted / accent / ink). Optional corner pill (Current plan / Recommended).
  • <UsageMeter> — labeled progress bar with current + limit + overage. Three states: healthy / approaching / over. Optional projection line for month-end forecast.
  • <InvoiceRow> — invoice list row: date + number + amount + status pill + download icon. Status states: paid / open / void / uncollectible. Used inside <BillingHistoryTable> or standalone.
  • <PaymentMethodCard> — card-on-file display: brand mark + last4 + expiry + actions. Brands: Visa / Mastercard / Amex / Discover / Generic.
  • <BillingHistoryTable> — composed table wrapping invoice rows with filter chip group + date-sort toggle.
  • <BillingPage> — page-shaped wrapper composing PlanCard grid + UsageMeter stack + PaymentMethodCard + BillingHistoryTable. Drop-in for the common billing-page shape.

Chapter

  • Chapter 24 — Billing (new). Operator bucket. Six sections (24.1–24.6). Page-nav routes 21 Designer Toolkit → 24 Billing → 25 Workspace (chapter 25 reserved for Plan C3).

Quality bar

Every new component visually verified at 1280/720/480/360px viewports in both light + dark modes, with keyboard navigation + focus rings + ARIA semantics confirmed before commit.

Lockstep with @magicblocksai/css@1.52.0.


[1.51.0] — 2026-05-19

Minor — Round C Plan 1: Settings cluster. Eight new operator-facing components extending chapter 13 (App Shell). The kit's first settings-page primitives + a page-shaped wrapper that composes them all together.

Added

  • <SettingsNavRail> — left rail navigation for settings pages. Groups items into labelled sections with optional icons + count badges. Active item carries aria-current="page" + accent highlight. Controlled via activeKey/onActiveChange, or drop-in with defaultActiveKey. Mobile (≤720px) reflows to horizontal-scroll so the rail stays usable on phones.
  • <SettingsHeaderBlock> — per-page header for settings pages. Eyebrow + title + description + actions row. Stateless; slot-driven.
  • <PreferenceToggleRow> — labeled row with a <Switch> on the right. Used 5–20× per settings page for boolean preferences. Composes the kit's existing <Switch> primitive.
  • <ApiKeyCard> — masked-by-default token with reveal + copy + revoke actions. Last-used metadata above. Used inside the API keys settings page.
  • <SessionList> + <SessionRow> — list of active sessions with device, location, last-active timestamp, and per-session revoke. Current session is marked and non-revocable.
  • <DangerZoneBlock> + <DangerZoneAction> — red-bordered footer for destructive actions. Takes children — consumers compose any number of action rows. Used on account, workspace, and integration settings pages.
  • <UnsavedChangesBar> — sticky bottom bar with Save + Discard actions. Tracks dirty state via controlled dirty prop — consumers wire form state (React Hook Form, Formik, plain useState). Portal-mounted so it floats above page scroll.
  • <SettingsAccountPage> — page-shaped wrapper composing <SettingsShell> + <SettingsNavRail> + <SettingsHeaderBlock> + body. Drop-in for the common API-keys / Profile / Sessions shape.

Quality bar

Every new component visually verified at 1280/720/480/360px viewports in both light + dark modes, with keyboard navigation + focus rings + ARIA semantics confirmed before commit.

Lockstep with @magicblocksai/css@1.51.0 (CSS for all eight components added to _shared.css).


[1.50.0] — 2026-05-18

Minor — Round CS-2: customer-success taxonomy unification. Follow-up to the v1.49.0 audit. Where v1.49.0 shipped the safe additive fixes and documented the deferred-design items, this round actually unifies the surfaces. No breaking changes — deprecated aliases preserve existing call sites.

Changed — <PipelineBar> default cleaned up

The zero-prop <PipelineBar /> default previously started at lead and used lead → qualified → negotiation → won → renewal. lead is a *lifecycle* stage on the contact, not a stage on the deal — deals don't exist until the lead is qualified into an opportunity. The aligned default now mirrors the <Kanban> zero-prop demo (qualified → discovery → negotiation → won) plus the trailing renewal so the bar still represents the full sales-to-renewal arc.

  • <PipelineBar> default: qualified (3d) → discovery (5d) → negotiation (Day 4, current) → won → renewal
  • Chapter §14.2 demo (data-mb-props + hand-rolled HTML + snippet tabs) updated to match
  • Production consumers passing their own stages are unaffected

Changed — <InboxRow state> completeddone (with back-compat alias)

<Checklist>'s ChecklistItemState has always used done for the "finished" state; <InboxRow state> used completed for the same concept. The two are now aligned on done — same word, same concept, same semantic.

  • New canonical value: state="done" (renders strikethrough + faded avatar, same as before)
  • state="completed" is now a deprecated alias — still accepted at the prop boundary, normalised to done before render. The emitted data-state attribute is always "done". To be removed in v2.0.0.
  • <Inbox /> zero-prop demo set updated to use done
  • Chapter §14.4 demo (data-mb-props + hand-rolled HTML + inline CSS + snippet tabs) migrated to done
  • components/_shared.css selectors target [data-state="done"] as canonical; the [data-state="completed"] selector is kept transitionally so consumers writing raw HTML with the old value still get the right treatment until v2.0.0

Added

  • Spelling convention note in CLAUDE.md — documents that the kit deliberately uses UK English in prose (personalise, customise, recognise, optimise) while US-spelling code identifiers (color, behavior). Stops the next agent from "correcting" the UK forms. The v1.49.0 audit had flagged "Personalise" as drift; the audit was wrong — grep shows 14× personalise and 0 personalize across the kit. The convention is real, just unwritten until now.
  • docs/_customer-success.md updated — the deferred-items section now reflects the resolved unifications; the "Same concept, different component" disambiguation tables show the aligned vocabularies.

Lockstep with @magicblocksai/css@1.50.0 (only CSS change: the .inbox-row[data-state="done"] selectors added alongside the existing [data-state="completed"] ones for back-compat).


[1.49.0] — 2026-05-18

Minor — Round CS: customer-success taxonomy audit + fixes. A read-only audit of the kit's customer-success surfaces (LifecyclePill, DealStagePill, RiskPill, RiskBadge, TicketStatusPill, PipelineBar, Kanban, Inbox, Checklist, ScoreRing/Card, SLARing, ActivityTimeline, StageChat/HappaArc) surfaced a handful of P0 bugs, a tier-naming inconsistency in <LifecyclePill>, two parallel risk vocabularies without a "when to use which" doc, and a duplicated HAPPA stage type. This round fixes everything that can ship without breaking changes; the deferred items (stage-vocab unification, Checklist/Inbox state alignment, <LifecycleStrip> primitive) are tracked in the new IA doc.

Fixed

  • <InboxRow> chapter 14 demo: 3 of 6 demo rows in components/14-app-pipeline.html (and the kit-site mirror) serialised "priority":"med" — not a valid value for InboxRowPriority ("low" | "medium" | "high"). When kit-islands hydrated the demo, the priority chip fell back to undefined. Fixed to "medium" everywhere.
  • <TicketStatusPill> doc: was missing the snoozed value documented in TSX since v1.9.2 (R8-2). Added the row to the tint table, expanded the type signature, added a usage note explaining the snoozed semantic vs pending/closed.

Changed

  • <LifecyclePill>: relabelled the v1.13.3 second tier from "post-sale operational stages" to "operational sales-to-renewal stages" — the previous name was inaccurate because four of the nine values (qualified, discovery, proposal, contracted) are pre-contract sales stages. The TSX comment + the doc now spell out the duplicate-pair semantics (sql vs qualified, customer vs contracted, churned vs churn).
  • <LifecyclePill>: rendered spans now carry an additional data-lifecycle-tier="funnel" | "operational" attribute so consumers can disambiguate the duplicate-pair stages in CSS without writing their own lookup table. New exported helper lifecycleTierOf(value) does the same lookup in TS. New LifecycleTier type exported from the package barrel.
  • <HappaArc> and <StageChat>: both now consume the new shared HAPPA_STAGES / HAPPA_STAGE_LABELS / HAPPA_STAGE_GLYPHS / HappaStageId primitives from src/lib/happa.ts. Previously each component duplicated its own version of the same five-stage list. StageChat's legacy StageChatStage type is now an alias of HappaStageId (back-compat preserved for existing consumers).
  • <ScoreRing>: TSX comment now includes a "score-vs-risk colour direction" callout — <ScoreRing band="low"> renders red while <RiskBadge level="low"> renders green. Same word, opposite tint. Added the same callout to <ScoreCard> and both risk-component docs.

Added

  • docs/RiskBadge.md — was missing entirely. Documents the 5-level scale, the pairing with <Sparkline>, and explains when to reach for <RiskBadge> vs <RiskPill>.
  • docs/RiskPill.md — added "when to use RiskPill vs RiskBadge" guidance plus the score-vs-risk colour callout.
  • docs/_customer-success.md — new IA doc covering the four orthogonal axes (lifecycle stage / pipeline position / workflow status / quantitative health), per-surface recipes (record detail, kanban, inbox, health dashboard, ticket detail), and "same concept, different component" disambiguation tables. Also lists the known gaps the audit deferred (state-vocab unification, default pipeline alignment, <LifecycleStrip>).
  • src/lib/happa.ts — new shared HAPPA-conversation primitives. Re-exported from the package barrel.
  • SLA_THRESHOLDS + slaStateFromElapsed(f) exported from <SLARing>. Consumers can now either roll their own status pill with the same banding as the kit's <SLARing>, or override state based on a custom rule while comparing against the kit's defaults.

Deferred (audit findings tracked for follow-up rounds)

  • Unify the default stage vocabularies of <PipelineBar>, <Kanban>, and <LifecyclePill> (currently negotiation/won vs proposal/contracted for the same concepts).
  • Normalise the done (Checklist) vs completed (Inbox) state vocab — same concept, two words.
  • Bulk-rename "Personalise" → "Personalize" across the 7+ chapter/component/doc files that ship it (>5-file rename, deferred per repo CLAUDE.md ask-first rule).
  • Absorb a <LifecycleStrip> primitive once a second consumer hits the wall described in <LifecyclePill>'s "When to outgrow this" doc section.

Lockstep with @magicblocksai/css@1.49.0 (no CSS changes this round; bump matches the convention).


[1.48.0] — 2026-05-18

Minor — Round W: the killer chat widget. Single-shot delivery of the full end-user-facing chat widget system in one focused version. Fourteen components covering the visitor runtime (10), the operator-facing designer (2), and supporting kit primitives (2). Every visual lever from the platform's Chat Appearance editor is wired to a CSS custom property emitted by <WidgetThemeProvider>. Lockstep with @magicblocksai/css@1.48.0.

Added — visitor runtime (what shows on acme.com)

  • <WidgetThemeProvider> + useWidgetTheme() + WIDGET_DEFAULTS — the token override layer. Accepts a deeply structured WidgetTheme config covering every lever (launcher / shell / messages user-side & ai-side / send / buttons primary-secondary-suggestion / feedback / composer / proactive / welcome disclaimer / legal disclaimer / debug surfaces / labels / mobile overrides / customCss). Emits scoped CSS custom properties under .widget-theme-scope. Mobile-specific overrides re-emit inside a @media (max-width: 540px) scope.
  • <WidgetLauncher> — the floating bubble. Theme-driven background colour / icon colour / icon glyph / image background / position (left/right) / size / offsets. Stateless. Unread badge slot.
  • <WidgetProactiveMessage> — pre-engagement bubble that pops out beside the launcher. Click body opens chat; click × dismisses.
  • <WidgetShell> — the expanded chat panel. Auto-built or override-able slots for header / welcome disclaimer / transcript / quick-replies row / composer / legal disclaimer / branding marker. Below 540px the floating panel becomes full-screen.
  • <WidgetMessage> — visitor-facing bubble. Two sides (user / ai), themable colours per side, smart 12h / 24h timestamp formatting from theme, optional AI name, streaming + failed states.
  • <WidgetComposer> — input + customizable Send button. Send label, icon, icon-position, colours theme-driven. Autogrowing textarea.
  • <WidgetButton> — three variants (primary / secondary / suggestion), each fully themable.
  • <WidgetFeedback> — thumbs up/down chip pair. Stateless.
  • <WidgetDisclaimer>welcome and legal variants with separate token sets.
  • <WidgetBrandingMarker> — "Powered by MagicBlocks" footer. Plan-gated by consumer.

Added — operator designer (Chat Appearance editor)

  • <WidgetStyleEditor> + <WidgetStyleSection> — split-pane shell with form pane + live preview pane + optional left sidebar. Below 960px collapses to single-column.
  • <WidgetEmbedSnippet> — embed-code generator supporting five framework targets (HTML / React / Next.js App Router / Vue / WordPress). Tab switcher + Copy button.

Added — support primitives (kit-wide additions)

  • <ColorField> — labelled colour picker with hex input + native swatch + optional presets row.
  • <ColorSwatchPicker> + WIDGET_SCHEMES — named-preset row of colour swatches. Default palette: Green / Yellow / Pink / Blue / Navy / Orange / Black / White. Supports two-tone swatches via accent.

What the theme covers

Every lever from the platform's Chat Appearance editor (see docs/Widget.md for the full map):

  • Top-level — dir / fontFamily / fontWeight / fontSize / lineHeight
  • Launcher — icon / background / iconColor / imageUrl / position / bottomOffset / sideOffset / size
  • Shell — backgroundImage / headerBackground / headerText / chatBackground / width / height / showBranding
  • Proactive — enabled / text / textColor / background / border / borderRadius
  • Messages (user) — textColor / bubble / border / borderRadius / showTimestamp / timestampColor / timestampFormat
  • Messages (AI) — textColor / bubble / border / borderRadius / showName / nameColor / timestamp
  • Debug — debugTimestampColor / debugActionColor (for in-widget trace surfaces)
  • Send — label / icon / iconPosition / textColor / buttonColor / border / borderRadius
  • Buttons — primary / secondary / suggestion (each: textColor / buttonColor / border / borderRadius)
  • Feedback — buttonColor / iconDefault / iconActive
  • Composer — placeholderColor / textColor / background / border / fontSize
  • Welcome disclaimer — enabled / message / textColor / background
  • Legal disclaimer — enabled / message / textColor / alignment
  • Notification sound, Labels (10 strings), Mobile overrides, Custom CSS escape hatch

Notes

  • All fourteen ship in the kit-chrome carve-out (no per-chapter HTML demo, docs-only at packages/ui/docs/). Overview at docs/Widget.md; per-component docs alongside.
  • Distinct from the B1 operator-facing conversation primitives — different audience, different chrome.
  • Embed-first. All styles namespaced under .widget-* selectors and the .widget-theme-scope wrapper, so embedding into a customer's site doesn't leak kit tokens or affect their CSS.
  • No external dependencies. No chart libs, no DnD libs, pure SVG + CSS + standard React.
  • Tokens, not CSS injection. Operators design via a form, not a code editor. The customCss escape hatch exists for edge cases.
  • Token-driven, dark-mode safe (where the embedding site uses dark mode), honours prefers-reduced-motion.
  • Pair with @magicblocksai/css@1.48.0.

Round retrospective

  • Rounds A1–A3 (1.41.0–1.43.0): 25 form / list-chrome / settings primitives
  • Round B1 (1.44.0): 5 conversation primitives (operator transcript)
  • Round B2 + B2-refined (1.45.0–1.46.0): TraceTimeline (the robot head)
  • Round B3 (1.47.0): TimeSeriesChart + StructuredDocEditor + LiveChatTester
  • Round W (this version): the full chat widget system

Remaining: Round C (settings/billing/workspace chrome); Round D (future-state visualisations — JourneyGraph, Evaluations, ReviewerInbox).


[1.47.0] — 2026-05-18

Minor — Round B3 closes Round B. Three independent shapes that round out the conversation / analytics surface before Round W (the killer end-user-facing chat widget, queued next): a multi-series line chart for Dashboard analytics, a sectioned doc editor for Sales Playbooks, and a composed LiveChatTester shell for Try-my-Agent surfaces. Lockstep with @magicblocksai/css@1.47.0.

Added

  • <TimeSeriesChart> — multi-series line chart for the Dashboard analytics surface and any "value over time" pane. Pure inline SVG with no external chart dependencies (no recharts, no chart.js, no d3). SSR-safe via viewBox-based rendering — no client-side container measurement. Features: auto Y-axis scaling with "nice" tick intervals (1 / 2 / 5 × 10ⁿ rounding), multi-series with toggleable legend chips, hover tooltip showing all visible series values at the snapped X, optional area fills, optional Catmull-Rom smooth curves, optional dashed lines. Default 8-colour palette mirrors <TraceTimeline> tone semantics for cross-component consistency. Documented at docs/TimeSeriesChart.md.
  • <StructuredDocEditor> — sectioned document editor for Sales Playbooks, structured Q&A docs, change logs, RFC drafts, and any "named sections with rich body per section" surface. Each section has a numbered heading, editable title (real <input>, not contentEditable), optional icon / kind chip / caption, and a body (default autogrowing textarea; pass renderBody for <RichTextEditor> / <SnippetTextarea> / custom). Optional sticky table-of-contents rail (left or right, with anchor-scroll). lockedTitle / lockedBody / required flags for template mode. Compose with <SortableList> for drag-to-reorder. Documented at docs/StructuredDocEditor.md.
  • <LiveChatTester> — composed shell for Try-my-Agent surfaces, channel sandboxes, any interactive agent-preview pane. Wraps <ChatTranscript> + <ChatComposer> (B1) with a sticky agent-status header (avatar + name + version pill + status dot + caption) and an optional reset / restart affordance. Four status states (online / offline / thinking / error) with appropriate dot treatments — thinking blinks amber, collapses to static under prefers-reduced-motion. header={null} opts out of the header entirely for embedded uses. Documented at docs/LiveChatTester.md.

Notes

  • All three ship in the kit-chrome carve-out (no per-chapter HTML demo, docs-only at packages/ui/docs/).
  • Round B is complete. Conversation surface (B1) + TraceTimeline (B2 / B2-refined) + Round B3 = the full operator-facing conversation, explainability, and analytics surface.
  • Round W (the end-user-facing embedded chat widget — <WidgetShell> + <WidgetLauncher> + <WidgetThemeProvider> + <WidgetStyleEditor> + <WidgetEmbedSnippet> + <WidgetBrandingMarker>) is next. This is the killer-feature round for the Channels page and Go-Live flow; getting it focused and right is the next big push.
  • Token-driven, dark-mode safe, honours prefers-reduced-motion. All animations (chart-line hover opacity, status-dot blink, transcript-scroll smooth, autogrow textarea) collapse to static states under the motion preference.
  • Pair with @magicblocksai/css@1.47.0.

Cumulative coverage

  • Round A1 (1.41.0) + A2 (1.42.0) + A3 (1.43.0) → 25 form / list-chrome / settings primitives
  • Round B1 (1.44.0) → 5 conversation primitives (operator-facing transcript)
  • Round B2 (1.45.0) + B2-refined (1.46.0) → <TraceTimeline> + <TraceEventCard> (the robot head)
  • Round B3 (this version) → <TimeSeriesChart> + <StructuredDocEditor> + <LiveChatTester>. Round B complete.

Remaining: Round W (widget — next), Round C (settings/billing/workspace chrome), Round D (future-state: JourneyGraph, Evaluations, ReviewerInbox).


[1.46.0] — 2026-05-18

Minor — TraceTimeline refinement pass. The v1.45.0 first cut anchored too closely on the platform-tour mockup (tinted cards everywhere, numbered badges as the primary signal, repeating timestamps, faint hairline track). This pass pushes the design past the mockup toward a more premium, scannable, production-grade surface. Lockstep with @magicblocksai/css@1.46.0. Fully backward-compatible — all new props are additive; v1.45.0 consumers get the refined visuals automatically.

Refined

<TraceTimeline> redesigned around four principles:

1. Calm by default. Per-event card tinting dropped. Every card uses the neutral Card background. Tone colour now lives in: the leading icon ring, the eyebrow chip text, bullet dots, and (only for error + security) a 2px coloured left rail. Loud colours earn their loudness; the rest of the timeline stays calm. Across 12 events this is dramatically less noisy. 2. Icons over numbers. Number badges downgrade to a small step 1· eyebrow prefix. The prominent visual slot now accepts a consumer-provided leadingIcon — drop <BookIcon> for Knowledge, <TargetIcon> for Goal, <BoltIcon> for Tool, <ShieldIcon> for Security. Instantly scannable in a glance. 3. Time as progression, not noise. Smart timestamp dedup via the new timeMs event prop + timeStrategy timeline prop. First event renders an absolute time; subsequent events render deltas (+12ms, +1.2s, +2min). Eleven repeating timestamps become one anchor + eleven readable steps. 4. Section auto-grouping. Adjacent events with different tones automatically get extra spacing — the success cluster, the error cluster, the security cluster visually separate without needing section headings.

Added (additive API)

  • event.leadingIcon — optional ReactNode shown in the gutter as the primary visual. Falls back to the auto-numbered step glyph when absent.
  • event.eyebrow — small uppercase mono category chip above the title ("KNOWLEDGE", "TOOL", "SECURITY"). Recommended for scannability.
  • event.timeMs — epoch / duration ms. When set, the timeline applies its timeStrategy to format timestamps. Backward-compatible with the existing event.timestamp string prop (which wins when both are set).
  • event.streaming — when true, the leading icon pulses softly with a tone-coloured ring, a small LIVE chip renders next to the timestamp, and the card gets a faint tonal halo. Future-proofs the component for live runtime traces.
  • timeStrategy on <TraceTimeline>: "absolute" / "deltas" / "first-then-deltas" (default).
  • hideStepNumbers on <TraceTimeline> — hides the step n eyebrow prefix while keeping leading icons.
  • hideLeading — renamed from hideNumbers (still backward-compatible-safe; both render the leading slot identically).

Refined CSS

  • Vertical track upgraded from a 1.5px hair-grey hairline to a confident 2px line in color-mix(in oklab, var(--ink) 10%, transparent). Brightens to 18% ink on row hover.
  • Leading slot grows from 22px to 28px and gets a box-shadow: 0 0 0 4px var(--bg-paper) "cutout" so the track terminates cleanly under it.
  • Card hover state: subtle bg shift to a warm off-paper, border accepts an 18% tonal tint. Real product polish.
  • Bullet items use a label: colon-after-pseudo and a flex-wrap body so long values flow gracefully.
  • Code items get a </> leading glyph in a tone-tinted pill and a ghost Copy button (no border, transparent until hover).
  • Streaming pulse is a CSS-only scaling ring animation; collapses to a static scale(1.1) frame under prefers-reduced-motion.
  • Section-break spacing is a single CSS rule (.trace-timeline-item.is-section-break { padding-top: var(--s-3); }).

Notes

  • Backward-compatible: every v1.45.0 consumer gets the refined visuals with no code changes. Adopt leadingIcon / eyebrow / timeMs / streaming opt-in to unlock the full polish.
  • Token-driven, dark-mode safe, honours prefers-reduced-motion. All animations (track-brighten, hover bg, chevron rotate, streaming pulse, live-dot blink) degrade gracefully.
  • Pair with @magicblocksai/css@1.46.0.

Round W flagged

The end-user-facing chat widget (Round W — <WidgetShell> + <WidgetLauncher> + <WidgetThemeProvider> + <WidgetStyleEditor> + <WidgetEmbedSnippet> + <WidgetBrandingMarker>) is queued as a dedicated future round, distinct from the operator-facing transcript surfaces shipped in v1.44.0.


[1.45.0] — 2026-05-18

Minor — Round B2 of the MagicBlocks Next Gen platform-tour gap analysis: the AI Reasoning Flow timeline (the "robot head"). Single-focus version. <TraceTimeline> is the single most powerful explainability surface in the entire product — operators use it to answer "why did the agent do that?" without reading JSON logs. Designed carefully against the platform-tour mockup; the docs include a full 12-event example reproducing it exactly. Lockstep with @magicblocksai/css@1.45.0.

Added

  • <TraceTimeline> — vertical, numbered list of agent-runtime events for a single message turn. Six tones group events by category (success / warn / error / security / info / neutral) drawn directly from the platform's observed event taxonomy; three item kinds (bullet / card / code) cover every body shape in the mockup. The faint vertical track connecting number badges auto-suppresses on the last item. Tone palette cascades via CSS custom properties on .trace-event, so a custom category requires one .trace-event.is-tone-<name> rule. Dark-mode adjustments lift saturation per tone. Documented at docs/TraceTimeline.md with a full 12-event example reproducing the platform-tour mockup and recipes for inline-expansion-beneath-message vs modal-deep-dive surfaces.
  • <TraceEventCard> — same shape as a single timeline row, exported for rendering one-off events outside a timeline (toast previews, single-event drawers, debug surfaces).

Tone vocabulary

| tone | Use for | |---|---| | "success" | Knowledge Used, Facts Captured, Goal Triggered, Director Match, Block transition success | | "warn" | Tool calls, Form completed, Form submitted, operational steps | | "error" | Model failed, Form post error, Tool timeout, Guardrail rejection | | "security" | Jailbreak detected, Moderation hit, Redaction applied, PII intercept | | "info" | Message ID, Session ID, telemetry, raw metadata | | "neutral" | Default — events without an established category |

Item kinds

| kind | Shape | |---|---| | "bullet" (default) | • {label}: {value} line with a small dot in the tone colour | | "card" | Boxed row, optional chevron + expand-to-reveal details body | | "code" | Mono code block with a Copy-to-clipboard button (Message ID shape) |

Notes

  • Both ship in the kit-chrome carve-out (no per-chapter HTML demo, docs-only at packages/ui/docs/).
  • Pure presentation — the kit doesn't enforce a runtime event schema. Consumers map their backend events into the TraceEvent shape.
  • Pairs with <MessageTraceButton> (Round B1, the per-message trigger) and <SummaryBanner> (Round B1, the TL;DR card typically rendered above the timeline in modal mode).
  • Token-driven, dark-mode safe, honours prefers-reduced-motion. Expand-chevron rotation and card-trigger hover are the only animations; both collapse to static states under prefers-reduced-motion.
  • Pair with @magicblocksai/css@1.45.0.

Cumulative coverage so far

  • Round A1 (1.41.0) + A2 (1.42.0) + A3 (1.43.0) — 25 form / list-chrome / settings primitives → "every operator form is skinnable from the kit"
  • Round B1 (1.44.0) — 5 conversation-surface primitives (the operator-facing transcript)
  • Round B2 (this version) — <TraceTimeline> + <TraceEventCard> → the trace surface is complete

Remaining in Round B: B3 — <TimeSeriesChart>, <StructuredDocEditor>, <LiveChatTester> (composed shell). Then Round C (chrome/settings/billing), Round D (future-state visualisations: JourneyGraph, Evaluations), and Round W (the dedicated end-user-facing chat widget — separate from the B1 operator-facing transcript).


[1.44.0] — 2026-05-18

Minor — Round B1 of the MagicBlocks Next Gen platform-tour gap analysis: the conversation surface. Five tightly-coupled primitives that compose into the Sessions viewer, Try-my-Agent surface, Contact Sessions tab, and any inline chat preview. Designed in a single API pass since the parts wire into each other (<ChatTranscript> wraps <ChatMessage> nodes, <ChatMessage actions={...}> typically holds a <MessageTraceButton>, <ChatTranscript footer={...}> typically holds a <ChatComposer>, and <SummaryBanner> sits above the whole stack). Lockstep with @magicblocksai/css@1.44.0.

Added

  • <ChatMessage> — one conversation bubble (agent / user / system). The atom. Three sides drive alignment + bubble shape + avatar treatment. Optional actions slot (hover-revealed on fine pointers, always-visible on coarse), reactions row (thumbs / emoji chips), status (streaming adds a blinking cursor; failed paints a red rail + retry hint), confidence (coloured dot beside the timestamp), density (comfortable / compact). System messages render as a centred pill ("Block transition: Hook → Align"). Pre-wrap whitespace preserved in the body. Documented at docs/ChatMessage.md.
  • <ChatTranscript> — scrollable conversation viewer shell with sticky header + pinned footer slots. Sticky auto-scroll behaviour: when new children arrive, scrolls to bottom *only* if the user is already near the bottom (~120px, configurable). If they've scrolled up to read older messages, new arrivals don't yank the view. Optional agentTyping indicator (three pulsing dots in an agent bubble), loading skeleton, and emptyState slot. Wraps content in role="log" aria-live="polite". Documented at docs/ChatTranscript.md.
  • <ChatComposer> — bottom-of-transcript composer. Autogrowing textarea (capped at maxRows, default 6) + Send button. Submit on Enter (Shift+Enter inserts a newline; toggle via submitOnEnter). Send is disabled when the value is empty (after trim); passing sending swaps the label for a spinner. Optional left-side attachments slot + right-side actions slot. The composer does NOT clear itself — the consumer owns the value and decides when to clear. Documented at docs/ChatComposer.md.
  • <MessageTraceButton> — per-message "robot head" trace icon. Pairs with <TraceTimeline> (shipping next in v1.45.0 / Round B2). The noteworthy dot-badge is the discoverability lever: most messages are boring, but the ones that triggered a block transition / action firing / guardrail / RAG miss announce themselves visually so operators learn to click them. Three sizes (xs / sm / md), optional event-count badge. Documented at docs/MessageTraceButton.md.
  • <SummaryBanner> — collapsible AI-generated TL;DR card. The kit's standard "here's a summary of this conversation / session / agent diff / change log" banner. Defaults to expanded; accent for the pink left rail used on AI-content surfaces. Optional icon (defaults to a sparkle glyph), caption, meta (typically a "Regenerate" button). hideToggle for static (always-visible) mode. Documented at docs/SummaryBanner.md.

Notes

  • All five ship in the kit-chrome carve-out (no per-chapter HTML demo, docs-only at packages/ui/docs/).
  • <MessageTraceButton> ships now (Round B1) but its companion <TraceTimeline> follows in v1.45.0 / Round B2. Consumers wiring up the trace surface can stub the modal with a console.log in onClick until then.
  • Token-driven, dark-mode safe, honours prefers-reduced-motion. The chat-bubble streaming caret, typing-dots animation, skeleton pulse, send-spinner, and summary-banner chevron all collapse to static / non-animated states under prefers-reduced-motion.
  • Pair with @magicblocksai/css@1.44.0.

[1.43.0] — 2026-05-18

Minor — Round A3 of the MagicBlocks Next Gen platform-tour gap analysis. Cleanup batch — ten remaining Round A primitives that close the "every agent-builder form, every list-page chrome, every settings surface is skinnable from the kit" claim. With v1.41.0 (Round A1) + v1.42.0 (Round A2) + this version, every Next Gen product-app surface identified in the gap analysis now has a kit primitive (Rounds B/C/D, the conversation surfaces and future-state visualisations, follow). Lockstep with @magicblocksai/css@1.43.0.

Added

  • <MultiBindingList> — repeater of select + remove rows, every row binds a single value from a shared options pool. Sibling to <KeyValueRepeater>. Backs the Brain → MCP picker (+ Add MCP), Knowledge Base Collections multi-select (≤3), multi-recipient email destinations. Documented at docs/MultiBindingList.md.
  • <FilterPopover> — generic filter trigger + popover with title / body / footer slots. Body is consumer-owned (typically <QueryBuilder>); the popover handles trigger / outside-click / Escape. Active-count pill on the trigger when filters are set. Documented at docs/FilterPopover.md.
  • <TimePicker> — HH:MM time input wrapping the native <input type="time"> for keyboard / a11y parity, with optional 12-hour caption. Used in Availability work-days, future Schedule-send, "from/to" surfaces. Documented at docs/TimePicker.md.
  • <SavedViewsRail> — named query-views rail with labels + counts + groups + optional star-toggling. Backs the Sessions left-rail saved views (All / Favorites / Goal Conversions / Principles Fixed / Missing Knowledge / Negative Sentiment) and Dashboard Latest-Sessions tabs. Documented at docs/SavedViewsRail.md.
  • <VersionSwitcher> — versioned-entity dropdown with timestamps + status pills + optional + Create new version / Compare versions footer actions. Used for Agents, Personas, Tasks (the platform's three versioned entities). Documented at docs/VersionSwitcher.md.
  • <HelpBubble> — floating help / chat launcher (the global bottom-right "Magic on Magic" bubble). 4 anchor presets, optional badge count, dismisses on Escape + outside-click. Documented at docs/HelpBubble.md.
  • <DomainList> — chip-input for hostnames. Enter / comma / space commits, backspace removes, custom validator hook. Optional "Allow all domains" toggle. Backs Design & Go Live whitelist and CORS-origin lists. Documented at docs/DomainList.md.
  • <CategoryGroupList> — grouped expandable list (label + count + meta header, items revealed on expand). Distinct from <AccordionGroup> — this one is item-focused, multi-open by default. Backs Contact Memories (5 categories), Knowledge categories rail. Documented at docs/CategoryGroupList.md.
  • <ExpandableEditRow> — collapsed row → expanded inline editor reveal pattern. Row-shaped (icon + title + meta in a line, fits inside a list), distinct from the card-shaped <Accordion>. Backs Knowledge Q&A items, Missing Knowledge convert flow, Key Fact ledger. Documented at docs/ExpandableEditRow.md.
  • <SourceFreshnessBanner> — status banner for a synced source (fresh / stale / changed / error tones). Backs Knowledge collection "Sitemap Changes Found · Setup Detector", Webhook last-ping status, Connection freshness. Documented at docs/SourceFreshnessBanner.md.

Notes

  • All ten ship in the kit-chrome carve-out (no per-chapter HTML demo, docs-only at packages/ui/docs/).
  • Cumulative coverage across Round A: 25 primitives, ~65% of the platform-tour gap analysis by surface count. Every form, list chrome, and settings surface identified in the analysis now has a kit primitive.
  • Token-driven, dark-mode safe, honours prefers-reduced-motion. <HelpBubble> uses position: fixed and is full-page chrome (sits above all other content via z-index 60).
  • Pair with @magicblocksai/css@1.43.0.

[1.42.0] — 2026-05-18

Minor — Round A2 of the MagicBlocks Next Gen platform-tour gap analysis. Nine product-app primitives — the high-leverage molecules behind the agent-builder condition surfaces, snippet-token text fields, mapping repeaters, and async probe buttons. <QueryBuilder> is the centrepiece: a single primitive that unifies four near-identical condition surfaces in the platform (Contacts filter, Action Conditions, Key Fact Ask-when, Webhook event filters). Lockstep with @magicblocksai/css@1.42.0.

Added

  • <QueryBuilder> + <ConditionRow> — single-level AND/OR condition builder. Each row composes a field picker → operator picker → type-driven value editor (text / number / select / multiselect / boolean / duration / known). Seven field types, fifteen operator labels, optional AND/OR conjunction toggle. Nested groups deliberately left out of v1; the row data model is stable enough to extend with groups? later without breaking consumers. Documented at docs/QueryBuilder.md.
  • <SnippetTextarea> — textarea + Insert snippet button + grouped picker menu. Inserts tokens at the cursor position, restores focus after insertion, dismisses on Escape + outside-click. Distinct from <MergeTagInput> (which is keyboard-driven via {{) — this one matches the platform's always-visible-toolbar chrome. Documented at docs/SnippetTextarea.md.
  • <FieldMapper> — two-column source → target field mapper with + Add field. Used for HubSpot deal mapping, Calendar field auto-population, CSV column → CRM field, and any source → target repeater shape. Documented at docs/FieldMapper.md.
  • <VariantsRepeater> — list of textarea variants with add + remove. AI Message Versions, Snippet Variants, "give me three ways to say this" surfaces. max cap, numbered rows. Documented at docs/VariantsRepeater.md.
  • <RadioCardList> — vertical radio cards w/ title, description, optional icon / meta / reveal sub-form. The kit's standard "pick one option with explanation" shape — First Action (8 types), Form Interaction, Region select, Off/On config panels, Credits top-up packages. Documented at docs/RadioCardList.md.
  • <TwoPathChooserDialog> — two-card chooser body (drop inside <Modal>). Recurring "pick one of two creation paths" shape: Key Fact Template-vs-Custom, Tools & MCP HTTP-vs-MCP-Server, Forms Custom-vs-Snippet, future block-template chooser. Documented at docs/TwoPathChooserDialog.md.
  • <PayloadPreview> — formatted JSON preview card w/ copy button + optional top-level-key highlight pills. HubSpot deal payload, Webhook destination, MCP argument shape, future event-stream samples. Display-only. Documented at docs/PayloadPreview.md.
  • <TestConnectionButton> — async probe button w/ built-in pending / ok / error chip. Auto-disables during the request, auto-clears the chip after a configurable timeout. Pass onTest: () => Promise<…> for the simple case; pass state for fully-controlled use. MCP Discover Tools, Webhook Test, HubSpot Test, Form Test. Documented at docs/TestConnectionButton.md.
  • <KpiDeltaTile> — KPI tile with explicit period-comparison delta ("+12% vs last 7 days"). Sibling to the existing <KpiTile>. invertDirection for metrics where up is bad. Documented at docs/KpiDeltaTile.md.

Notes

  • All nine ship in the kit-chrome carve-out (no per-chapter HTML demo, docs-only at packages/ui/docs/).
  • Round A2 covers another ~25% of the new surface area identified in the platform-tour gap analysis (cumulative with A1: ~40%).
  • Token-driven, dark-mode safe, honours prefers-reduced-motion. AND/OR conjunction toggle is responsive (collapses gracefully below 720px).
  • Pair with @magicblocksai/css@1.42.0.

[1.41.0] — 2026-05-18

Minor — Round A1 of the MagicBlocks Next Gen platform-tour gap analysis. First batch of product-app primitives surfaced by mapping every section of the AI sales-agent platform against the kit's existing component inventory. Six small kit-chrome carve-outs that unblock a long tail of agent-builder, settings, and list-page surfaces. Lockstep with @magicblocksai/css@1.41.0.

Added

  • <Slider> — numeric slider with paired value readout. The kit's standard "creativity", "temperature", "weight" control. Drop-in via defaultValue + onValueChange (controlled with value). Custom formatValue for non-default readouts. Honours prefers-reduced-motion. Documented at docs/Slider.md.
  • <Accordion> + <AccordionGroup> — single-section disclosure with optional grouping. Backs Guardrails Off/On panels, per-block Advanced overrides (First action / Persona / Brain / Guardrails / Settings), and Chat-appearance style-category panels. AccordionGroup defaults to single-open; allowMultiple opens any subset. Stable item ids preserved across edits. Documented at docs/Accordion.md.
  • <SyncStatus> — "Synced 3 minutes ago · Refresh" status line. Auto-ticks the relative time every minute (tickMs override). Spinner state via busy. Recurs on every list page in the Next Gen app. Documented at docs/SyncStatus.md.
  • <SplitButton> — primary action + caret-menu of alternate modes ("Send now" + "Schedule send", "Save" + "Save and publish"). Four tones (primary / neutral / danger / ghost), three sizes. Caret-menu dismisses on Escape + outside-click. Documented at docs/SplitButton.md.
  • <DurationField> — composite count + unit input. Backs the No message received from user for… agent condition and future delay surfaces. Default units: minutes / hours / days / months; override via units. Documented at docs/DurationField.md.
  • <KeyValueRepeater> — list of key/value pairs with add + remove affordances. Canonical shape for MCP auth headers, Webhook headers, HTTP function parameters. readOnly mode for discovered-state displays. Documented at docs/KeyValueRepeater.md.

Notes

  • All six ship in the kit-chrome carve-out (no per-chapter HTML demo, docs-only at packages/ui/docs/). Round A2/A3 + Rounds B/C/D from the platform-tour gap analysis (QueryBuilder, SnippetTextarea, FieldMapper, ChatTranscript / TraceTimeline, WidgetStyleEditor, JourneyGraph, …) follow in subsequent versions.
  • Token-driven, dark-mode safe, honours prefers-reduced-motion.
  • Pair with @magicblocksai/css@1.41.0.

[1.40.0] — 2026-05-16

Minor — narrative / long-form utilities harvested from "The Reliability Gap" research-report landing page. Four reusable patterns from a long-form MagicBlocks-vs-OnePrompt comparison piece lifted into the canonical kit. Lockstep with @magicblocksai/css@1.40.0. Not consumer-driven (no Spark / Website / Wingman round) — designer ship, no fixes-log entry.

Added

  • useReveal() hook (in the lib/ primitives barrel) — attach to an element with className="reveal" (and optional .d1.d5 stagger classes); the hook flips .in when the element enters the viewport. Synchronously applies .in if the element is already onscreen at mount (handles back-button navigation landing mid-page). Honours prefers-reduced-motion. Options: rootMargin / threshold / once. Documented at docs/ScrollCue.md.
  • <ScrollCue> — animated "scroll for more" indicator. Mono uppercase label above a 38px vertical hairline that pulses on a 2.4s loop. Honours prefers-reduced-motion. Position via the consumer's wrapper (typically absolute-positioned inside a position: relative hero). Documented at docs/ScrollCue.md.
  • <DialogueContrast> family (DialogueContrast + DialogueColumn + DialogueMessage + DialogueEllip) — two-column conversation comparison container. Each column has a header (who · tag · status), a stack of from="us" / from="them" messages, and an optional outcome banner. tone="win" | "loss" | "neutral" colours the top accent stripe and the is-us bubble fill. Collapses to a single column below 720px. <DialogueEllip> renders soft italic stage-direction text ([explains privacy policy at length]) inside a message. Documented at docs/DialogueContrast.md.
  • <DotMatrix> — proportion visualisation. Renders a grid of dots where a subset is highlighted to communicate "out of N, M did X." Default 1000-dot 40×25 grid (one dot per lead in a 1k-lead sample). distribution="scatter" | "head" | "tail" | "random" controls placement; scatter (default) spaces highlighted dots evenly so the proportion reads visually. Pure markup (no canvas, no JS), scales fluidly, prints cleanly. CSS custom properties (--dm-cols, --dm-gap, --dm-base, --dm-highlight) for per-instance tuning. Documented at docs/DotMatrix.md.

Notes

  • Four new primitives ship in the kit-chrome carve-out (no per-chapter HTML demo, docs-only at packages/ui/docs/, same pattern as <EmailThreadRow> / <ScoreCard> / <NarrationLog> etc.).
  • All four honour prefers-reduced-motion. Token-driven, dark-mode safe.
  • Pair with @magicblocksai/css@1.40.0.

[1.39.0] — 2026-05-15

Minor — Wingman Round 1 (the first round of asks from the 10DLC Wingman team — a third named consumer alongside Spark CRM and the MagicBlocks Sales Website) plus a small Spark Round X+1 ask rolled in lockstep. Lockstep with @magicblocksai/css@1.39.0 and @magicblocksai/brand@1.1.0. Full close-out at WINGMAN-FIXES-LOG.md Round 1 + SPARK-FIXES-LOG.md Round 38.

Added

  • <ScoreCard> (W-001) — hero "score + interpretation + breakdown" card. Composes <ScoreRing size="xl"> + a Card-style surface + label / interpretation / meta slots. fill="paper" | "warm" | "ink". Consumer-owned interpretation copy (kit only knows the score, not what it means in your domain). Kit-chrome carve-out; documented at docs/ScoreCard.md.
  • <NarrationLog> (W-002) — event-log / scan-narration list for long-running async processes (scan / build / deploy / sync / audit). Status-coloured rows (info / pass / warn / fail), optional maxVisible collapsing of older entries behind a "show N earlier" toggle, optional enterAnimation="slide-fade". Lighter than <ActivityTimeline> — no filters, no per-day grouping, no rich-row layout. Documented at docs/NarrationLog.md.
  • <EvidenceQuote> (W-003) — verbatim quote + source caption for audit / review / compliance surfaces. 3px accent left rail, italic body, mono uppercase caption with optional url → "Open ↗" link (opens in a new tab with noopener noreferrer). Documented at docs/EvidenceQuote.md.
  • <PasteAndRecheckArea> (W-004) — paste-revised-text + re-run-audit composition. Composes <Textarea> + <Button> + a status row. Tracks textarea state internally; consumer owns the async work and result reporting. result.delta animates on improvement. Documented at docs/PasteAndRecheckArea.md.
  • <ScoreRing size="xl"> (W-008) — 140px hero ring with a 38px value font. Used internally by <ScoreCard> and exposed for direct use too.
  • <Drawer placement="bottom"> (W-007) — bottom-anchored sheet (slides up from the viewport bottom). Mobile-first capture flow shape — email gate, cookie consent, post-action confirmation. Same focus-trap / scroll-lock / Esc primitives. New height?: number | string prop sets the sheet height for the bottom placement (defaults to auto up to 90dvh); width becomes a no-op when placement="bottom". The pre-1.39.0 side prop is deprecated as an alias for placement (kept until v2.0.0); when both are passed, placement wins.
  • <MenuItem keepOpen> / <DropdownMenu.Item keepOpen> (Spark Round X+1) — opt-in boolean, default false. When true, selecting the item runs onSelect but suppresses the auto-close. Multi-toggle flows (workspace switcher with checkboxes, column-visibility chooser, tag picker) no longer have to re-open the menu between each click. Applies to both the compositional API (<DropdownMenu.Item keepOpen>) and the data-driven API ({ label, onSelect, keepOpen: true } inside groups).

Notes

  • Four new primitives (ScoreCard / NarrationLog / EvidenceQuote / PasteAndRecheckArea) ship in the kit-chrome carve-out (no per-chapter HTML demo, same pattern as <EmailThreadRow> / <EmailThread> / <MentionInput> / <SectionCard>).
  • W-005 (validateAgainstVoice()) shipped in @magicblocksai/brand@1.1.0 — see that package's CHANGELOG.
  • W-009 (Tailwind v4 @import ordering note) shipped in INSTALL-FROM-NPM.md.
  • W-006 (sub-path exports for tree-shaking) deferred to a follow-up round — needs a tsup multi-entry refactor + per-component exports entries generated from the catalogue. Tracked in the Wingman log.
  • Pair with @magicblocksai/css@1.39.0 + @magicblocksai/brand@1.1.0.

[1.38.0] — 2026-05-14

Minor — Spark Round 39. New hideFilters prop on <ActivityTimeline> so consumer surfaces that already render their own filter chip row can opt out of the kit's auto-derived row. Lockstep with @magicblocksai/css@1.38.0 (no CSS changes; lockstep only). Full close-out at SPARK-FIXES-LOG.md Round 38.

Added

  • <ActivityTimeline hideFilters> (R39) — boolean prop, default false. When true the auto-rendered .act-filters chip row is omitted from the markup. defaultFilter and onFilterChange keep working — they're just decoupled from the chip UI when this is set. Lets consumers like Spark's EntityActivity (which renders a richer filter row with per-category counts from /api/v1/activities/counts above the composer) drop the kit chips without resorting to a .act-filters { display: none } consumer CSS override.

Notes

  • No breaking changes. Existing call sites continue to get the chips by default.
  • Pair with @magicblocksai/css@1.38.0.

[1.37.0] — 2026-05-14

Minor — Spark Round 39 (Spark IDs R37 + R-sortable-space). Two fixes: 1. <DataTable> non-sortable header cells now render in the same <button> structural shell as sortable headers — uniform resize-grip hit target and uniform trailing caret slot for right-aligned label alignment. 2. useSortable (and therefore every consumer of <SortableList>) no longer hijacks Space / ArrowUp / ArrowDown when the event originates inside an <input> / <textarea> / <select> / contenteditable child of a sortable item.

Lockstep with @magicblocksai/css@1.37.0. Full close-out at SPARK-FIXES-LOG.md Round 37.

Changed

  • <DataTable> header shell uniformity (R37) — every header now renders inside <button class="data-table-sort">, regardless of sortable. When sortable: false the button has type="button" with no onClick, tabIndex={-1}, aria-disabled={true}, and the modifier class .is-static (which neutralises the hover recolour and pointer cursor). The caret-slot <span> is still emitted but with empty content for non-sortable headers — a min-width: 11px rule reserves the same trailing space so right-aligned labels line up across the row even when some columns are sortable and some aren't. Resize-grip hit target is consistent across all headers. No prop changes; this is a markup change inside the component that consumers won't see except in their browser inspector.

Fixed

  • useSortable space-key hijack (R-sortable-space, P1)itemKeyDown now bails out early when the event target is an <input>, <textarea>, <select>, or [contenteditable] element. Pre-1.37.0 the kit unconditionally e.preventDefault()'d Space on the sortable wrapper, swallowing the keystroke before the browser could insert it. Fixes typed-into-input handling on every page that mounts a sortable card stack (per Spark: IssueDetailPage / ContactDetailPage / CompanyDetailPage / DealDetailPage). The same guard covers ArrowUp / ArrowDown so caret nav inside grabbed-card inputs keeps working.

Notes

  • No new exports, no new props.
  • Pair with @magicblocksai/css@1.37.0.

[1.36.0] — 2026-05-14

Minor — Spark Round 38. New <EmailThreadRow> primitive: a Gmail-shaped inbox row sibling to <InboxRow>. Both compose into <Inbox>. Lockstep with @magicblocksai/css@1.36.0. Full close-out at SPARK-FIXES-LOG.md Round 36.

Added

  • <EmailThreadRow> (R38) — 5-column row layout (star · avatar · sender+subject+preview · time · archive) for mailbox-shaped surfaces. Star is always-visible and pressable; archive is hover-discoverable on pointer-fine devices and always-visible on (pointer: coarse). unread paints a 3px accent left-rail and bolds the sender + subject text. Keyboard contract: Enter / SpaceonOpen(id). Typed-into-input bubbling is honoured. The slot model is opt-in — omit onStar to hide the star column, omit onArchive to hide the archive column. Sender / subject / preview / time accept ReactNode so consumers can slot in chips, icons, or formatted timestamps. Kit-chrome carve-out (no per-chapter HTML demo); documented at packages/ui/docs/EmailThreadRow.md.

Notes

  • Lives alongside <InboxRow>, not in place of it. Reach for <EmailThreadRow> when the row represents an email / message thread; reach for <InboxRow> when the row represents a task / ticket to dispatch.
  • Pair with @magicblocksai/css@1.36.0.

[1.35.0] — 2026-05-13

Patch — Spark Round R-cmdk. CSS-only scoping fix to the kit's keyboard focus ring; no TSX surface change. Lockstep with @magicblocksai/css@1.35.0. Full close-out at SPARK-FIXES-LOG.md Round 35.

Changed

  • Focus ring no longer paints on bare <input> / <button> / <textarea> / <select>. Previously _shared.css carried a :focus-visible { box-shadow: var(--sh-focus) } rule plus a bare-element union that caught every focusable form control — meaning a chromeless input (e.g. Spark's Cmd+K command-palette search field, which intentionally omits the .input class) showed a 3px pink halo on focus with no way to opt out without a local override. v1.35.0 replaces those with a single scoped rule covering only semantic-default focusables (a, [role="button"], [tabindex]). Consumers using the kit's classes (.input, .btn, .icon-btn, etc.) see no change — each class still declares its own focus styling further down in _shared.css. Consumers who relied on auto-styled bare elements need to add the class or supply their own focus ring.

Notes

  • All TSX components in @magicblocksai/ui already use the opt-in classes — no fixture diffs.
  • The useControllableState / focus-trap / etc. primitives are untouched.
  • Pair with @magicblocksai/css@1.35.0.

[1.34.0] — 2026-05-12

Patch — Spark Round R36. Single CSS-only fix to the <DataTable tableKey> chrome layout. No TSX surface change; no new prop. Full close-out at SPARK-FIXES-LOG.md Round 34.

Changed

  • <DataTable tableKey> columns kebab now overlays the table's top-right corner (R36 — option 3, auto-collapse). Pre-1.34.0 the .data-table-chrome toolbar took ~32px of vertical space above the header row to host the columns kebab; with no other chrome children populating the band it read as a layout bug — the gap between the SectionCard title bar and the column headers was visible dead space. Fix: chrome now position: absolutes onto the table's top-right corner (top: var(--s-1); right: var(--s-1)), with pointer-events: none on the chrome wrapper and pointer-events: auto on its children so the underlying header row stays click-through for sort. The kebab functions identically and the popover anchors against the chrome's positioning context (preserved). Bumped chrome z-index to 3 so the overlay sits above the sticky header (which is z-index: 2). Spark filed three options (chromeInline prop, renderChrome slot, auto-collapse) — option 3 has the best zero-config story and matches their workaround in apps/web/src/index.css. Spark deletes that override after upgrading.

Notes

  • No prop surface change. Existing <DataTable tableKey="..."> calls render the kebab in the new corner-overlay position automatically. Tables that don't pass tableKey (no chrome) are completely unaffected.
  • Markup-equivalence unaffected — the chrome only renders when tableKey is set, and no chapter HTML demos tableKey. Existing fixtures pass unchanged.
  • All gates green: typecheck ✓, markup-equivalence ✓, check:docs ✓, CSS / UI / kit-site builds.
  • Pair with @magicblocksai/css@1.34.0.

Round R36 mapping

| spark-id | kit-id | what shipped | |---|---|---| | R36 (open) | R34-1 | .data-table corner-overlay chrome — option 3 (auto-collapse). CSS-only; no TSX change. |


[1.33.0] — 2026-05-12

Minor — Spark Rounds R33 + R34 + R35-B. Six small additive props / glyphs from the Spark /admin/docs/* build-out, all opt-in and zero-breaking. Three docs-page primitives (R33-A · R33-B · R33-C), two source-control icons (R34), and the per-table compact density (R35-B) Spark filed alongside the resize round (R35-A is already shipped — see "Notes" below). Full close-out at SPARK-FIXES-LOG.md Round 33.

Added

  • <SectionCard subtitle> (R33-A). New subtitle?: ReactNode prop renders a one-line scoping note inside the header band, on a second line under the title. Smaller (12.5px) and softer (var(--fg-soft)) so the title stays the primary anchor — mirrors <PageHeader summary>. When set, the kit wraps title + subtitle in a column-flex container (.section-card-head-text) so the icon / count / action slots stay vertically centred against the text stack via the band's align-items: center. When unset, the head markup is identical to pre-1.33.0 (no extra wrapper) — adopt subtitle on the cards that need it without ripple changes elsewhere. Subtitle is capped at 60ch so long copy wraps cleanly inside narrow cards. Default-variant only.
  • <Switch onCheckedChange> (R33-B). New onCheckedChange?: (checked: boolean) => void higher-level handler — receives the boolean directly, mirroring the Radix / shadcn convention used elsewhere in the kit (<Checkbox onCheckedChange>, <Combobox onValueChange>, <MultiSelect onValueChange>). Sibling to — not a replacement for — the native onChange; both fire when set, with onChange first (consumers reading e.target.checked see the same value onCheckedChange is about to be called with). Useful when you only care about the boolean and don't want to plumb (e) => onChange?.(e.target.checked) everywhere.
  • <CodeBlock copyable> (R33-C). New copyable?: boolean prop renders the copy button as a corner-positioned overlay in the top-right of the block, instead of (or in addition to) the figcaption header strip. When set, the figcaption-strip copy button is suppressed so the block only ever shows one copy affordance — when neither language nor filename is set, the figcaption collapses entirely and the corner button is the only chrome. Use for chrome-light docs pages where every snippet has a copy affordance but the figcaption header strip would feel like banner bloat. Pre-1.33.0 the only way to land this composition was to wrap <CodeBlock> in a position: relative container and absolute-position a separate <CopyButton> over it — Spark was repeating that wrapper a dozen times across /admin/docs/*. The new prop bakes the composition into the kit so consumers stop reinventing it. Pairs with new .codeblk-copy-corner CSS rule that anchors the overlay top-right with a small ink-tint + blur backdrop so the button reads cleanly against any code colour underneath.
  • <GithubIcon> + <BranchIcon> (R34). Two source-control glyphs for the Spark /admin/docs/github docs surface (and any other consumer wanting canonical GitHub mark / git-branch iconography). <GithubIcon> is a single-stroke Octocat outline; <BranchIcon> is the canonical two-rail tri-node git-branch mark (top-left + bottom-left + top-right nodes joined by a curve from the right rail back to the left). Both follow the kit's monoline 18×18 / 1.5px / currentColor style; both inherit the existing IconProps surface (size, forwarded className, aria-label, etc.). Spark replaces a hand-rolled inline Octocat SVG (~200 bytes) in apps/web/src/components/GithubUnfurl.tsx and switches DocsHomePage.tsx from <LinkIcon> to <GithubIcon> for the GitHub docs card.
  • <DataTable density> (R35-B). New density?: "comfortable" | "compact" prop. "comfortable" is the historic default (var(--s-3) var(--s-4) = 12px vertical, 16px horizontal cell padding). "compact" halves the horizontal padding to var(--s-2) (8px) on every cell + sortable-header button — on a 9-column table that's ~144px of column budget bought back for content. Vertical padding is left at 12px so row height stays scan-friendly. Per-table; doesn't interact with the global body[data-density] mode (which targets <Inbox>, <DashboardShell> rows, etc. — <DataTable> is intentionally outside that ladder so a CRM page can mix dense tables with comfortable inboxes without the global mode picking sides). Best for numeric-heavy tables (counters, percentages, money) on /customers/health, /payments, persona surfaces.

Notes

  • R35-A already shipped in v1.31.0. Spark's R35-A asked for resizableColumns + storageKey props on <DataTable> for drag-to-resize columns with localStorage persistence. That's exactly what v1.31.0's R32 close-out shipped — but with the more comprehensive tableKey="..." API that unlocks resize and reorder and show/hide all in one prop. R35-A is satisfied by tableKey; the per-column resizable: false opt-out Spark wanted is also already in. No kit change needed for R35-A — Spark just needs to use tableKey instead of the proposed prop names.
  • R34 (naming nit) acknowledged, no kit change. Spark also flagged that MarketingFeatureGrid has an associated type but no MarketingFeatureIcon icon export. Naming is fine; no rename needed. Logged the team's docs-page choice (RocketIcon) for context.
  • All five additions are opt-in. Existing consumers see no behaviour change. SectionCard markup is identical when subtitle is unset; Switch fires onChange first when both handlers are set; CodeBlock figcaption-with-copy is unchanged when copyable is omitted; DataTable padding is unchanged when density is omitted.
  • All gates green: typecheck ✓, markup-equivalence ✓, check:docs ✓, CSS / UI / kit-site builds.
  • Pair with @magicblocksai/css@1.33.0.

Round R33–R35 mapping

| spark-id | kit-id | what shipped | |---|---|---| | R33-A (open) | R33-1 | <SectionCard subtitle> slot + .section-card-head-text + .section-card-subtitle CSS | | R33-B (open) | R33-2 | <Switch onCheckedChange> higher-level boolean handler | | R33-C (open) | R33-3 | <CodeBlock copyable> corner-overlay copy button + .codeblk-copy-corner CSS | | R34 (open) | R33-4 | <GithubIcon> + <BranchIcon> source-control glyphs | | R35-A (open) | — | Already shipped in v1.31.0 as tableKey prop (more comprehensive than the proposed resizableColumns + storageKey) | | R35-B (open) | R33-5 | <DataTable density="compact"> per-table compact-density mode |


[1.32.0] — 2026-05-09

Patch — Website Round W7 (R025 + R026 + R027 + R028 + R029). Five mobile-layout bugs across kit narrative components — every fix is scoped to mobile breakpoints (≤640px / ≤720px / ≤768px); desktop renders identically to v1.31.0. Full close-out at WEBSITE-FIXES-LOG.md Round W7.

Fixed

  • <RaceTimeline> blends both scenarios on mobile (R025). Pre-1.32.0 the column-collapse on mobile rendered rows in source order (left-1, right-1, left-2, right-2, …) — the two-timeline mental model was destroyed and visitors read it as one nonsensical interleaved sequence. Fix: mobile media query now uses CSS order to stack all left ticks (without-MagicBlocks scenario) under the bad heading, then all right ticks (with-MagicBlocks scenario) under the good heading. Visitor reads scenario A top-to-bottom, then scenario B top-to-bottom — the cleaner mobile rhythm Spark requested. The display: contents pair-wrappers in the JSX let order apply to children as direct grid items. Pure-CSS fix; no TSX change.
  • <ConversationPreview> .cv-msg escapes the device frame (R026). The website team observed message bubbles rendering 616px above their .cv-thread parent on /use-cases/prequalify-leads at 375×812. Their diagnosis suspected .cv-msg absolute positioning — but the kit's .cv-msg rule has no positioning; the bug was natural-flow placement going wrong inside a tall fixed-aspect parent (.device.phone .device-viewport whose aspect-ratio: 9/19.5 produces a 325×705 viewport on mobile while .cv content is shorter). Fix: anchor the layout deterministically — .cv becomes a flex column (head + thread + foot stack vertically); .cv-thread flex-grows to fill remaining space (flex: 1 1 auto; min-height: 0). Messages flow inside the thread regardless of parent height. Desktop unchanged (parents on desktop have natural height; flex-grow with no height constraint resolves to auto).
  • Hero StatStack overlaps HeroLiveDemo on mobile (R027). Consumer-side composition bug — the website's hero composes HeroLiveDemo + StatsStrip and the mobile reflow at ~375px caused stat numbers to paint on top of the phone illustration. Fix: new .kit-hero CSS recipe utility class. Consumers wrap their hero in <section class="kit-hero"> with three slot classes (.kit-hero-text, .kit-hero-visual, .kit-hero-proof); the recipe handles desktop side-by-side layout and mobile reflow (text → visual capped at 360px centred → proof) so the proof block always stacks below the visual. Documented in HeroLiveDemo.md §"Hero composition recipe".
  • <DecayCurve> axis labels illegible on mobile (R028). SVG text scales with the viewBox; at narrow viewports the chart shrinks to ~280px wide and the 11px axis labels render at ~3px (illegible). Fix: at ≤640px wrap the chart in a horizontally-scrollable scope with the SVG's min-width: 480px + a soft right-edge mask cue (same idiom as <DataTable> mobile + <PressStrip layout="scroll">). The chart paints at min 480px wide; the visitor swipes horizontally to see the full curve while every label stays at its 11px readable size. The website team's R028 entry preferred a "2-state comparison instead of full curve" alternative — that's a real future enhancement requiring component restructuring; this round ships the CSS-only relief and leaves room for the bigger redesign later.
  • <EngineBlock> channel chips disappear on mobile (R029). The kit's mobile rule explicitly hides the SVG orbit (display: none on .engine-orbit) at ≤720px — that broke the "we work across multiple channels" beat on every mobile visitor to /how-it-works, /, and /built-for-production. Fix: TSX renders an additional .engine-channel-strip HTML strip below the engine-core listing the same 4 channel labels as inline pills with an "Across" caption; CSS hides the strip on desktop (where the orbit owns the channels) and shows it on mobile (where the orbit is hidden). Channel labels reuse the channels prop (defaults to ["SMS", "MAIL", "CHAT", "VOICE"]).

Notes

  • All five fixes are mobile-only. Desktop renders identically to v1.31.0 — every change is wrapped in @media (max-width: ...) or applies to a mobile-shown HTML element. Existing kit-site chapter demos and dashboard-shaped consumers see no behaviour change.
  • No breaking changes. R025–R028 are CSS-only. R029 adds one new HTML element to <EngineBlock>'s render tree (the .engine-channel-strip) but it's hidden on desktop via CSS; markup-equivalence fixtures match.
  • All gates green: typecheck ✓, markup-equivalence (25/0/58) ✓, check:docs (346 snippets) ✓, CSS / UI / kit-site builds.
  • CSS bundle: 590450 / 386504 bytes. Tokens-only subpath unchanged.
  • Pair with @magicblocksai/css@1.32.0.

Round W7 mapping

| website-id | kit-id | what shipped | |---|---|---| | R025 (open) | W7-R025 | RaceTimeline mobile reflow via CSS order — sequential stacked lists | | R026 (open) | W7-R026 | ConversationPreview .cv flex-column + .cv-thread flex-grow anchor | | R027 (open) | W7-R027 | New .kit-hero CSS recipe utility class for hero compositions; HeroLiveDemo.md doc | | R028 (open) | W7-R028 | DecayCurve mobile horizontal-scroll with min-width: 480px + mask-image cue | | R029 (open) | W7-R029 | EngineBlock mobile-only .engine-channel-strip HTML strip; reuses channels prop |

Website queue: empty

This round closes the open queue from the website team's BRAND_KIT_REQUESTS.md. Whatever they file next is the next round.


[1.31.0] — 2026-05-09

Minor — Spark Round 30 (R31 + R32 by Spark numbering). The <DataTable> user-customisable column layout round. R31 ships the small developer-side opt-out for chip-shaped cells (truncate: false per column); R32 ships the major user-side knob (tableKey prop unlocking drag-to-resize + drag-to-reorder + show/hide columns + reset, all with localStorage persistence). Both shipped together because they're complementary — R31 fixes the truncated-chip display bug; R32 fixes the underlying "consumer-sized columns don't fit every user's workflow" problem of which the truncated chip is one symptom. Full close-out at SPARK-FIXES-LOG.md Round 30.

Added (R31 — truncate: false per column)

  • DataTableColumn.truncate?: boolean (default true). When false, applies .is-no-truncate to the cell, disabling text-overflow: ellipsis so chip / pill / badge / button cells render complete instead of being painted with a stray for any few-pixel overflow. The historic kit default (true) is correct for cells with string-shaped content (names, domains, subject lines) — it's wrong only for cells whose render returns a single inline visual unit. Spark hit this on Support's priority chip; the workaround was over-sizing the column to fit the chip + cell padding, which is a leaky abstraction (consumers had to know chip intrinsic width + cell padding + a font-metrics fudge factor). truncate: false lets consumers author chip-bearing columns at the right intrinsic width without the fake-ellipsis artefact.

Added (R32 — user-customisable column layout)

  • DataTableProps.tableKey?: string — when set, unlocks four user-facing affordances: 1. Drag-to-resize columns — hover the right edge of any header cell, the cursor switches to col-resize, drag commits a new width. Persists to localStorage under magicblocks.data-table.${tableKey}.widths. Min-width clamped to 60px. 2. Drag-to-reorder columns — drag a header, drop on another to insert before/after based on cursor position relative to the target's midpoint. Persists to magicblocks.data-table.${tableKey}.order. HTML5 drag-and-drop (no new npm dependency). 3. Show / hide columns — kebab menu in a new chrome bar above the grid lists every column with checkboxes. Hidden set persists to magicblocks.data-table.${tableKey}.hidden. 4. Reset columns — menu item at the bottom of the kebab menu clears all three localStorage keys at once and re-reads the consumer's column definitions.
  • Per-column gating fields on DataTableColumn (all default true): - resizable: false — pin the column's width. - reorderable: false — pin the column's position. - hideable: false — appears in the show/hide menu but the checkbox is disabled with a "Pinned" badge. Use for primary-key columns the table is meaningless without (e.g. a "Name" column).
  • useDataTableLayout(tableKey) hook — exported from @magicblocksai/ui. Returns the layout state + setters (setWidth, setOrder, toggleHidden, setHidden, reset). Pass tableKey={undefined} to receive a no-op layout (always empty; setters are silent). Lets consumers read or mutate the layout state externally — e.g. to render a sibling "Reset columns" button outside the table chrome, or mirror the layout to a different surface.
  • applyDataTableLayout(columns, layout) helper — pure function that returns columns in user-defined order, with width overrides applied, and hidden columns filtered out. Generic over the column type — only requires a stringifiable key field. Lets consumers apply the same layout state to non-DataTable surfaces (printable views, export configs, etc.).

CSS additions

  • .data-table-cell.is-no-truncate (R31) — disables text-overflow: ellipsis; preserves white-space: nowrap.
  • .data-table-chrome (R32) — the chrome bar above the grid hosting the kebab trigger.
  • .data-table-menu-trigger + .data-table-menu family — kebab + popover; uses the kit's existing --sh-2, --sh-focus, animation tokens.
  • .data-table-resize-grip — 8px-wide invisible-by-default hit target on the right edge of each header cell with a 2px tinted bar shown on hover via ::after.
  • .data-table-th.is-reorderable / .is-dragging / .is-drop-before / .is-drop-after — drag-state visual feedback. Drop indicator is a 2px accent-coloured bar at the target cell's leading or trailing edge.

Notes

  • Both behaviours are gated on tableKey. Without it, <DataTable> is byte-equivalent to v1.30.0 — no chrome bar, no kebab, no resize-grips, no draggable headers. The kit ships zero customisation UI for tables that don't opt in.
  • No new npm dependencies — uses HTML5 drag-and-drop for reorder, native pointer events for resize. The kit's existing useLocalStorage (v1.7.0+) backs the persistence layer.
  • SSR-safeuseLocalStorage returns the fallback during server render and on first client render, then hydrates on mount. This produces a single column-layout flicker on first paint for tables with non-default saved layouts; documented as a tolerated trade-off matching <ThemeToggle> behaviour.
  • Layered overrides, not a data binding. The consumer's columns prop remains authoritative on first load and after Reset. Saved layouts are an override on top — when the consumer adds a new column, it appears at the end of the user's saved order automatically; when the consumer removes a column, the user's order silently drops the dead key.
  • All gates green: typecheck ✓, markup-equivalence (25/0/58) ✓, check:docs (346 snippets) ✓, CSS / UI / kit-site builds.
  • CSS bundle: 583810 / 385031 bytes. Tokens-only subpath unchanged.
  • Pair with @magicblocksai/css@1.31.0.

Companion features deferred

Spark's R32 entry mentioned two "worth considering" items that didn't ship in this round:

  • Column-pin (sticky-left / sticky-right) — useful for wide tables on desktop (the leading "Name" column stays visible while horizontal scrolling). Real feature, real demand, but requires CSS position: sticky on the cell + careful interaction with the existing mask-image mobile scroll cue. Deferred to a follow-up round.
  • Saved named layouts (presets per tableKey) — power users who slice the same table multiple ways. Lower priority and a non-trivial UI surface. Deferred indefinitely until a concrete consumer ask lands.

Spark migration

After upgrading to @magicblocksai/ui@1.31.0 + @magicblocksai/css@1.31.0:

1. Restore the LifecyclePill columns to their natural width on Contacts, Companies, CustomersWork, Expansion. The 175px workaround Spark flagged as "surgical fix; will be retired when R32 lands" can come out — pass tableKey="spark.contacts" (etc.) on each table; users will resize as they need. 2. Add truncate: false to every column whose render returns a chip / pill / badge / button. Spark's specific call-out is the Support priority + status chips, but the round notes "likely lurks on every other chip / pill / badge column" — sweep them. 3. Pin primary-key columns with hideable: false so users can't hide the column the table is meaningless without (e.g. the Name column on Contacts). 4. Pick stable, namespaced tableKey values"spark.tickets", "spark.contacts", "spark.deals", etc. The key participates in the localStorage path; collisions across pages would corrupt each other's saved layouts.

Round 30 mapping

| spark-id | kit-id | what shipped | |---|---|---| | R31 (open) | R31 | DataTableColumn.truncate?: boolean + .data-table-cell.is-no-truncate CSS | | R32 (open) | R32 | DataTable.tableKey?: string + useDataTableLayout hook + applyDataTableLayout helper + per-column resizable / reorderable / hideable flags + chrome bar + kebab menu + resize-grip + drag-and-drop reorder + Reset menu item |

Spark queue: empty

This round closes the open Spark queue. Whatever Spark files next is the next round.


[1.30.0] — 2026-05-09

Patch — Spark Round 29 (R30 by Spark numbering). Singleton CSS-only fix following the same shape as R29 (v1.29.0), one component family over. Lifts the v1.9.3 R9-1 chrome-strip rule from direct-child (>) to descendant for the four on-root-chrome list primitives so it survives consumer wrapper-div compositions. Full close-out at SPARK-FIXES-LOG.md Round 29.

Fixed

  • <Inbox> (and siblings) doubled-up chrome inside bucket-wrapped <SectionCard> layouts (R30). The kit's v1.9.3 R9-1 chrome-strip family used direct-child selectors (.section-card-body > .inbox, > .checklist, > .data-table, > .calendar, > .panel) — correct for the simple <SectionCard padded={false}><Inbox/></SectionCard> case, but broken for the bucket-wrapped pattern consumers reach for to interleave date-group headers between list groups:

``html <SectionCard padded={false}> <div> <!-- bucket wrapper --> <div class="spark-bucket-head">…</div> <!-- "LAST WEEK" header --> <Inbox> <!-- grandchild — R9-1 missed it --> <InboxRow … /> </Inbox> </div> <div>… next bucket …</div> </SectionCard> ``

Pre-1.30.0 the inner <Inbox> was a grandchild of .section-card-body and inherited its full card chrome (border + radius + paper bg), doubling up against the outer SectionCard. Visible on Spark's email-replies / multi-bucket inbox pages.

Fix: lift the chrome-strip from direct-child to descendant for the four on-root-chrome primitives (.inbox, .checklist, .calendar, .panel). .data-table stays at direct-child because DataTable's chrome lives on a known inner element (.data-table-grid, see R29's sibling rule in v1.29.0) — there's no wrapper-div ambiguity to handle there.

Notes

  • No breaking changes. The descendant selector is strictly more permissive — every consumer relying on the old > behaviour still gets the chrome-strip; bucket-wrapped consumers now also do.
  • Edge case — a kit consumer who genuinely wants a deeply-nested .inbox/.checklist/.calendar/.panel to keep its chrome inside a SectionCard body (e.g. a literal "card within a card" composition with intentional doubled chrome) would now lose it. This is exotic; consumers in that case can re-add the chrome on the inner element from their own stylesheet.
  • CSS-only patch. No TSX changes, no new prop. Same shape as R29 (v1.29.0).
  • All gates green: typecheck ✓, markup-equivalence (25/0/58) ✓, check:docs (346 snippets) ✓, CSS / UI / kit-site builds.
  • CSS bundle: 577921 / 381474 bytes. Tokens-only subpath unchanged.
  • Pair with @magicblocksai/css@1.30.0.

Spark migration

After upgrading to @magicblocksai/ui@1.30.0 + @magicblocksai/css@1.30.0:

  • apps/web/src/index.css — delete the scoped .section-card-body .inbox { border: 0; border-radius: 0; background: transparent } override that fixed the bucket-wrapped email-replies layout. The kit ships the equivalent rule by default now.

The lesson, generalised

Two consecutive rounds (R29 v1.29.0 + R30 v1.30.0) caught the same shape of bug — direct-child selectors are brittle to consumer markup variations. R29's fix kept > because DataTable's chrome lives on a known inner element (.data-table-grid) which the consumer doesn't wrap. R30's fix lifts to descendant because the four on-root-chrome primitives' chrome lives on the root, and any wrapper between root and section-card-body breaks the rule.

The audit-on-markup-change rule from R29's close-out generalises: any time the kit team adds an interleaved-header pattern (or any other "wrap each list in a div" recipe) to a chapter or doc, the R9-1 chrome-strip family should be re-checked — both directions. The kit-side check is mechanical: grep _shared.css for .section-card-body > selectors and confirm the wrapped-grandchild case still resolves correctly.


[1.29.0] — 2026-05-09

Patch — Spark Round 28 (R29 by Spark numbering). Singleton CSS-only fix for a long-standing no-op in the kit's chrome-strip rule for list-roots inside SectionCards. Full close-out at SPARK-FIXES-LOG.md Round 28.

Fixed

  • <DataTable> doubled-up chrome inside <SectionCard padded={false}> (R29). The kit's v1.9.3 R9-1 chrome-strip rule (.section-card-body > .inbox, .section-card-body > .checklist, .section-card-body > .data-table, .section-card-body > .calendar, .section-card-body > .panel { border: 0; border-radius: 0; background: transparent }) correctly stripped chrome from .inbox, .checklist, .calendar, and .panel (all chrome-on-root primitives) but was a no-op for .data-table — DataTable's chrome (border + radius + paper background) lives on the inner .data-table-grid element, not the outer .data-table flex wrapper. Result: a 14px-radius outer SectionCard around a 10px-radius inner DataTable grid, both with the same hair-coloured border. Visible on every Spark list page (/sequences, /contacts, /deals, /companies, /leads, /broadcasts, /triage).

Fix: sibling rule that targets the actual chrome-bearing inner element: .section-card-body > .data-table > .data-table-grid { border: 0; border-radius: 0; background: transparent }. Zero-config — no prop, no modifier; the canonical <SectionCard padded={false}><DataTable /></SectionCard> shape just renders correctly now. Same idiom as the rest of the chrome-strip family.

Notes

  • No breaking changes. The fix only takes effect when DataTable sits as a direct child of .section-card-body — standalone DataTable use (page-top-level inside a regular wrapper) keeps its full card chrome.
  • CSS-only patch. No TSX changes, no new prop. The kit's React <DataTable> is unchanged.
  • All gates green: typecheck ✓, markup-equivalence (25/0/58) ✓, check:docs (346 snippets) ✓, CSS / UI / kit-site builds.
  • CSS bundle: 576906 / 381474 bytes. Tokens-only subpath unchanged.
  • Pair with @magicblocksai/css@1.29.0.

Spark migration

After upgrading to @magicblocksai/ui@1.29.0 + @magicblocksai/css@1.29.0:

  • apps/web/src/index.css — delete the scoped .section-card-body .data-table-grid { border: 0; border-radius: 0; background: transparent } override. The kit ships the equivalent rule by default now.

Why ship as :where()-style auto-strip rather than <DataTable bare> prop

Spark's R29 entry offered both options. Going with the zero-config CSS rule because:

1. Matches the kit's existing pattern — the v1.9.3 R9-1 family handles .inbox, .checklist, .calendar, .panel automatically; making DataTable the only opt-in primitive in the family would be inconsistent. 2. Zero new API surface — no prop to teach, document, or maintain. The canonical <SectionCard><DataTable /></SectionCard> shape just works. 3. Edge cases are exotic — consumers using a SectionCard body for something other than the "card-of-list" pattern with a chrome-wanting DataTable inside are rare. If anyone hits that, they can override by re-adding the chrome on the inner .data-table-grid from their own stylesheet.

The descendant selector is scoped tightly (> .data-table > .data-table-grid direct-children, not deep descendant) so nested compositions don't accidentally strip chrome from a DataTable that lives several layers deep inside the SectionCard body.


[1.28.0] — 2026-05-09

Minor — Website Round W6 (R021 + R022 + R023). Three-item CSS-only batch closing the open website-team queue. All three are polish items on existing primitives (no new components, no new props). Full close-out at WEBSITE-FIXES-LOG.md Round W6.

Fixed

  • .section.is-warm / .section--warm token rescope (R021) — extended to include --bg-paper (pinned to var(--paper, #FFFFFF)) and --hair-soft. Pre-1.28.0 the rule only rescoped --bg, --fg, --fg-soft, --fg-dim, --fg-faint, --hair — kit components inside warm sections that consume var(--bg-paper) (<HandoffCard>, <EcosystemRings> inner chips, anything with paper-elevated card surfaces) inherited the body-level dark-mode value (#2A3050 elevated ink) and rendered as dark blobs on cream surfaces in dark mode. The .section.is-ink rule already rescoped --bg-paper correctly; this brings .is-warm to symmetry.
  • .triptych margin-inline: auto (R022) — missed from the v1.25.0 W5-R018 narrative-component centring batch. The Triptych has max-width: 1080px but pre-1.28.0 had no margin-inline, so on a 1200px page-content measure it sat at the start of the line with all 120px of leftover space stuck on the right beneath a centred head. Same shape as the .scoreboard / .engine-block / .decay-curve / .happa-arc / .handoff-card rules that did get the W5-R018 treatment.
  • .ecosystem-rings .er-ring and .integration-hub .spokes line strokes (R023) — bumped stroke-width from 11.6 on both, and shifted the stroke colour to a more readable tint: - .ecosystem-rings .er-ring (inner): var(--hair)color-mix(in oklab, var(--ink) 22%, transparent) - .ecosystem-rings .er-ring.outer: 32% accent → 55% accent (the brand-pink anchor of the visual centre) - .integration-hub .spokes line: 35%-accent / --hair mix → 45%-accent solid

Pre-1.28.0 both components shipped dashed connectors at 1px / hair-tinted strokes — they read as faded ghosts on cream / paper marketing surfaces at 1× zoom, undermining what should be the visual centre of both components ("the engine connects everything"). The new defaults make the connectors clearly visible without losing the "ambient backdrop" feel.

Notes

  • No breaking changes. All three are CSS-only adjustments to existing rules. Stroke widths bumped 0.6px and tints adjusted within the same colour family — visually a touch-up, not a redesign.
  • Visual change scoped to consumers using .section--warm (R021), .triptych (R022), and the two narrative components (R023). Any other surface is unchanged.
  • All gates green: typecheck ✓, markup-equivalence (25/0/58) ✓, check:docs (346 snippets) ✓, CSS / UI / kit-site builds.
  • CSS bundle: 576104 / 381386 bytes. Tokens-only subpath unchanged.
  • Pair with @magicblocksai/css@1.28.0.

Website migration

After upgrading to @magicblocksai/ui@1.28.0 + @magicblocksai/css@1.28.0:

1. BaseLayout.astro .section--warm token-rescope override — delete; the kit pins --bg-paper to paper-white inside warm sections now. Kit cards (<HandoffCard>, <EcosystemRings> chips, etc.) render correctly in both themes. 2. BaseLayout.astro .triptych { margin-inline: auto } override — delete; the kit ships this rule by default now. 3. BaseLayout.astro er-ring + .spokes line stroke overrides — delete; the kit's defaults are now the values the website team was overriding to.

Round W6 mapping

| website-id | kit-id | what shipped | |---|---|---| | R021 (open) | W6-R021 | .section.is-warm + .section--warm rescope adds --bg-paper: var(--paper) and --hair-soft | | R022 (open) | W6-R022 | .triptych margin-inline: auto (W5-R018 family member that was missed) | | R023 (open) | W6-R023 | .ecosystem-rings .er-ring + .integration-hub .spokes line stroke bump + tint |

Website queue: empty

This round closes the last open kit-team item from the website team's BRAND_KIT_REQUESTS.md. Whatever the team files next is the next round.


[1.27.0] — 2026-05-09

Minor — Spark Round 27 (kit-side; R18 + R19 + R25 + R26 + R28 by Spark numbering). Five-item batch covering the open Spark queue accumulated since v1.26.0: a new icon, a Label-composability fix, an InboxRow grid-track fix, an InlineHeadline-inside-PageHeader CSS scope, and a SageDrawer composer-cap prop. Full close-out at SPARK-FIXES-LOG.md Round 27.

Added

  • <TrashIcon> (R18). Bin glyph for destructive-remove affordances on list rows / attachments / custom-field deletions. Distinct from <CloseIcon> (the X-glyph "dismiss / close" affordance) — Spark was using <CloseIcon> as a delete fallback, which read as "dismiss" not "delete." Matches the kit's standard 18×18 monoline spec; documented at packages/ui/docs/IconFamily.md.
  • <SageDrawer maxRows> prop (R28). Maximum rows the composer textarea grows to before scrolling internally. Default 3. Pre-1.27.0 the kit shipped a hardcoded max-height: 180px (~9 rows), which let the composer eat too much vertical real-estate in floating drawers with busy chat threads — the message stream above ended up cramped and the user lost sight of the last reply while typing a multi-line follow-up. Translates to a --sage-input-max-rows CSS custom property the .sage-input rule consumes via max-height: calc(var(--sage-input-max-rows, 9) * 1.45em + 20px). Same ergonomic shape as <Textarea rows>. The fallback 9 preserves pre-1.27.0 behaviour for any consumer rendering .sage-input outside a <SageDrawer> and not setting the variable.

Fixed

  • <InboxRow> 6-child overflow on desktop (R25). Pre-1.27.0 the .inbox-row grid template had 5 explicit tracks (4 base + the v1.22.0 R21-1 selectable variant) but <InboxRow> could render 6 children when ix-select + av + ix-body + ix-due + ix-priority + ix-actions were all present. Grid auto-flow pushed the 6th child onto a new row at column 1, so the ✓ complete button rendered in the bottom-left corner of any row that set due + priority + onComplete. Visible on /tasks, the home overview's Tasks/Upcoming card, the contact / company / deal detail LinkedTasks card, and any InboxPage section.

Fix: add a trailing auto track to both base templates. 36px 1fr auto auto36px 1fr auto auto auto (and the selectable variant gets the same treatment). Trailing unused auto tracks collapse to 0 width — no regression for rows with fewer children. The mobile rule (@media (max-width: 480px)) already accounted for all six slots via subgrid stacking, so this is desktop-only.

  • <PageHeader onTitleSave> title wraps every word inside flex parent (R26). When <InlineHeadline> was composed inside <PageHeader>'s display: inline-flex title slot, the kit's default word-break: break-word on .inline-headline-text collapsed the text node's CSS min-content to 1 character — the inline-flex parent shrank to ~icon + 1ch, then <InlineHeadline width: 100%> filled that tiny parent and the title wrapped at every word boundary even on wide viewports. "MagicBlocks Platform" rendered on two lines as MagicBlocks / Platform on the project detail page (and same on issue / ticket detail).

Fix: scoped CSS rule that swaps the page-header-title-slot's <InlineHeadline> to width: auto; max-width: 100% and the inner .inline-headline-text to white-space: nowrap; overflow: hidden; text-overflow: ellipsis; word-break: normal. Standalone <InlineHeadline> (e.g. inside a card body) keeps word-break: break-word for long-word safety; the page-header title gets ellipsis-at-edge — the right rhythm for a page-title slot anyway.

  • <Label> collapses gap when wrapping composite kit controls (R19). The kit's recommended pattern is the sibling layout (<Label htmlFor=…>Text</Label> <Input id=… />), but the nested-child pattern (<Label>Text<Input/></Label>) is what most consumers reach for first because it works for native <input> (the input's block-level rendering provides incidental gap). For composite kit controls (<DatePicker>, <Combobox>, <MultiSelect>, <Select>, <DateRangePicker>) the gap collapsed to zero — the label text and trigger button rendered flush.

Fix: :has() rule on .input-label that turns it into a flex column when it wraps any kit form control as a direct child. Both patterns now produce the same vertical rhythm (label text + 6px gap + control). Consumers wrapping their own custom controls can opt into the same layout via the new .input-label.is-stack modifier.

Notes

  • No breaking changes. All five fixes are either additive (new icon, new prop, new CSS rule) or strictly more permissive (the InboxRow grid template grows from 5 to 6 explicit tracks; the trailing auto collapses for fewer-child rows).
  • All gates green: typecheck ✓, markup-equivalence (25/0/58) ✓, check:docs (346 snippets) ✓, CSS / UI / kit-site builds.
  • CSS bundle: 574044 / 381275 bytes. Tokens-only subpath unchanged (16865 / 4992).
  • Pair with @magicblocksai/css@1.27.0.

Spark migration

After upgrading to @magicblocksai/ui@1.27.0 + @magicblocksai/css@1.27.0:

1. apps/web/src/index.css — delete the scoped @media (min-width: 481px) .inbox-row { grid-template-columns: ... 6-track } workaround for R25. Kit ships the 6th track by default now. 2. Project / issue / ticket detail page CSS — delete the scoped .page-header-title-text .inline-headline { width: auto } + .inline-headline-text { white-space: nowrap } overrides for R26. Kit ships those rules by default now (scoped to PageHeader's title slot). 3. apps/web/src/routes/admin/EmailDeliverabilityPage.tsx — replace the <CloseIcon> "remove" buttons with <TrashIcon>. Same for any other surface where a close-glyph was serving double-duty as a delete affordance (attachment lists, custom-field deletions, etc.). 4. apps/web/src/routes/ProjectDetailPage.tsx (PlanCycleDialog) — drop the hand-rolled <label style={{display:'flex',flexDirection:'column',gap:6}}> workaround. Wrap the <DatePicker> in <Label> directly; the :has() rule does the right thing now. 5. Sage drawer override — delete .sage-drawer .sage-input { max-height: 80px } per-page CSS. Pass <SageDrawer maxRows={3}> instead (3 is the kit default, so just dropping maxRows works too).

Round 27 mapping

| spark-id | kit-id | what shipped | |---|---|---| | R18 (open) | R18 | <TrashIcon> added to icons.tsx + barrel + carve-out + IconFamily.md catalogue | | R19 (open) | R19 | :has() rule on .input-label for composite-control children + .input-label.is-stack opt-in modifier | | R25 (open) | R25 | .inbox-row grid template extended from 4/5 → 5/6 explicit tracks | | R26 (open) | R26 | Scoped white-space: nowrap + ellipsis on .page-header-title-text .inline-headline-text | | R28 (open) | R28 | <SageDrawer maxRows> prop + --sage-input-max-rows CSS custom property |

Spark queue: empty

This round closes the last open kit-team item from the Spark file. Whatever Spark files next is the next round.


[1.26.0] — 2026-05-08

Minor — Spark Round 26 (R27 by Spark numbering). Singleton round adding the <Inbox striped> prop Spark requested, plus a small cosmetic CSS fix for the last-row hairline bleeding through <SectionCard> rounded corners. Full close-out at SPARK-FIXES-LOG.md Round 26.

Added

  • <Inbox striped> prop (R27). Boolean, default false. Same behaviour as the existing banded prop (which is now deprecated alongside) but with the friendlier name matching <DataTable striped>. When striped={true}, every other row gets a hairline-soft tint to give long mailbox-style lists rhythm to scan. The kit's CSS class is unchanged (.inbox.inbox-banded) so vanilla CSS-class consumers see no behaviour change.

The kit's default has always been NOT striped. Spark's R27 ask read as "default is striped, please add an opt-out" — that was a misread of the kit's current state (the v1.12.0 R11-3 zebra rule is scoped to .inbox-banded, opt-in only). The right migration for Spark's /inbox page is to drop the implicit banded (or pass striped={false} explicitly to override any upstream config).

Deprecated

  • <Inbox banded> prop. Alias for striped — same behaviour. Kept for back-compat with v1.12.0 call sites; will be removed in v2.0.0. When both striped and banded are set, striped wins.

Fixed

  • Last-row hairline bleeds through <SectionCard> rounded corners. When an <Inbox> (or <Checklist>, or <DataTable>) is the direct child of a <SectionCard>, the row's border-bottom: 1px solid var(--hair-soft) paints across the bottom edge of the last row — and that 1px line gets clipped by the card's overflow: hidden rounded corner curve, producing a tiny hairline arc that cuts inside the bottom-left and bottom-right corners. v1.26.0 drops the last-row border on every list-shaped child of .section-card-body so the card's curve owns the bottom edge cleanly.

Notes

  • No breaking changes. <Inbox> consumers without striped or banded see no change (the kit default has always been clean rows). <Inbox banded> consumers continue to render banded — same CSS class under the hood, the prop is just deprecated. The cosmetic last-row fix is invisible on cards where the layout was already fine; visible only where the artefact existed.
  • CSS bundle grows ~10 lines (the new last-row rule + comment block).
  • All gates green: typecheck ✓, markup-equivalence (25/0/58) ✓, check:docs (346 snippets) ✓, CSS / UI / kit-site builds.
  • Pair with @magicblocksai/css@1.26.0.

Spark migration

After upgrading to @magicblocksai/ui@1.26.0 + @magicblocksai/css@1.26.0:

1. /inbox page CSS overrides — delete the .spark-page-feed .inbox-row, .spark-page-feed .inbox-row:nth-child(2n) { background: var(--bg-paper) } rules. The kit's default is already clean rows; just don't pass banded (or striped) on the inbox-inside-SectionCard instances. 2. .spark-page-feed .section-card-body > .inbox:last-child .inbox-row:last-child { border-bottom: 0 } — delete; the kit ships this rule by default now for every list-shaped child of <SectionCard>. 3. Existing <Inbox banded> call sites — keep working as deprecated alias. Optional cleanup: rename bandedstriped for consistency with <DataTable striped>. The TypeScript prop carries @deprecated so editor hovers will surface the rename.


[1.25.0] — 2026-05-08

Minor — Website Round W5 (R012 + R014 + R015 + R018). Narrative-component layout batch — four asks shipped together about how kit narrative components and dark-mode tokens behave on real marketing surfaces. Closes the website-team queue (R006–R020 all shipped or documented). Full close-out at WEBSITE-FIXES-LOG.md Round W5.

Added

  • <HeroBloomCanvas bleed> prop (W5-R015). Drops the canvas's default border-radius: var(--r-lg) for full-bleed marketing-page heroes. The default rounded variant stays correct for kit-chapter use (canvas inset on a paper page); bleed is the marketing-hero opt-in for contexts where the canvas extends to the section edges and rounded corners produce a visible "card on a page" gap. Maps to .hero-bloom-canvas.is-bleed CSS.

Fixed

  • Dark-mode tokens recognise data-theme="dark" on either <html> OR <body> (W5-R014). Pre-1.25.0 every dark-mode selector was scoped to body[data-theme="dark"] — the conventional no-flash pattern (run-before-paint script in <head>) sets the attribute on <html> first because <body> hasn't parsed yet, so kit tokens stayed in light values until body parsed. Fix: mass-replace every body[data-theme="dark"] selector with :is(html, body)[data-theme="dark"] (46 rules). :is() keeps the original specificity (both html and body are 0,0,1 type selectors) so cascade is identical; the :is() ancestor accepts whichever root the consumer prefers. The website team can now drop their dual-set workaround and put data-theme on <html> only.
  • Narrative components centre by default (W5-R018). Pre-1.25.0 <Scoreboard>, <EngineBlock>, <DecayCurve>, <HappaArc>, and <HandoffCard> had their own max-width constraints internally but no margin-inline: auto — when dropped into a wider section under a centered head, they sat at the start of the line and read as left-aligned beneath a centered headline. The siblings (<EcosystemRings>, <IntegrationHub>, <GuardianShield>) already shipped centred. v1.25.0 aligns the family — every chapter-11 narrative component now margin-inline: autos by default. The <HandoffCard> was the worst offender (380px constraint inside a 1200px section = 410px off-axis); now centres cleanly.

Documentation

  • <HeroBloomCanvas> fixed-aspect-frame constraint documented (W5-R012). New "Layout — fixed-aspect canvas + sibling overflow" section in HeroBloomCanvas.md. The canvas is intentionally fixed-aspect (aspect-ratio: 16/7; min-height: 320px) with absolutely-positioned .hbc-content — children sit centred inside the cinematic frame and don't grow the canvas. The fix is always the sibling pattern (consumer proof bands / stats strips render beneath the canvas, not inside it), never position: relative; height: auto on .hbc-content — that would break the fixed-aspect contract every kit chapter demo relies on. Inline ⚠️ comment added in _shared.css near the rule pointing at the doc.

Notes

  • :is(html, body)[data-theme="dark"] keeps specificity identical (0,1,1 — same as body[data-theme="dark"]). No cascade reshuffle; existing rules that override or are overridden by these selectors behave the same way.
  • CSS bundle grows by ~30 lines (the .hero-bloom-canvas.is-bleed rule, margin-inline: auto declarations on five narrative components, comment blocks). The dark-mode selector replacement adds bytes via the longer selector but doesn't add rules. Tokens-only subpath: 16064 → 16865 bytes (~5KB minified, marginal increase from the wider dark-token selector).
  • No breaking changes. Every existing consumer keeps working — <HeroBloomCanvas> defaults to rounded; dark-mode tokens still cascade when consumers set data-theme on body (the original pattern); narrative components that were previously left-aligned will now centre, which most consumers explicitly wanted (the siblings already centred — this is bringing the family in line).
  • All gates green: typecheck ✓, markup-equivalence (25/0/58) ✓, check:docs (346 snippets) ✓, CSS / UI / kit-site builds.
  • Pair with @magicblocksai/css@1.25.0.

Website migration

After upgrading to @magicblocksai/ui@1.25.0 + @magicblocksai/css@1.25.0:

1. BaseLayout.astro no-flash script — drop the dual-set workaround (the deferred <body> set after DOMContentLoaded). Setting data-theme on <html> only is now sufficient for the kit's tokens to cascade correctly. 2. .home-hero .hero-bloom-canvas { border-radius: 0 } consumer override — delete; pass <HeroBloomCanvas bleed> instead. 3. <StatsStrip> proof band placement — already correct as a sibling of <HeroBloomCanvas> (the website team's local fix). Keep it. The kit now documents this as the canonical pattern in HeroBloomCanvas.md. 4. Narrative-component centring overrides — the :global .scoreboard, .engine-block, .decay-curve, .happa-arc, .handoff-card { margin-left: auto; margin-right: auto } rule in index.astro — delete; the kit centres these by default now.

Round W5 mapping

| website-id | kit-id | what shipped | |---|---|---| | R012 (open) | W5-R012 | Doc + ⚠️ inline comment for <HeroBloomCanvas> fixed-aspect-frame constraint; sibling-overflow recipe in HeroBloomCanvas.md | | R014 (open) | W5-R014 | :is(html, body)[data-theme="dark"] selector (46 dark-mode rules updated) | | R015 (open) | W5-R015 | <HeroBloomCanvas bleed> prop + .hero-bloom-canvas.is-bleed CSS | | R018 (open) | W5-R018 | margin-inline: auto on .scoreboard, .engine-block, .decay-curve, .happa-arc, .handoff-card |

Website queue: empty

This round closes the last open kit-team item from the website team's BRAND_KIT_REQUESTS.md (R006–R020 all resolved; the team's log just needs to mark R006–R011 ✅ from v1.19.0). Recap of website items shipped:

  • W1 (v1.17.0) — R003 partial (tokens subpath), R004 (Drawer lazy-import doc), R005 (Scoreboard composition recipe)
  • W2 (v1.18.0) — R001 (<IndustryBar>), R002 (<CostCompare>)
  • W3 (v1.19.0) — R006 (--on-accent token), R007 (.hero reserved), R008 (--f-italic token), R009 (<EcosystemRings> dev-warn), R010 (HeroLiveDemo / ChatCompare freeze-frame doc), R011 (<PressStrip layout="scroll">)
  • W4 (v1.24.0) — R013/R016 (.section-head.is-stack), R017 (.section.is-ink/.is-warm), R019 (<Button variant="ink">), R020 (<PressStrip chrome="bare">)
  • W5 (v1.25.0, this) — R012, R014, R015, R018

Whatever the website team files next is the next round.


[1.24.0] — 2026-05-08

Minor — Website Round W4 (R016/R013 + R017 + R019 + R020). Marketing-page chrome batch — four asks shipped together because they're all about making the kit's primitives read correctly on real marketing surfaces (vs the kit chapter pages where they were authored). Closes the highest-impact website-team items per the user's recommended sequencing. Full close-out at WEBSITE-FIXES-LOG.md Round W4.

Added

  • <Button variant="ink"> (W4-R019, headline item). New filled CTA variant — ink bg + paper text — for sales-led marketing primary CTAs. Per the brand kit's own 02-colour.html §2.2: "Pink-700 is the signature accent. … the colour that glows without shouting." Pink-as-primary on every marketing CTA inverts that — it makes the brand colour the resting paint instead of an accent. The ink variant restores the right separation: pink stays for accents (italic words, callouts, comparisons, in-app affirmative actions like Save/Apply), ink takes the marketing-CTA role (homepage hero, pricing page, "Watch a demo" / "Book a demo" / "Get started"). Hover state mixes 8% accent into the ink so the brand colour stays in the interaction model. Active mixes 14%. Dark-mode automatically inverts to a paper-on-ink-page button so the CTA still reads as primary in dark contexts.
  • <PressStrip chrome="bare"> (W4-R020). New chrome variant for marketing-page trust strips. The default chrome="card" (back-compat) renders the strip with paper bg + hairline border + 14px radius — correct for kit chapter pages where the strip is a card-on-paper demo, wrong on real marketing pages where the rounded card chrome reads as a "weird floating block." "bare" strips the chrome (transparent bg, no border / radius / padding) so the strip becomes a thin trust signal that flows with the page rhythm. The token rescope inside .press-strip stays so eyebrow + label tones still resolve correctly. Pair with layout="scroll" (v1.19.0 W3-R011) for the canonical homepage trust-strip recipe.
  • .section.is-ink / .section.is-warm modifiers (W4-R017). Surface-tone modifiers for marketing sections. Same idiom as the kit's existing .scoreboard.dark, .cc-col, and .hero-bloom-canvas[data-variant="war-room"] token-rescope pattern — the modifier rescopes --bg, --fg, --fg-soft, --fg-dim, --fg-faint, --hair, --hair-soft to ink-mode (or warm-mode) values inside the section. Every kit text primitive (.eyebrow, .lede, .caption, .micro, .section-title, etc.) adapts its colour automatically without per-class .on-ink modifier proliferation. Pre-1.24.0 the website team had to ship a .section-head--on-ink modifier with hardcoded paper-tinted overrides; now the surface modifier on the wrapping .section does the work. BEM-style .section--ink / .section--warm aliases also accepted for consumers who prefer that convention.
  • .section-head.is-stack modifier (W4-R016/R013). Centred editorial variant of the .section-head row. The default .section-head is a dashboard-style flex row (justify-content: space-between) for app-surface "Title | Actions" headers — the website team's marketing pages need the inverse: a vertically-stacked centered triplet (eyebrow + h2 + lede). The new .is-stack modifier flips display to block, centres children via margin: 0 auto, and caps <p> lede children at 64ch. Pair with .section.is-ink / .section.is-warm for surface-tone-aware rendering.

Documentation

  • Button.md — full rewrite of the "Picking a variant" section. New table mapping surface (marketing / in-app / destructive / secondary / tertiary) → variant. New "Migration from a variant="primary"-only world" section with the audit recipe.
  • PressStrip.md — new "Marketing-page recipe — chrome="bare" + layout="scroll"" section with the canonical homepage trust-strip pattern.
  • ReservedClassNames.md.section-head added to the reserved-class table with a recommendation to reach for .section-head.is-stack (v1.24.0+) for marketing-page editorial headers.
  • _shared.css carries inline ⚠️ comments at the new rules pointing at the docs.

Decisions

  • Ship ink as a new variant, not as a surface-flag on primary. Keeping btn-primary (pink) intact preserves every existing call site's behaviour. The decision tree at the consumer site is "marketing surface → use ink; app surface → use primary" — clean cognitive model, no implicit-context resolution. Forcing every consumer to migrate to a different default (or have the variant adapt based on parent surface) would be a hard breaking change for thin payoff.
  • .section.is-ink rescopes tokens, doesn't ship per-class .on-ink modifiers. .eyebrow, .lede, .caption, .micro, .section-title, every other kit text primitive — they already use the kit's token system. The modifier rescopes the tokens; every primitive inherits automatically. Per-class .eyebrow.on-ink modifiers would lock the kit into knowing which primitives need ink-aware rendering, instead of letting the token system handle it.
  • .section-head.is-stack modifier, not a new .marketing-section-head class. Same shape as <Kanban mobileLayout> (R18-2) and <PressStrip layout> (R11) — opt-in modifier, default unchanged for back-compat. The website team's existing .section-head overrides become a one-class swap.

Notes

  • No breaking changes. Every existing consumer keeps working — <Button variant="primary"> paints pink as before; <PressStrip> defaults to chrome="card"; .section-head defaults to its dashboard flex layout. Every new variant / modifier is additive opt-in.
  • CSS bundle grows ~85 lines (.btn-ink family + .press-strip.is-bare + .section.is-ink/.is-warm + .section-head.is-stack). Tokens-only subpath unchanged in shape — the modifier rules live below /* @TOKENS-END */.
  • All gates green: typecheck ✓, markup-equivalence (25/0/58) ✓, check:docs (346 snippets) ✓, CSS / UI / kit-site builds.
  • Pair with @magicblocksai/css@1.24.0.

Website migration

After upgrading to @magicblocksai/ui@1.24.0 + @magicblocksai/css@1.24.0:

1. Top-nav Watch a Demo → + hero Watch a Demo — replace local .btn-ink port with <Button variant="ink">. Drop the :global([data-theme='dark']) inversion override (kit handles it). 2. .press-strip consumer override in BaseLayout.astro — delete; replace the <PressStrip> call site with <PressStrip chrome="bare" layout="scroll" items={…} />. 3. .section-head consumer overrides in index.astro scoped CSS — delete; replace <header class="section-head"> with <header class="section-head is-stack">. 4. .section--on-ink / .section-head--on-ink modifiers in index.astro — delete; replace with .section.is-ink (or BEM-style .section--ink) on the wrapping section. Eyebrow + lede + section-title all adapt their colour automatically. 5. R006–R011 stale-log entries — mark them ✅ RESOLVED in BRAND_KIT_REQUESTS.md (they all shipped in v1.19.0 — the website team's log just got out of sync).

Round W4 mapping

| website-id | kit-id | what shipped | |---|---|---| | R013 (open) | W4-R013 | .section-head reserved-class doc + .section-head.is-stack modifier (consolidated with R016) | | R016 (open) | W4-R016 | Same as R013 — duplicate ask consolidated | | R017 (open) | W4-R017 | .section.is-ink / .section.is-warm surface-tone modifiers | | R019 (open) | W4-R019 | <Button variant="ink"> + .btn-ink CSS family + Button.md rewrite | | R020 (open) | W4-R020 | <PressStrip chrome="bare"> + .press-strip.is-bare modifier |

Deferred to W5 (next round)

  • R012<HeroBloomCanvas> .hbc-content is position: absolute; height: 623.4px; (children overflow)
  • R014 — Dark-mode tokens scoped to body[data-theme="dark"] (no-flash scripts set on <html> first)
  • R015<HeroBloomCanvas> has border-radius: 14px baked in (no bleed prop)
  • R018 — Narrative components don't margin-inline: auto by default

W5 ships next as a "narrative-component layout" round.


[1.23.0] — 2026-05-08

Minor — Spark Round 25 (kit-side; R20 + R22 by Spark numbering). The "DataTable + PageHeader composability" round per the user's recommendation. Two non-overlapping additive features: anchor-target exposure on <DataTable rowHref> rows (R20) and click-to-edit titles on <PageHeader> (R22) via a new <InlineHeadline> primitive. Closes Spark's queue from the latest hand-off message. Full close-out at SPARK-FIXES-LOG.md Round 25.

Added

  • <DataTable rowTarget> + rowRel props (R20). Exposes the underlying <a>'s target and rel attributes for rows rendered as anchors. Pass rowTarget="_blank" for "open every row in a new tab" (file-download tables, external-resource lists), or a (row) => "_blank" | "_self" | undefined callback for per-row targeting. When rowTarget resolves to "_blank" and rowRel isn't explicitly set, the kit auto-injects rel="noopener noreferrer" on those rows for the standard tabnabbing-protection pattern. Pre-1.23.0 consumers had to fall back to onRowClick + window.open(href, "_blank", "noopener,noreferrer"), which loses the cmd-click / middle-click / right-click "Open in new tab" affordances native anchors provide.
  • <InlineHeadline> primitive (R22). Click-to-edit page-title primitive matching headline typography (28px / 600 / display face). Resting state is a <button> with the headline; click swaps to a styled <input> with the same metrics so there's no visual jolt. Enter commits, Escape cancels, blur commits. Async onSave supported (kit doesn't manage in-flight state). Documented at packages/ui/docs/InlineHeadline.md. Listed in the kit-chrome carve-out (no chapter demo).
  • <PageHeader onTitleSave> + titlePlaceholder props (R22). When onTitleSave is set, the title slot becomes click-to-edit via <InlineHeadline> automatically — eyebrow / icon / summary / actions all stay in their normal positions; only the title gains hover/click state. Requires title to be a string (the kit dev-warns and falls back to a non-editable title for that render if it isn't). The standalone <InlineHeadline> is the right reach when your page chrome doesn't fit <PageHeader> (custom hero layouts, modal titles, drawer headers).

CSS

  • New .inline-headline family (~70 lines) — .inline-headline, .inline-headline-display, .inline-headline-text, .inline-headline-placeholder, .inline-headline-input, plus hover/focus/disabled/editing states and a 640px-and-below mobile rule that drops the headline to 22px (matching <PageHeader title>'s mobile reflow from v1.14.0 R18-8).

Notes

  • <DataTable> API surface changes are purely additive. Existing consumers without rowTarget see no change — the resolved target stays undefined, anchor renders as a same-tab navigation. The rel auto-inject only fires when the consumer opts into rowTarget="_blank", and they can override with an explicit rowRel.
  • <PageHeader> API surface change is purely additive. Existing consumers without onTitleSave see no change — the title still renders as a static <h1> with the same icon / eyebrow / summary / actions chrome. The new editable path only fires when onTitleSave is set.
  • <InlineHeadline> is 'use client' (it owns useState for the editing mode + draft value, refs for the input, keyboard handlers). <PageHeader> itself stays declarative — when onTitleSave is set it composes <InlineHeadline> into the title slot, and React handles the client-boundary inheritance.
  • All gates green: typecheck, markup-equivalence (25/0/58), check:docs (346 snippets), CSS / UI / kit-site builds.
  • CSS bundle: dist/magicblocks.css 559723 bytes (was 557087); tokens-only subpath unchanged.
  • Pair with @magicblocksai/css@1.23.0.

Spark migration

After upgrading to @magicblocksai/ui@1.23.0 + @magicblocksai/css@1.23.0:

  • Files tab on apps/web/src/routes/ProjectDetailPage.tsx — the onRowClick + window.open(href, "_blank", "noopener,noreferrer") fallback can come out. Replace with rowHref={(f) => f.downloadUrl} + rowTarget="_blank". Cmd-click / middle-click / right-click affordances all return.
  • apps/web/src/routes/TicketDetailPage.tsx — the local <SubjectEditor> workaround can come out. Replace with <PageHeader title={ticket.subject} onTitleSave={async (next) => { … }} eyebrow="Tickets" />. Or, if the ticket page renders its own custom hero chrome that doesn't fit <PageHeader>, use the standalone <InlineHeadline> primitive directly.
  • apps/web/src/components/InlineHeadline.tsx — delete after the import-path swap from "../components/InlineHeadline""@magicblocksai/ui".
  • apps/web/src/routes/IssueDetailPage.tsx / ContactDetailPage.tsx / CompanyDetailPage.tsx / DealDetailPage.tsx / ProjectDetailPage.tsx — pass onTitleSave to the <PageHeader> to enable click-to-edit. Same recipe as <TicketDetailPage>. Spark gets editable titles on every detail-page headline by adding one prop per page.

Round mapping

| spark-id | kit-id | what shipped | |---|---|---| | R20 (open) | R20 | <DataTable rowTarget> + rowRel props with _blank auto-rel injection | | R22 (open) | R22 | <InlineHeadline> primitive + <PageHeader onTitleSave> + titlePlaceholder |

Spark queue: empty

This round closes the last open kit-team item from Spark's most recent feedback. Open Spark items prior to this batch (R19-1, R21-1, R21-2, R21-3, R20, R22, R24) all shipped: R19-1 in v1.21.0, R21-1/2/3 in v1.22.0, R20 + R22 in this round (v1.23.0), R24 in v1.20.0. Whatever Spark files next is the next round.


[1.22.0] — 2026-05-08

Minor — Spark Round 24 (kit-side; R21-1 / R21-2 / R21-3 by Spark numbering). Three small detail-page UX fixes batched per the user's recommendation. All three were producing real visible bugs in Spark's running app — alignment broken on /tasks, missing empty states on every detail-page card with a portaled dialog or && short-circuit children, and warning-yellow / exclamation-mark icons making half the activity timeline read as "alert."

Fixed

  • <InboxRow selectable> desktop grid template (R21-1). Pre-1.22.0 the .inbox-row base rule set grid-template-columns: 36px 1fr auto auto (4 children). When selectable was on, a 5th leading checkbox child got prepended but no rule extended the grid template above the 480px breakpoint — children flowed into 4 columns, avatar grabbed all the slack, body / due / actions piled up on the right with a wide empty band between avatar and title. Fix: new .inbox-row.is-selectable { grid-template-columns: 22px 36px 1fr auto auto } rule alongside the base. The mobile equivalent (@media (max-width: 480px) .inbox-row.is-selectable { grid-template-columns: 22px 36px 1fr }) already existed; this is the missing desktop counterpart. Spark's only consumer was /tasks.
  • <SectionCard emptyState> no longer suppressed by JSX && short-circuit (R21-2). The old gate was emptyState !== undefined && children === undefined, which only treated literal undefined children as empty. JSX commonly produces false (from && short-circuit), null, […false…] (arrays of all-falsy), or React elements (e.g. always-rendered portaled dialogs that don't actually paint into the card subtree) — none of which === undefined. Result: <SectionCard emptyState={…}>{rows.length > 0 && <List/>}</SectionCard> never showed the empty state when rows was empty. Spark hit this on every detail-page Tasks / Subscriptions / Referred-customers card. Fix: new isChildrenEmpty(children) helper treats null | undefined | false and arrays where every entry is itself empty as "empty." React elements, non-empty strings, numbers (incl. 0), and arrays containing real content stay non-empty.
  • <ActivityTimeline> icon styling (R21-3). Three sub-fixes: 1. See-through icon backgrounds — every per-type tint was alpha-channel rgba (var(--info-soft) / --accent-soft / --warning-soft / --success-soft / a color-mix(…, transparent) for meeting), and the kit's vertical rail line bled through every chip. Fix: opaque var(--bg-paper) baseline on .act-icon plus the soft tint as a linear-gradient overlay (background-image: linear-gradient(<tint>, <tint>)), so paper sits underneath everywhere. The meeting rule's color-mix(in oklab, var(--ink) 8%, transparent) second-arg-of-transparent now sits on opaque paper too. 2. note retinted from warning-yellow (--warning-soft / --warning-text, which read as "⚠️ alert") to --accent-soft / --accent-text (brand pink — "this is content"). Notes aren't alerts. 3. custom glyph swapped from a circle-with-exclamation (which read identical to <Banner tone="warning">) to a four-point sparkle. custom is the catch-all type for every consumer-defined sub-type that doesn't map to email/conversation/meeting/note/stage-change — in Spark's app that's task_*, ticket_*, stripe_*, mb_*, form_submitted, file_uploaded, etc. (most rows). The exclamation flagged every catch-all row as "needs attention."

Added

  • <ActivityItem icon> slot (R21-3, optional). Every variant of the ActivityItem discriminated union now accepts an optional icon?: ReactNode prop that overrides the default per-type glyph the kit ships in ICON. Pass any 13×13ish SVG using currentColor so it tints with the kit's per-type palette. Use it for richer per-row glyphs the kit's 6-type set doesn't cover natively (e.g. task → ✓, ticket → headset, stripe → coin). The CSS (.act-row[data-type="…"] .act-icon) still drives the chip background/border colour from the type discriminant — the icon prop only swaps the inner SVG. Spark's local lib/activity.tsx per-sub-type glyph workaround (a :has([data-spark-type^="task_"]) .act-icon::after CSS injection) can come out in favour of passing icon to each item.

Notes

  • No new props on <SectionCard> or <InboxRow>. The fixes are purely internal — existing call sites work correctly without code change.
  • Spark workarounds can be ripped out. The scoped CSS block at the bottom of apps/web/src/index.css (R21-1 + R21-3 overrides) and the three R21-2 codebase sweeps (move dialogs out of subtree / ?: ternary / inline <SectionCard.Empty>) are no longer needed. The dialog-as-child trap auto-resolves; && short-circuit works as users naturally write it; the :has() per-sub-type glyph injection becomes a first-class icon prop.
  • No breaking changes. <ActivityItem> shape is byte-equivalent for callers who don't pass icon. The <SectionCard emptyState> gate is a strict superset of the old gate — anything that fired pre-1.22.0 still fires. CSS rules updated in place; the .act-icon per-type rules now use background-image instead of background to layer paper underneath, but the visible result for non-overlapping cases is identical.
  • Pair with @magicblocksai/css@1.22.0.

Spark migration

After upgrading to @magicblocksai/ui@1.22.0 + @magicblocksai/css@1.22.0:

  • apps/web/src/index.css — delete the scoped block that overrides .inbox-row.is-selectable desktop grid (R21-1) and the per-.act-icon paper-layer + note-tint + custom-glyph injection (R21-3). The kit owns those rules now and they'll fight the new defaults if left in place.
  • apps/web/src/components/LinkedIssues.tsx — the move of <IssueDialog> out of the <SectionCard> subtree is no longer required (R21-2). Same for the ?: ternary rewrites in SubscriptionsCard.tsx / ReferredCustomersCard.tsx / CustomerWorkDetailPage.tsx. Optional cleanup; the workarounds keep working.
  • apps/web/src/lib/activity.tsx — replace the per-sub-type CSS injection with icon: <Glyph/> on each ActivityItem you produce. Use the kit's existing icons (CheckIcon, HeadphonesIcon, CoinIcon, WandIcon, etc.) or any consumer-defined SVG.

Round mapping

| spark-id | kit-id | what shipped | |---|---|---| | R21-1 (open) | R21-1 | .inbox-row.is-selectable desktop grid template | | R21-2 (open) | R21-2 | isChildrenEmpty helper for <SectionCard emptyState> gate | | R21-3 (open) | R21-3 | Paper layer under .act-icon; note retint to accent-soft; sparkle custom glyph; new icon slot on ActivityItem |

Deferred from this round (queued)

  • R20<DataTable rowHref> no target="_blank" exposure
  • R22<InlineHeadline> for click-to-edit titles

Both ride in a "DataTable + PageHeader composability" round.


[1.21.0] — 2026-05-08

Minor — Spark Round 23 (kit-side; R19-1 by Spark numbering, P0). Fixes a real "live tables broken on mobile" regression from v1.14.0 R18-3. Singleton round — Spark's open queue (R20, R21-1/2/3, R22) is queued for separate dedicated rounds. Full close-out at SPARK-FIXES-LOG.md Round 23.

Fixed

  • <DataTable> priority-hidden columns no longer leave grid tracks behind (R19-1 / P0). Pre-1.21.0 the v1.14.0 R18-3 mobile rules hid cells with display: none, but the underlying grid-template-columns track for the hidden column stayed allocated — a 7-column table with three tertiaries squashed its visible columns to ~5px wide on a phone. Spark hit this on /leads, /sequences, and /broadcasts and reverted the priority annotations as a workaround.

The fix: the React component now computes three grid-template-columns strings (one per priority tier — desktop / tablet / mobile) and exposes them as CSS custom properties (--data-table-cols-desktop, --data-table-cols-tablet, --data-table-cols-mobile) on every .data-table-row. The kit's media queries read the right variable at each breakpoint via var(), so the surviving columns expand into the freed space at every viewport. No inline-style precedence fight (variables get set inline, declarations live in CSS), no JS resize observer, no flicker on viewport changes.

Spark's workaround can come out: re-add priority: "tertiary" / "secondary" annotations on /leads, /sequences, and /broadcasts columns and the mobile layouts will collapse cleanly.

Notes

  • Behavioural change scoped to mobile breakpoints only. Desktop (≥720px) renders identically to v1.20.0 — the desktop template still includes every column. Only at ≤720px (drop tertiary) and ≤520px (drop secondary as well) does the new computed template take effect.
  • CSS-class / vanilla consumers consuming .data-table-grid + .data-table-row directly need to set the three custom properties inline themselves to get the priority-aware behaviour. The fallback ladder (var(--data-table-cols-mobile, var(--data-table-cols-tablet, var(--data-table-cols-desktop, 1fr)))) keeps the old behaviour intact for consumers who don't set them.
  • No new props or API changes on <DataTable>. Existing call sites with priority annotations work correctly without any code change beyond upgrading.
  • The display: none on .data-table-cell[data-priority="tertiary"] / [data-priority="secondary"] rules stay — they're still useful for any custom row layout a consumer authors with the same data-priority convention. The grid tracks are now correctly sized regardless.
  • Pair with @magicblocksai/css@1.21.0.

Deferred from this round (queued)

  • R20<DataTable rowHref> no target="_blank" exposure
  • R21-1<InboxRow selectable> desktop grid template missing the leading checkbox column
  • R21-2<SectionCard emptyState> JSX && short-circuit suppression
  • R21-3<ActivityTimeline> icon tints (see-through, warning-yellow on note, alarming custom glyph)
  • R22<InlineHeadline> for click-to-edit titles

These ride in their own dedicated rounds. R21-1/2/3 will likely batch (all small detail-page UX fixes); R20 + R22 will likely batch as a "DataTable + PageHeader composability" round.


[1.20.0] — 2026-05-08

Minor — Spark Round R24 (file-type icon set + dispatcher). Closes the kit gap Spark filed in .scratch/components-to-fix-or-add.md while polishing the project Files tab — Spark hand-rolled an inline 7-glyph file-type SVG set in apps/web/src/components/FileTypeIcon.tsx, this round absorbs that surface into the kit so the local copy can be deleted. Full close-out at SPARK-FIXES-LOG.md Round 22.

Added

  • Seven file-type icons in icons.tsxFileIcon, FileTextIcon, FileImageIcon, FilePdfIcon, FileVideoIcon, FileAudioIcon, FileArchiveIcon. All match the kit's standard icon spec (18×18 viewBox, 1.5 stroke, currentColor, no fill, round caps + joins) and share a "page with dog-ear corner fold" base so the family reads as cohesive — the inner mark is what distinguishes the type. All export from the package barrel and forward IconProps like every other kit icon.
  • <FileTypeIcon contentType="…" /> dispatcher — picks the right kit icon from a MIME / content-type string. Forwards every standard icon prop (size, className, aria-label, native SVG attrs) to the resolved icon. Spark's existing <FileTypeIcon contentType={file.content_type} size={18} /> call sites are byte-equivalent after swapping the import path.
  • getFileTypeIcon(contentType) helper — returns a ComponentType<IconProps> for use inside generated lists, <DataTable> cell renderers, or anywhere else you need to capture the icon as a value first. The dispatcher above is getFileTypeIcon(...) rendered.
  • getFileTypeKind(contentType) helper — returns the bare FileTypeKind union ("doc" | "text" | "image" | "pdf" | "video" | "audio" | "archive") for branching downstream of the icon (e.g. "preview button only for images and videos").
  • FileTypeKind + FileTypeIconProps types — both exported from the package barrel for consumer typing.
  • packages/ui/docs/FileTypeIcon.md — full doc with API reference, examples, the exact mapping rules, and the migration recipe for Spark's local file. IconFamily.md gains a new "File-type set" subsection in the Catalogue.

Style exception (documented inline)

  • <FilePdfIcon> carries a typographic "PDF" label — 4.25px monospace text, stroke="none", fill="currentColor". The kit's icons are otherwise pure monoline outlines (no fills, no two-tone), but at 18×18 grid, PDF can't be visually distinguished from a generic document without either letters or fills. Spark's hand-rolled set already used the letters approach — the kit follows. The label tints with currentColor and scales cleanly with size so the icon stays consistent with the rest of the family visually. The exception is scoped to this one icon and documented in icons.tsx inline + FileTypeIcon.md §"Style note — the PDF letter exception".

Mapping rules

getFileTypeKind(contentType) runs in this order — first match wins:

1. Lowercase + trim. Falsy (null / undefined / "") → "doc". 2. image/*"image" 3. application/pdf"pdf" 4. video/*"video" 5. audio/*"audio" 6. (zip|tar|gzip|gtar|compressed|7z|rar|bzip|xz|lzma) regex match → "archive" 7. text/* OR JSON / XML / YAML / CSV / JSONL flavours → "text" 8. Default → "doc"

The set is deliberately small (7 kinds) — consumers needing richer treatment (a spreadsheet glyph, a slide-deck glyph, a "code file" variant) should add their own sibling icon using the same iconComponent factory pattern, or fall back to <FileIcon> plus a sibling label.

Notes

  • Kit-chrome carve-out. <FileTypeIcon> and the seven new individual icons all live in scripts/check-coverage.mjs's KIT_CHROME set under "1.20.0 — Spark Round R24". They're consumer-facing primitives without per-chapter demos; the per-component MD doc + the IconFamily.md catalogue subsection are the canonical reference.
  • No 'use client'. Both the dispatcher and every individual icon are purely declarative — render in Astro / Next RSC without a hydration boundary.
  • No CSS surface change. The icons render via currentColor like every other kit icon; no new classes in _shared.css. @magicblocksai/css@1.20.0 is a lockstep no-source-change bump.
  • Pair with @magicblocksai/css@1.20.0.

Spark migration

After installing @magicblocksai/ui@1.20.0:

```tsx // before import { FileTypeIcon } from "../components/FileTypeIcon"; <FileTypeIcon contentType={file.content_type} size={18} />

// after import { FileTypeIcon } from "@magicblocksai/ui"; <FileTypeIcon contentType={file.content_type} size={18} /> ```

Then rm apps/web/src/components/FileTypeIcon.tsx. The component prop shape (contentType, size, aria-label) is byte-equivalent to Spark's local interface — no other changes needed at call sites.


[1.19.0] — 2026-05-07

Minor — Website Round W3 (R006–R011). Six asks filed by the magicblocks-website team after first-pass adoption. Five ship with code/CSS/doc changes; one (R010 — animation controllers) ships docs-only with a clear roadmap. Two real bugs fixed (R007 + R008), one new token (R006 → --on-accent), one new prop (R011 → <PressStrip layout>), one runtime dev-warn (R009 → <EcosystemRings>), and three new docs (ReservedClassNames.md, Tokens.md, plus updates to HeroLiveDemo.md / ChatCompare.md / PressStrip.md). Full close-out at WEBSITE-FIXES-LOG.md Round W3.

Added

  • --on-accent token (W3-R006) — colour for text PLACED ON --accent background (primary CTAs, accent pills, etc.). Resolves to var(--paper) (cream) in both light and dark modes — paper-on-pink reads at WCAG AA-large for the kit's accent in both themes. Distinct from --accent-text which means "accent-coloured text on a NEUTRAL background" (aliased to --accent). The two roles were collapsed into a single --accent-text token historically, which led consumers reading the name as text-on-accent (the convention in some other design systems) to ship invisible pink-on-pink CTAs. Documented at packages/ui/docs/Tokens.md §"Accent vs on-accent". The kit's own .btn-primary rule now uses var(--on-accent, var(--paper)) — fallback preserves back-compat with consumers on older @magicblocksai/css releases.
  • --f-italic token (W3-R008) — aliases to var(--f-serif) (Fraunces). The kit's chapter rules (.pg-title em, .ctab-title em, .au-quote em, .cv-bubble em, .nf-title em, .hero-title em, and ~10 more) reference var(--f-italic) paired with font-variation-settings: "SOFT" 80 for the Fraunces SOFT-80 italic-accent treatment. Pre-1.19.0 the token was undefined — <em> text fell back to whatever browser default italic was inherited (usually the body face, in italic). Trivial bugfix; rules already shipped, the token was just orphaned.
  • <PressStrip layout> prop (W3-R011)"wrap" | "scroll", default "wrap" (back-compat). "scroll" switches to flex-wrap: nowrap with horizontal overflow-scroll and a soft right-edge fade (same idiom as <IndustryBar> mobile). Below 640px both variants wrap (horizontal scroll on phones is a footgun: users miss off-screen logos). For lineups of 8+ outlets at marketing widths, "scroll" reads as a "row of credibility"; the default wrapping reads as a logo wall. New .press-row.is-scroll CSS family in _shared.css.
  • Runtime dev-warn for <EcosystemRings> empty labels (W3-R009) — when process.env.NODE_ENV !== "production", the component checks each middleLabels / outerLabels entry and emits a console.warn if label is missing, null, or empty. The warning names the offending ring + index + id and explicitly calls out the typo trap (the kit prop is label, not text). No production cost; no behavioural change. Shipped because Astro's loose JSX type-checking can let typo'd prop names through silently — the website team hit this on the homepage with <EcosystemRings middleLabels={[{ top, left, text: "Chat" }]}>, which produced invisible labels with no console signal. Type-level guard rails in the EcosystemRingLabel interface + the per-prop JSDoc now also call out the trap.
  • packages/ui/docs/ReservedClassNames.md — new doc listing every kit-reserved global class name (.hero, .section, .container, .page, plus the canonical component primitives). Consumers naming their own sections with these classes silently inherit the kit's chrome (overflow rules, surfaces, token rescopes) — this doc names the trap and recommends namespaced-class patterns (.home-hero, .product-hero, etc.). The .hero rule in _shared.css now carries an inline ⚠️ comment pointing at this doc.
  • packages/ui/docs/Tokens.md — new doc covering token-pair naming notes that are easy to misread. Currently covers "Accent vs on-accent" (W3-R006) and "Italic-accent face: --f-italic" (W3-R008). Will grow as new ambiguities are documented.

Documentation

  • HeroLiveDemo.md + ChatCompare.md — "Consumer integration — animation behaviour" sections (W3-R010) — clear answer to "the kit doesn't animate on consumer sites." Documents the freeze-frame end-state pattern (staticDisplay={true} default + no client:* directive) as the kit's blessed integration pattern for consumer sites, with the rationale: the end-state still tells the full story, ships zero JS overhead, and works in Astro / Next RSC out of the box. The autoplay prop / vanilla controller path is on the roadmap (W4 or later) — documented but not shipped this round, since it requires a 'use client' boundary and a non-trivial controller implementation. Consumers needing autoplay sooner can open a GitHub issue to prioritise.
  • PressStrip.mdlayout prop docs with a "When to use layout='scroll'" recipe.

Bug fixes

  • .hero global class collision (W3-R007, doc-only fix). The kit's global .hero { overflow: hidden; padding: …; background: var(--warm-3); border-radius: var(--r-xl); isolation: isolate; } rule applies to chapter 09 narrative + the kit-site index. Consumers naming their own page sections class="hero" silently inherit the chrome — including overflow: hidden, which clips legitimate consumer content (the website team hit this with their homepage's V2 proof bar). Fix: documented in the new ReservedClassNames.md; _shared.css carries an inline ⚠️ comment near the .hero rule pointing at the doc. Renaming .hero.kit-hero (the obvious "fix") would be a hard breaking change for every kit consumer + every chapter HTML demo, so the chosen path is documentation + a recommended namespaced-class pattern (.home-hero, .product-hero, etc.).
  • --f-italic undefined (W3-R008) — see "Added" above.

Notes

  • CSS bundle grows by ~30 lines (sentinel comments + the new .press-row.is-scroll family). Tokens-only subpath unchanged in shape — --on-accent and --f-italic both live above /* @TOKENS-END */ and ship in tokens.css.
  • No breaking changes. Every existing API is unchanged. The <PressStrip> default stays layout="wrap"; the <EcosystemRings> dev-warn doesn't fire in production; --accent-text keeps its current value and meaning.
  • Pair with @magicblocksai/css@1.19.0.

W3 mapping

| website-id | kit-id | what shipped | |---|---|---| | R006 (bug) | W3-R006 | New --on-accent token + Tokens.md "Accent vs on-accent" doc | | R007 (bug) | W3-R007 | ReservedClassNames.md doc + ⚠️ comment in _shared.css near .hero | | R008 (bug) | W3-R008 | --f-italic: var(--f-serif) token added | | R009 (enhancement) | W3-R009 | Runtime dev-warn in <EcosystemRings> for empty labels + tightened JSDoc | | R010 (enhancement) | W3-R010 | Docs-only — freeze-frame canonical pattern in HeroLiveDemo.md + ChatCompare.md; autoplay prop on roadmap | | R011 (enhancement) | W3-R011 | <PressStrip layout="wrap" \| "scroll"> prop (default "wrap" for back-compat) |

Website migration

After installing @magicblocksai/ui@1.19.0 + @magicblocksai/css@1.19.0:

  • Primary CTAs — drop the local color: var(--paper) override on .site-nav__cta / .btn-primary and rely on the kit default. The kit's .btn-primary now resolves to color: var(--on-accent, var(--paper)) — the on-accent token does the right thing automatically.
  • <em> italic accents — drop the local --f-italic definition in BaseLayout.astro; the kit defines it now.
  • <PressStrip> — pass layout="scroll" if you want the marquee-style row. The hand-rolled .press-row { flex-wrap: nowrap; … } override in BaseLayout.astro global styles can come out.
  • Homepage hero — the .hero.home-hero rename you already shipped is the right pattern; keep it. The new ReservedClassNames.md doc backs that up.
  • <EcosystemRings> middle/outer labels — already migrated to { id, label, top, left } shape; no further change needed. The new dev-warn would have caught the text: typo at runtime if it had landed sooner.

[1.18.0] — 2026-05-07

Minor — Website Round W2. Closes the two new-component asks the website team filed in W1 (R001 + R002) once the V2 spec was attached. Both ship as kit-chrome consumer-facing primitives (no chapter demo; per-component MD doc). Full close-out at WEBSITE-FIXES-LOG.md Round W2.

Added

  • <IndustryBar> (W2-R001) — slim full-width sub-nav strip beneath the topnav. Answers "is this for me?" in under half a second. N-agnostic — pass however many industries the consumer's sitemap calls for (the V2 spec drifted between 6 and 7 across different sections; the kit takes a list rather than picking a side). Two surface variants (warm default + ink); compact modifier; mono / uppercase / letter-spaced 0.06em label styling per spec. Active item gets aria-current="page" + accent dot + underline. Hover lifts text to --accent with a 1px underline. Below 768px the strip becomes horizontally scrollable with a soft right-edge gradient cue. Custom Link component prop for client-side navigation (Next.js / React Router / TanStack — same shim pattern as <SectionCard LinkComponent>). Documented at packages/ui/docs/IndustryBar.md.
  • <CostCompare> (W2-R002) — pricing-page-hero side-by-side cost-comparison bars. Full-width muted bar (high) + ratio%-width accent bar (low) with optional mono caption between. The ratio prop (default 5, clamped to 1–100) lets the consumer scale the visual proportion precisely as the underlying numbers shift, so the bar widths stay honest to the math. The bars are decorative (aria-hidden); the cost values in the labels are the screen-reader surface. Documented at packages/ui/docs/CostCompare.md.

CSS

  • New .industry-bar family in _shared.css (~95 lines): .industry-bar, .industry-bar.is-ink, .industry-bar.is-compact, .industry-bar-track, .industry-bar-eyebrow, .industry-bar-list, .industry-bar-item, .industry-bar-sep, .industry-bar-link, .industry-bar-link.is-current, .industry-bar-link:focus-visible, .industry-bar-dot, .industry-bar-label. Mobile rules ≤768px shrink the row to 36px (30px compact), drop label size to 11px, and apply a mask-image right-edge fade for the horizontal scroll cue. Reduced-motion drops the colour transition.
  • New .cost-compare family in _shared.css (~70 lines): .cost-compare, .cost-compare-row.is-high / .is-low, .cost-compare-meta, .cost-compare-label, .cost-compare-value, .cost-compare-unit, .cost-compare-bar.is-high / .is-low, .cost-compare-gap. The low-bar width comes from --cost-compare-ratio (custom property) — set inline by the React component or by hand on CSS-class consumers.

Notes

  • Both components are kit-chrome carve-out. Consumer-facing primitives without per-chapter demos; per-component MD doc is the canonical reference. Listed in scripts/check-coverage.mjs KIT_CHROME set under "1.18.0 — Website Round W2".
  • Spec drift (industry count) handled by parameterisation, not adjudication. The website team's note flagged that V2 sitemap says 7 industries while the Component-1 anatomy diagram and responsive breakpoint note both say 6. Building for N (consumer passes the list) makes both readings correct; the kit doesn't need to pick.
  • No 'use client'. Both components are purely declarative — no hooks, no events, no browser APIs. Render in Astro / Next RSC without a hydration boundary.
  • CSS bundle grows by ~165 lines for the two new component families.
  • Pair with @magicblocksai/css@1.18.0 (the CSS package mirrors _shared.css verbatim — every new rule lands there too).

W2 mapping

| website-id | kit-id | what shipped | |---|---|---| | R001 (P0) | W2-R001 | <IndustryBar> — N-agnostic, two surface variants, compact modifier, mobile scroll cue | | R002 (P1) | W2-R002 | <CostCompare>high + low sides + ratio + gapLabel |

Website migration

After installing @magicblocksai/ui@1.18.0 + @magicblocksai/css@1.18.0:

  • src/components/nav/IndustryBar.astro — replace the Phase 0 static fallback with <IndustryBar industries={INDUSTRIES} currentSlug={activeSlug} />. The Astro recipe in IndustryBar.md shows the URL-based activeSlug derivation.
  • /pricing hero — drop in <CostCompare> per the doc example. The 5% default ratio matches the V2 spec's "$2.50–$4.00 vs $30–$80" framing.
  • No need to ship a local port for either component now.

[1.17.0] — 2026-05-07

Minor — Website Round W1. First feedback round from the magicblocks-website build team (in addition to the existing Spark CRM stream). Five entries filed (R001–R005); two ship as docs in this round, one is partial-shipped via a tokens-only CSS subpath (see @magicblocksai/css@1.17.0), and two are deferred pending the V2 spec doc. Full close-out at WEBSITE-FIXES-LOG.md Round W1.

Documentation

  • Drawer.md: "Lazy-import on first interaction" recipe (W1-R004) — the canonical answer for budget-sensitive pages where <Drawer> is the rare-path mobile-menu. Two complete copy-paste recipes (Astro vanilla-script and Next.js / Remix lazy() form) plus a "trade-offs" sub-section covering first-tap latency, bundler chunking requirements, and when to skip the pattern. The CSS-only-drawer alternative (R004's option (a)) is deferred to a future round — would add a third drawer surface beyond React + CSS-class.
  • Scoreboard.md: "Composition recipes" section (W1-R005) — answers the website team's .stress-scoreboard question with a "compose, don't extend" recipe. <Scoreboard tone="dark"> already provides the war-room chrome; the consumer wraps the section with their own .stress-scoreboard class for layout chrome and passes a stress-framed rows payload. The kit will not ship a dedicated <StressScoreboard> for this — the new doc section also enumerates the three signals that *would* warrant a dedicated component (structurally different markup / different prop contract / different default behaviour) and confirms none apply here.

Notes

  • Two consumer streams. This is the first round serving both Spark CRM (via SPARK-FIXES-LOG.md) and the magicblocks-website (via WEBSITE-FIXES-LOG.md). The kit's CLAUDE.md now documents both consumer paths under a new "Downstream consumers" section. Conventions for cross-stream conflicts are written into the W1 close-out.
  • Deferred items requiring spec attachment. R001 (<IndustryBar>) and R002 (<CostCompare>) are real new components blocked on _components-to-commission.md Components 1 + 8 from the website team's V2 doc. The website team's local mitigation is shipping a static fallback (R001) / deferring to Phase 1 (R002), so neither blocks their critical path. Will ship in a W2 round once the spec is attached.
  • No 'use client' / barrel changes. All shipped items in this round are doc-only on the React side. The CSS package is the one that picks up actual code (the new tokens.css build output).
  • Pair with @magicblocksai/css@1.17.0 for the tokens subpath.

W1 mapping

| website-id | kit-id | what shipped | |---|---|---| | R001 (P0) | — | Deferred — needs _components-to-commission.md Component 1 spec | | R002 (P1) | — | Deferred — needs _components-to-commission.md Component 8 spec | | R003 (perf) | W1-R003 | Tokens-only subpath in @magicblocksai/css/tokens (4.9 KB min). Per-chapter splits deferred — PostCSS purge gives strictly better tree-shaking | | R004 (perf) | W1-R004 | Lazy-import recipe in Drawer.md (Astro + Next/Remix forms) | | R005 (docs) | W1-R005 | Composition recipe in Scoreboard.md; <Scoreboard tone="dark"> already covers the use case |


[1.16.0] — 2026-05-07

Minor — Spark Round 20 R20-1: structured <SectionCard> empty states. Singleton round shipping R20-1; further R20 items (if any) will land in a follow-up bump.

Added

  • <SectionCard emptyState> accepts a structured object (R20-1) — in addition to the existing ReactNode (string / JSX) shape, the prop now accepts { icon?, title?, description?, action? } and renders the canonical icon-chip · title · description · action stack inside the existing .section-card-empty wrapper. Sized for in-card density (40px icon chip, 14px title, 13px description, 44ch description cap) — distinct from the larger <EmptyStatePage> / chapter-7 .empty family that's sized for full-page surfaces.
  • <SectionCard.Empty> subcomponent (R20-1) — exposed as a static property on <SectionCard> (and as a standalone SectionCardEmpty export) for consumers who need to render the structured empty-state visual from inside a non-empty body slot. Same content slots as the emptyState prop; identical DOM minus the outer .section-card-empty wrapper (the consumer owns positioning).
  • SectionCardEmptyContent interface exported from the package barrel for consumers who want to type their empty-state config separately from the JSX.

CSS

  • New .section-card-empty-content wrapper plus four slot classes — .section-card-empty-icon, .section-card-empty-title, .section-card-empty-description, .section-card-empty-action — sized for in-card density. Dark-mode token swap on the icon-chip (--bg-sunk background + dimmed-fg colour). The bare-text path (children rendered directly inside .section-card-empty) is unchanged.

Notes

  • No breaking change. The emptyState prop's existing ReactNode shape is preserved verbatim; the union widens via TypeScript's narrowing (plain objects aren't valid ReactNodes, so the structured shape resolves cleanly without ambiguity). Runtime detection uses React's isValidElement — a non-array, non-element object is the structured shape.
  • <SectionCard.Empty> is attached via Object.assign so <SectionCard> retains its forwardRef exotic type while picking up the static property. Both <SectionCard.Empty …> and <SectionCardEmpty …> work; pick whichever you find more readable.
  • Both packages bump lockstep — @magicblocksai/css@1.16.0 ships the new slot CSS.

Spark migration

After installing @magicblocksai/ui@1.16.0 + @magicblocksai/css@1.16.0:

  • For sections that should render a structured empty state when there's nothing to show, swap any local hand-rolled empty-state markup for emptyState={{ icon, title, description, action }}.
  • For sections that need the empty-state visual *inside* a populated body (LinkedTasks etc.), render <SectionCard.Empty …> directly inside the body slot. Drop the per-consumer hand-roll.

[1.15.0] — 2026-05-06

Minor — Spark Round 19 R19-2: <SectionCard padded> prop with default-flip. Singleton round (R19-1 still pending from Spark; shipping R19-2 standalone since the change is mechanical and Spark's adoption is blocked on it).

Added

  • <SectionCard padded> prop (R19-2)boolean, default true. Pads the body region with var(--s-4) var(--s-5). Defaults toward the more common case (free-form prose / KV pairs / form fields). Pass padded={false} for list-shaped children (<Inbox>, <Checklist>, <DataTable>, <Calendar>, <Panel>) that paint their own edge-to-edge rows. Replaces the deprecated padBody prop, which had the inverse default and forced every consumer to wrap free-form bodies in a padded <div>.

Deprecated

  • padBody prop on <SectionCard> — kept as an alias for back-compat. When set explicitly, takes precedence over padded so existing call-sites are byte-equivalent until you touch them. The TypeScript prop carries @deprecated so editor hovers surface the rename. Will be removed in v2.0.0.

Behavioural change (consumer-visible)

  • <SectionCard> body padding default flipped from off → on. Existing call-sites that relied on the default flush behaviour (i.e. didn't pass padBody and expected flush) now render padded. Migration is one prop addition per call-site:

| Old call (v1.14.x) | New call (v1.15.0) | |---|---| | <SectionCard>…flush list…</SectionCard> | <SectionCard padded={false}>…flush list…</SectionCard> | | <SectionCard padBody>…prose…</SectionCard> | <SectionCard>…prose…</SectionCard> (drop the prop) | | <SectionCard padBody={false}>…</SectionCard> | <SectionCard padded={false}>…</SectionCard> |

Call-sites still passing padBody keep their behaviour unchanged (the deprecated alias wins when explicitly set), so the migration can be incremental.

Notes

  • No CSS change. The .section-card-body.is-padded modifier already exists in _shared.css; this round only changes the TSX default to emit it. CSS-class consumers (@magicblocksai/css) see no behaviour change — they still opt in to padding by adding .is-padded on the body.
  • The SectionCard.md doc updates the existing examples to show padded={false} on the list / table / chrome-only patterns and adds a "Migration from padBody" section.
  • Pair with @magicblocksai/css@1.15.0 (lockstep version bump only — no CSS source change).

Spark migration

  • Sweep <SectionCard>…<Inbox|Checklist|DataTable|Calendar|Panel>…</SectionCard> call-sites and add padded={false}. Or grep for padBody and rename to padded (with the inverse value if you were using padBody={false} → drop entirely).
  • Where you were wrapping free-form bodies in <div style={{ padding: 16 }}> to compensate for the old default — drop the wrapper, the kit handles it now.

[1.14.0] — 2026-05-06

Minor — Spark Round 18 mobile audit. 10-item sweep filed by Spark in .scratch/mobile-audit-kit.md; the kit ships all 10 in this release. Two were already shipped pre-round (verified — see Notes). Three P0s + five P1s + two P2s — see "Round 18 mapping" below for spark-id ↔ kit-id correspondence.

Added

  • <SortableHandle> primitive (R18-1, P0) — drag-grip + secondary-action affordance for sortable cards / rows. Composes with useSortable from the kit's primitive layer. Visible at rest on touch (@media (pointer: coarse)); fades in on :hover / :focus-within of the parent row on mouse devices. Single action → direct icon button; 2+ actions → trigger + popover menu (Esc + outside-click dismissal). Ships because every consumer who builds a hover-reveal handle and forgets the touch case ends up with an invisible drag affordance on mobile — Spark hit this exact bug on /overview cards in the round prior to filing this audit. Documented at packages/ui/docs/SortableHandle.md. Listed in the kit-chrome carve-out (no chapter demo).
  • <Kanban mobileLayout> prop (R18-2, P0)"scroll" (default) | "stack". New default at ≤720px is horizontal flex with scroll-snap-type: x mandatory so columns retain their pipeline shape and snap one at a time on phones. Behavioural change: prior to v1.14.0 the kit collapsed .kanban to a single column below 520px, which (per Spark) "destroys the pipeline metaphor" on the most-used screens (Deals, Onboarding). The "stack" opt-in preserves the old single-column behaviour for triage kanbans where stage order is informational rather than the visual metaphor.
  • DataTableColumn.priority field (R18-3, P0)"primary" | "secondary" | "tertiary" (default "primary"). Tertiaries hide ≤720px; secondaries hide ≤520px. Wired into header / skeleton / row cells via data-priority. Below 720px the table also picks up a gradient-mask scroll cue (mask-image: linear-gradient(...)) on the right edge so users can see there's more content off-screen. Hide is implemented via display: none rather than dynamic grid-template recalc (documented cosmetic-gap caveat for tables with very wide tertiary columns).

Changed

  • 44pt tap-target sweep on touch devices (R18-4, P1) — under @media (pointer: coarse), the following compact controls now meet Apple's 44pt / Android's 48dp minimum: .btn-sm, .btn-icon-sm, .modal-close, .drawer-close, .section-card-action, .section-card-count, .dropdown-menu-item, .menu-item, .inbox-row .ix-action, .demo-tabs button. Implemented via padding + negative-margin so the visual size on mouse stays unchanged.
  • Modal mobile keyboard handling (R18-5, P1) — the .modal panel becomes a flex column capped at calc(100dvh - var(--s-5) * 2) (using dynamic viewport height so iOS Safari's collapsing toolbar doesn't push the bottom of long forms below the soft keyboard). The body slot scrolls independently (overflow-y: auto + -webkit-overflow-scrolling: touch). Below 600px the footer becomes position: sticky; bottom: 0 with background: var(--bg-sunken) so the primary action stays reachable inside long forms.
  • Drawer head shrink on mobile (R18-6, P1) — below 600px, .drawer-head vertical padding shrinks and .drawer-title drops to 18px so more body content is visible above the fold.
  • PageHeader mobile reflow (R18-8, P1) — at ≤640px the text block (eyebrow + title + summary) takes flex-basis: 100% and the summary loses its max-width: 56ch cap so long summaries don't crop. The actions container also takes flex-basis: 100%; child action buttons get flex: 1 1 auto so two buttons divide the row evenly.
  • InboxRow mobile chip stacking (R18-9, P2) — below 480px the row reflows from a five-column grid to a two-row stack (avatar/checkbox + title in row 1; body + due + chips in row 2). Compact density variant follows the same rule.

Notes

  • R18-7 (P1, popover viewport clamp) — already shipped pre-round. usePopover already clamps to viewport with an 8px gutter on both axes (packages/ui/src/lib/usePopover.ts:149). Verified, no change needed.
  • R18-10 (P2, RichTextEditor <img> max-width) — already shipped pre-round. .rte-content img { max-width: 100%; height: auto } already exists in _shared.css. Verified, no change needed.
  • All mobile rules use @media (max-width: ...) or @media (pointer: coarse) — they don't fire on desktop and don't change desktop visuals.
  • dvh (dynamic viewport height) requires Safari 15.4+ / Chrome 108+ / Firefox 101+. Falls back to vh semantics on older browsers (no JS shim required).
  • Pair with @magicblocksai/css@1.14.0 (the CSS package mirrors _shared.css verbatim — every mobile rule lands there too).

Round 18 mapping

| spark-id | kit-id | what shipped | |---|---|---| | R18-1 (P0) | R18-1 | <SortableHandle> primitive + (pointer: coarse) always-visible CSS family | | R18-2 (P0) | R18-2 | <Kanban mobileLayout="scroll"> default + data-mobile-layout="stack" opt-in | | R18-3 (P0) | R18-3 | DataTableColumn.priority + gradient-mask scroll cue | | R18-4 (P1) | R18-4 | 44pt tap-target sweep on (pointer: coarse) | | R18-5 (P1) | R18-5 | Modal dvh cap + body overflow + sticky foot | | R18-6 (P1) | R18-6 | Drawer head shrink ≤600px | | R18-7 (P1) | — | Already shipped pre-round (verified usePopover) | | R18-8 (P1) | R18-8 | PageHeader text-block + actions reflow ≤640px | | R18-9 (P2) | R18-9 | InboxRow chip stacking ≤480px | | R18-10 (P2) | — | Already shipped pre-round (verified .rte-content img) |

Spark migration

  • Delete any local hover-reveal .row-handle / DragHandle shadow; swap to <SortableHandle id={card.id} actions={[…]} />.
  • For pipeline kanbans (Deals, Onboarding) — no migration needed (new default matches the desired behaviour). For triage kanbans where the old single-column stack was wanted, add <Kanban mobileLayout="stack">.
  • For DataTable consumers — annotate columns with priority: "tertiary" for "nice to have on desktop, hide on phone" and priority: "secondary" for "OK on tablet, hide on phone." Defaults to "primary" (always visible) so existing tables keep working.
  • Delete any local mobile-only overrides for .modal-foot { position: sticky } / .drawer-head { padding-y: smaller } / inbox-row chip stacking — kit owns those now.

[1.13.4] — 2026-05-06

Patch — docs-only correction to v1.13.3's R17-4 close-out (Spark Round 17 follow-up).

Documentation

  • LifecyclePill.md: "When to outgrow this" section — same pattern as RichTextEditor.md's outgrow section. Lists the four signals that mean a single-pill primitive isn't enough (phase-bucketed display / canonical phase model / multiple display modes per stage / PHASE · Sub-stage chip rendering) and points at Spark's <LifecycleStrip> + phase model in apps/web/src/lib/lifecycle.ts as the canonical reference. Future kit consumers reading the doc find the limit-line and the proven pattern without rediscovering them from scratch.
  • SPARK-FIXES-LOG.md Round 17 close-out corrected. v1.13.3's note told Spark to delete their local <LifecyclePill> shadow now that the kit's union accepts the post-sale stages. That instruction was wrong. Spark replied: the shadow's role changed between R17-1 being filed and being shipped. Pre-R17 it existed as a type-union workaround; post-R17 it's a thin wrapper around <LifecycleStrip mode="inline"> rendering the unified PHASE · Sub-stage chip ("CUSTOMER · Success Sprint") that landed in their lifecycle-unification commit. Deleting it would regress the phase-bucketed display they just shipped. The R17-4 row + a new "Why the shadow stays" subsection now reflect the actual close-out.

Re-classification (no code change)

  • Option 3 (kit-side <LifecycleStrip> phase primitive) moves from "deferred for a third consumer" to "deferred — Spark consumer ready, awaiting kit absorption." My v1.13.3 framing was wrong on two counts: (1) Spark IS the second consumer (they built the local primitive ahead of the kit), not a future one; (2) the phase-bucketed display is a real production pattern in their lifecycle-unification commit, not a hypothetical. The kit may absorb <LifecycleStrip> if a second consumer beyond Spark hits the same wall. Until then, Spark's apps/web/src/components/LifecycleStrip.tsx + the phase model in apps/web/src/lib/lifecycle.ts is the canonical reference.

Notes

  • Pair with @magicblocksai/css@1.13.4 (no source change in CSS; lockstep version bump only).
  • No React API changes. No CSS changes. Documentation correction round.

[1.13.3] — 2026-05-06

Patch — chapter-11 narrative fixes + Spark Round 17 R17-1 (LifecyclePill widening).

Added

  • 9 post-sale lifecycle stages on LifecycleValue (R17-1)qualified, discovery, proposal, contracted, onboarding, success_sprint, expansion, churn, winback. Pre-1.13.3 the union was HubSpot-funnel-only (lead / prospect / mql / sql / customer / user / churned / disqualified), which forced B2B CRM consumers with operational customer-success surfaces to shadow the kit's <LifecyclePill> with their own. Each new stage maps to one of the kit's existing badge / pill tints — no new tints introduced. Spark's local LifecyclePill shadow can be deleted on adoption.

This is Option 1 of Spark's three proposed paths (widen the union vs. make generic vs. ship a <LifecycleStrip> phase primitive). The cheaper additive widen lands now; if a third consumer needs vocabulary that doesn't fit, the generic / phase-primitive options remain on the deferred list.

Fixed (chapter 11 narrative components)

  • .dormant-mine "last touch" cell stayed at the cold value (e.g. "47 days") even when the row's pill animated through Sent → Replied. Visually contradictory: a row showing "Replied" claiming "47 days last touch". Added a stacked-spans pattern matching the existing .dm-pill-lbl shape — cold label fades out at 34% of the 14s loop and "0 days" fades in until the cycle restarts. Per-row --i keeps the new column in lock-step with the pill animation. prefers-reduced-motion: reduce snapshots the end state ("0 days").
  • .stress-scoreboard non-MagicBlocks row was a flat wall of red which read as "100% fail" instead of "60% complete, 40% lost". Replaced the blanket .is-fail span { background: var(--error) } rule with per-dot [data-state="pass"|"fail"] selectors set by the inline JS. Now both rows show a green/red mix proportional to the actual stat: MagicBlocks row is 24 green + 1 red (97.5% pass), Single-prompt row is 15 green + 10 red (60% pass with the existing fade-tail opacity). Updated the snippet tabs (HTML / CSS / React) to stay in sync.

Resolved (no kit code change)

  • §11.14 <RevenueCalculator> "not showing on the site" — diagnosed as a stale deployed kit-islands.js bundle missing RevenueCalculator from its registry. Local source has the registry binding (RevenueCalculator: Ac in the minified bundle); the deployed bundle from a prior commit didn't. Shipping this patch triggers Cloudflare's auto-deploy + rebuilds the islands bundle, which restores hydration. Visitors were seeing the static fallback markup with "—" placeholder outputs — non-interactive but not broken.

Notes

  • Pair with @magicblocksai/css@1.13.3.
  • The chapter-11 fixes are visible immediately on the kit-site after redeploy. The lifecycle widening is purely additive — no consumer-visible default change.

[1.13.2] — 2026-05-06

Patch — Spark Round 15 follow-ups (R15-4 + R15-5).

Added

  • <SectionCard icon> slot (R15-5) — leading icon cell rendered in the header band before the title. Pass any 14–18px SVG; the kit colours it --fg-dim so it reads as a quiet leading marker. Vertical-aligned with the title + count chip via the band's align-items: center. Mirrors the <PageHeader icon> slot's contract. Default-variant only — the hero variant uses avatar instead. Documented in packages/ui/docs/SectionCard.md.

Fixed

  • R15-4: <Modal size> prop was a silent no-op — the React component already set data-size on the root and the backdrop, but @magicblocksai/css shipped no .modal[data-size="…"] selectors, so every modal rendered at the bare 460px default regardless of what consumers passed. Added the four size variants with viewport-min() guards so small-screen modals hug the edges instead of overflowing horizontally: - smmin(380px, calc(100vw - var(--s-5) * 2)) - mdmin(560px, calc(100vw - var(--s-5) * 2))default size, was effectively 460px - lgmin(720px, calc(100vw - var(--s-5) * 2)) - xlmin(960px, calc(100vw - var(--s-5) * 2))

Visible default change: any consumer passing <Modal size="md"> (or omitting the prop, since "md" is the default) jumps from the silent 460px to 560px. Consumers passing lg / xl jump from 460px → 720 / 960. The bare .modal { max-width: 460px } rule stays as a fallback for raw-HTML consumers using <div class="modal"> without a data-size attribute. Spark's local override block can be removed.

Notes

  • Pair with @magicblocksai/css@1.13.2.
  • The Modal default-size jump (460 → 560px) is the only consumer-visible change. Modals that genuinely should stay narrow (confirm dialogs, simple prompts) should pass <Modal size="sm"> to land at 380px.

[1.13.1] — 2026-05-06

Patch — Spark Round 15 (token alias + docs).

Added

  • --font-sans Tailwind theme alias (R15-3) — both tailwind.preset.cjs (v3) and theme.css (v4) now expose sans as an alias for the kit's body face, so font-sans utility classes resolve to --f-body for muscle-memory consumers. Pairs with the new --f-sans CSS custom-property alias in @magicblocksai/css@1.13.1.

Documentation

  • RichTextEditor.md: "When to outgrow this" section (R15-1). The kit's source comment foreshadowed a future TipTap engine swap; Spark hit that wall, evaluated the trade-off, and stayed local. The doc now explicitly documents the limits of the kit's contenteditable + execCommand engine, the four signals that mean you've outgrown it (mention atoms, inline media nodes, schema-driven validation, slash-command palettes), and a reference for how Spark built <SparkEditor> on TipTap 3.x as a consumer-side wrapper. The kit may absorb a TipTap-based <RichTextEditorPro> (or rev <RichTextEditor> to v2 with engine config) once a second consumer hits the same wall — until then the engine swap stays consumer-side because the ~140KB gzipped bundle cost shouldn't fall on every kit consumer.
  • INSTALL-FROM-NPM.md: explicit pointer to the CSS variables reference (R15-3). Adds a "CSS variables — single source of truth" callout pointing at brand.magicblocks.ai/tokens#variables (filterable list of every CSS custom property the kit declares). Spark scanned the npm exports and missed the website page; the explicit pointer should prevent the same trip-up for future consumers.

Notes

  • Pair with @magicblocksai/css@1.13.1.
  • No React API surface changes — patch release.
  • R15-2 (<DenseRow>) explicitly carried forward as deferred per Spark's note ("local row layout there is stable so this stays deferred until another consumer appears").

[1.13.0] — 2026-05-05

Spark CRM Round 14 — bulk-selection on <InboxRow> + new <InlineDateField> primitive.

Added

  • <InboxRow selectable selected onSelectChange> (R14-1) — first-class bulk-selection slot on the kit's existing inbox row. Renders a leading 16px checkbox column. The kit handles the click target (clicks on the checkbox don't fire onOpen), the row tinting (.is-selected adds --accent-soft background), and the keyboard model (Space toggles selection instead of opening when selectable; Enter always opens). onSelectChange(next, shiftKey, id) passes the Shift modifier so consumers can implement Linear / GitHub-style range-select against their own "last anchor" id. Documented in packages/ui/docs/Inbox.md.

Replaces the consumer-side wrap-with-sibling-checkbox pattern (Spark's apps/web/src/routes/TasksPage.tsx had a 32px-left grid + sibling <input type="checkbox"> outside the row's interactive surface). Both Spark's Todos page + project tasks list collapse to the kit prop on adoption.

  • <InlineDateField> (R14-2) — edit-in-place date field for detail-page rails. Click the resting label → swap to a native <input type="date">; Enter / blur commits, Escape cancels. Default smart relative formatter ("Today", "in 3d", "3d late") with tone-driven colour (warning today, error overdue). format prop overrides the formatter; clearable={false} for required-date fields. Documented at packages/ui/docs/InlineDateField.md.

Replaces the local DueDateField pattern Spark was carrying in IssueDetailPage.tsx and the equivalent "click-to-edit a date in a rail" pattern other consumers were rolling. Visual + keyboard contract matches Linear / Notion / Asana inline date fields.

Notes

  • Pair with @magicblocksai/css@1.13.0 (_shared.css mirror picks up the .ix-select family + selected-row tint + the .inline-date-field family).
  • <InlineDateField> joins the kit-chrome carve-out (no per-component chapter demo; documented at packages/ui/docs/InlineDateField.md).
  • <InboxRow> selection is additive — existing consumers without selectable see no change. The Space-keypress behaviour change (toggle selection vs. open) is gated on selectable being truthy.

[1.12.0] — 2026-05-05

Spark CRM Round 13 — biggest single release since v1.10.0. 9 items across two of Spark's local rounds (R11-2/3/4 + R12-1/2/3/4/5/6).

Added

  • <MultiSelect> (R12-3) — chip-rendered multi-value selector with searchable popover. Backspace at empty input removes last chip. closeOnSelect: false by default (multi-pick is iterative). Optional maxChips for overflow +N chips. Works sync OR async via onSearchChange for server-side option resolution. Documented at packages/ui/docs/MultiSelect.md. Replaces consumer-side comma-separated <Input> fallbacks for multiselect custom-field types.
  • <PageHeader> (R12-4) — canonical "eyebrow · title · summary · actions" rhythm for product-app routes. Bakes the type scale, gap, mobile wrap behaviour, and the icon-in-h1 pattern. level prop (1/2/3) for nested chrome. Documented at packages/ui/docs/PageHeader.md. Replaces ~38 hand-rolled .spark-page-head instances across Spark's routes.
  • <MiniCardLink> + <MiniCardLinkList> + <MiniCardMeta> (R12-5) — linked-entity row pattern for <SectionCard> body slots (companies on a contact page, tickets on a deal page). Polymorphic root: <a> when to, <button> when onClick, <div> otherwise. LinkComponent for SPA routing. Documented at packages/ui/docs/MiniCardLink.md.
  • <AgendaList> + <AgendaRow> + <AgendaTitle> + <AgendaMeta> + <DateBand> (R12-6) — time-grouped list for calendar / upcoming / agenda surfaces. Generic <AgendaList<T>> with groups + renderItem; <AgendaRow> is the canonical row (time-prefix · body · meta) with the same polymorphic root pattern as <MiniCardLink>. <DateBand> is the standalone date-band heading — usable above any list. Documented at packages/ui/docs/AgendaList.md. The <DateBand> upstream from the kit's deferred-candidate flag.
  • <AppShell mobileNavOpen> + onMobileNavOpenChange + drawerWidth (R12-2) — opt-in slide-in drawer for the sidebar at the mobile breakpoint. Bakes the slide animation, scrim overlay, and --app-shell-drawer-w token. Consumer-controlled state (useState + hamburger click). Auto-close on route change + Esc + scroll-lock are documented as a tiny consumer-side hook composing useEscapeKey + useScrollLock from the kit's lib/. Documented in the existing packages/ui/docs/AppShell.md (TODO removed).
  • <DashboardNavGroup forceOpen> (R11-4) — override the persisted state without disturbing it. The canonical pattern: detect "this group contains the currently-active route" router-side, lift it as forceOpen. Manual toggle clicks no-op while forced. When forceOpen flips back to false, the group returns to the user's last toggled preference. Documented in DashboardNavGroup.md.
  • <Inbox banded> prop + .inbox.inbox-banded CSS (R11-3) — opt-in row alternation for long inbox lists. Hover state unchanged.
  • <InboxRow secondaryActionVisibility> (R12-1)"always" (default — quiet at rest, lifts on hover) or "hover" (legacy Gmail-style hover-only). Pre-1.12.0 the secondary-action button was effectively invisible at rest — un-discoverable on touch and on first scan.
  • <Avatar tone> prop (R11-2)"neutral" (default) / "accent" / "info" / "success" / "warning" / "error". Backed by new .av-tone-* CSS modifiers.

Changed

  • .av ships with a default fill (R11-2) — visible to consumers. Pre-1.12.0 .av was background-less; on a same-tone page wash (e.g. --warm-1) initials rendered invisible. Default now background: var(--bg-warm); color: var(--fg-soft). Image children clip via overflow: hidden so this fill is invisible behind avatars rendered as <img>. Five .av-tone-* modifiers ship alongside.
  • .inbox-row .ix-action rest state (R12-1) — visible to consumers. Was opacity: 1 but background-less + transparent border (effectively invisible at rest). Now color: var(--fg-faint); opacity: 0.7 at rest, lifts to opacity: 1 on hover. Opt back into the legacy hover-only behaviour via the new secondaryActionVisibility="hover" prop or the .inbox.inbox-actions-on-hover class.

Notes

  • Pair with @magicblocksai/css@1.12.0.
  • All five new components join the kit-chrome carve-out (no per-component chapter demos; all documented at packages/ui/docs/).
  • Two consumer-visible visual changes — both opt-out via class or prop. Marketing demos using bare .av or .ix-action should re-check on upgrade; product apps benefit immediately.

[1.11.1] — 2026-05-05

Patch — Spark Round 12.

Fixed

  • R12-1: <ActivityTimeline> rail extended past day boundaries. The .act-row::before 1px hairline painted top: 0; bottom: 0 on every row. On the first row of each day group, the top: 0 stub extended UP into the row's top padding, visually bridging the gap to the previous day's last-row stub — the rail looked continuous past the date divider. Added .act-divider + .act-row::before { top: 50% } so the rail starts at the icon centre on the first row of each day. Pairs with the existing :last-child::before { bottom: 50% } so the rail terminates cleanly at both ends of each day group.

Notes

  • Pair with @magicblocksai/css@1.11.1 (_shared.css mirror picks up the one-line CSS patch).
  • Spark hot-patched locally in apps/web/src/index.css — that override can be removed on adoption.

[1.11.0] — 2026-05-05

Spark CRM Round 11 — collapsible nav-group wrapper for long product-app sidebars.

Added

  • <DashboardNavGroup> (R11-1) — wraps a .dash-nav-label heading + a stack of .dash-nav-item rows in a <details> element so the section can be collapsed by clicking the heading. Built on native <details> / <summary> so the no-JS path works in SSR / printable / no-script environments. Optional localStorage persistence keeps the open state across reloads (persistKey="crm.nav.today"). Controlled mode via open + onOpenChange for "collapse all" header buttons or cross-sidebar sync. Documented at packages/ui/docs/DashboardNavGroup.md.

Long sidebars (CRMs, product apps with many sub-areas) get hard to scan past ~10 rows. Linear / Slack / Notion all let users collapse sections — <DashboardNavGroup> ships that pattern as a kit primitive so consumers stop reaching for raw <details> + custom CSS.

Lazy useState initialiser reads localStorage synchronously on first client render, so the group doesn't flash open-then-close (or vice versa) during hydration. SSR-safe: returns defaultOpen on the server, reads localStorage on the client during the first render.

Pure-CSS markup also works without React: ``html <details class="dash-nav-group" open> <summary class="dash-nav-label">Today</summary> <a class="dash-nav-item" href="/overview">Overview</a> … </details> ``

Notes

  • Pair with @magicblocksai/css@1.11.0 (_shared.css mirror picks up the .dash-nav-group family).
  • Joins the kit-chrome carve-out (no per-component chapter demo; consumer-facing CRM primitive).
  • Mixing <DashboardNavGroup> with bare .dash-nav-labels in the same sidebar is fully supported — only the sections you want collapsible need to be wrapped.

[1.10.0] — 2026-05-04

Spark CRM Round 10 — two new CRM-canonical primitives.

Added

  • <MentionInput> (R10-1) — async @-mention textarea. Type the trigger char (default @) → popover anchored below the textarea (Linear / GitHub pattern, no caret-pixel measurement) showing items resolved by your search(query) callback. Sync return for static lists, Promise for async APIs. Inserts @Label markers into the body; extractMentions(body) recovers the ids at save time. Configurable trigger char (use # for channels, / for slash commands), formatMention for custom marker shapes, onMention(item) for tracking which entities were mentioned. Cmd/Ctrl+Enter fires onSubmit. Documented at packages/ui/docs/MentionInput.md. Replaces consumer-side <MentionTextarea> workarounds.
  • <EmailThread> (R10-2) — full email viewer (NOT chat bubbles). Per-message header with sender + Details toggle (To / Cc / Bcc / Date dropdown). Latest message expanded by default; older messages collapsed inline as one-line summaries; click to expand. Quoted history (<blockquote>, Gmail's gmail_quote div, "On … wrote:" pattern) auto-collapses behind a "Show trimmed content" chevron. Attachment chips at the bottom of each body. Optional opens / clicks / replied tracking icons next to the timestamp. Reply / Reply all / Forward action bar via onReply / onReplyAll / onForward props; renderComposer(latest) slot for an inline reply composer. Documented at packages/ui/docs/EmailThread.md.

Security: the html field is rendered via dangerouslySetInnerHTML — pre-sanitise on the consumer side (DOMPurify, sanitize-html, etc). The kit is intentionally agnostic about which sanitiser you use. Documented prominently in EmailThread.md.

Use <EmailThread> for email; use <ConversationPreview> / <MessageBubble> for chat-style UI. Email reads as paragraph text from a named sender at a fixed time, with quoted history and attachments — chat-bubble UI applied to email reads as messaging, not as mail. Gmail / Front / Spark / Linear all draw the same line.

Notes

  • Pair with @magicblocksai/css@1.10.0 (_shared.css mirror picks up the new .mention-input + .mention-picker* family and the .email-thread + .email-message* family).
  • Both new components join the kit-chrome carve-out (no per-component chapter demo; verified via the kit-site Astro build + pnpm check:docs).
  • Both 'use client' (stateful — picker open/highlight/async items for MentionInput; per-message expand/collapse + details toggle + quoted-history toggle for EmailThread).

[1.9.3] — 2026-05-04

Patch — Spark Round 9. Two CSS-only fixes for the SectionCard + Inbox composition.

Fixed

  • R9-1: nested list-root chrome doubled up inside <SectionCard>. When <Inbox> (or <Checklist>, <DataTable>, <Calendar>, <Panel>) sits directly inside <SectionCard>'s body, the inner primitive's own border + radius + paper bg painted on top of the SectionCard's identical chrome. Added a direct-child reset (section-card-body > .inbox, etc.) that strips border / radius / background / overflow / shadow from nested list-roots. Generalises to every "list-root" primitive consumers commonly nest. Direct-child only, so deeper-nested primitives that legitimately want their own chrome stay untouched.
  • R9-2: <InboxRow state="overdue"> red dot was clipped + bled through the .inbox border-radius. Replaced the 6px ::before dot at left: 0 with a Linear-style 3px left-rail accent (box-shadow: inset 3px 0 0 var(--error)). No layout shift, the parent .inbox's overflow: hidden clips the rail cleanly at the rounded corners, and "needs attention" rows read at-a-glance from across the screen in a way the small dot never did.

Notes

  • Pair with @magicblocksai/css@1.9.3 (_shared.css mirror).
  • All consumers using <SectionCard> + <Inbox> together (or <Inbox state="overdue"> rows) should bump.

[1.9.2] — 2026-05-03

Patch — Spark Round 8.

Fixed

  • R8-1: kit <Button> in the <SectionCard> action slot pushed the header band ~21px taller. The default .btn is 38.5px tall vs the band's natural ~17px text height; with align-items: center the band grew to fit. Added a scoped reset for .section-card-action .btn (compact 5×12px padding, 12.5px font, sm radius, no primary shadow) so any kit button slotted as the action stays sized to the band. Consumer-side custom buttons that don't use the .btn class are untouched.

Added

  • R8-2: "snoozed" value on TicketStatusValue. Renders with the .pill-purple tint to read as "set aside for later" — distinct from closed (muted-grey) and pending (warning-yellow). Mirrors the InboxRow state="snoozed" semantic without sharing visual styling with closed. Spark's local fallback to a neutral <Chip> can be removed.

Notes

  • Pair with @magicblocksai/css@1.9.2 (_shared.css mirror picks up the .section-card-action .btn reset).

[1.9.1] — 2026-05-03

Patch — Spark Round 7 hot-fix.

Fixed

  • R7-1: <SectionCard variant="hero"> rendered avatar stacked above title. The default-variant .section-card { flex-direction: column } rule (correct for the header-band variant) leaked into the hero variant via shared classes, so hero tiles rendered avatar-on-top-of-title instead of beside-it. Added flex-direction: row to .section-card-hero to re-assert the horizontal layout. Spark's local override can be removed.

Added

  • R7-2: <SectionCard variant="hero" LinkComponent={Link}>. Swap the default <a href> for a custom Link component (Next.js <Link>, React Router <Link> via shim, TanStack Router <Link> via shim) so the hero tile triggers client-side navigation instead of a full reload. Frameworks that name the prop to instead of href (React Router, TanStack) need a tiny shim — documented at packages/ui/docs/SectionCard.md.

Notes

  • Pair with @magicblocksai/css@1.9.1 (lockstep — _shared.css mirror picks up the flex-direction: row fix).
  • All consumers using <SectionCard variant="hero"> should bump.

[1.9.0] — 2026-05-03

Spark CRM Round 6 — surface hierarchy. 3 changes that make the kit's "cards on a page" pattern work without local overrides.

Added

  • <AppShell pageWash> prop + --app-page-bg token (R6-1). Opt into a recessed page wash so cards visually lift off the background. Default false (page bg matches card bg, the back-compat behaviour). When true, page bg flips to --warm-1 in light / --ink in dark — the standard "cards pop off a recessed page" pattern every CRM-style consumer hits. Pairs naturally with the new Card shadow default below.
  • <Card flat> prop + .card-flat class (R6-2). Opt back into the hairline-only card for inline contexts that don't need the shadow.
  • <SectionCard variant="hero"> (R6-3). Clickable single-row context tile (avatar · title · sub) for the "Company / Primary Contact" pattern on detail pages, "Associated work" rows, etc. New props: variant, sub, avatar, avatarStyle, href, onClick, target, rel. Renders as <a href> when href is set, <button> when onClick is set, <div> otherwise. Hover lifts to var(--sh-2) with an accent border; focus shows the standard kit ring.

Changed

  • <Card> ships with box-shadow: var(--sh-1) by default (R6-2) — visible to consumers. On a same-colour page bg, hairline + radius alone makes cards visually disappear. The small drop shadow gives the "card feels like a card" treatment every product app reaches for. Marketing-grid contexts that intentionally want flat cards (.feat-grid, .kpi-grid, etc.) opt out via <Card flat> or .card-flat.

Notes

  • Pair with @magicblocksai/css@1.9.0.
  • All changes are additive props or new defaults — no breaking React API changes. The Card shadow default is the only consumer-visible visual change; opt out per-card via flat.
  • The <AppShell pageWash> + <Card> shadow pair work together: page recesses, cards lift. Use both for the canonical cards-pop-off-page CRM look.

[1.8.0] — 2026-05-03

Spark CRM Round 5 — 5 issues across 2 bugs, 1 default-styling change, and 2 layout-token / prop additions. All kit-wide improvements.

Added

  • <AppShell topbarHeight> prop + --app-topbar-h token (R5-4). AppShell now exposes its topbar height as a CSS custom property so consumers can write calc(100vh - var(--app-topbar-h)) instead of hardcoding calc(100vh - 56px) magic numbers. Default 56px, override via the prop or the variable directly. Sidebar width is also aliased as --app-shell-side-w (alongside the existing --app-shell-side) for naming consistency.
  • <Kanban fillHeight> prop + --kanban-col-min-h / --kanban-col-max-h tokens (R5-5). Drops the demo's 360–520px column height caps to 0 / none and sets height: 100% on both the kanban root and its columns. The HubSpot-style "deals view fills the viewport" look. Granular alternative: set the two CSS variables directly for specific values.

Changed

  • Refined default scrollbars (R5-3) — visible to consumers. @magicblocksai/css now ships slim, theme-aware, hover-lift scrollbars (10px, rounded thumbs, transparent track, ink-tint in light / cream-tint in dark). Native scrollbars looked industrial against the rest of the kit's chrome. Opt out via <body data-scrollbars="native"> — restores the OS default for users who've configured custom scrollbar widths via OS / browser preferences.

Fixed

  • R5-1: <AppShell scrollMode="page"> sidebar rules survived past the mobile breakpoint. The position: sticky; max-height: 100vh rule applied even when .app-shell-side was already display: none on mobile, forcing consumer slide-in nav drawers to fight position: fixed !important; max-height: none !important; width !important; top !important overrides. Now gated to @media (min-width: 961px) so mobile slide-in panels work without the override war.
  • R5-2: .app-shell-side painted no background in dark mode. v1.7.0 (R3-2) scoped the warm-cream sidebar bg to light mode only; dark-mode in-grid sidebars were fine because they inherited paper from the surrounding .app-shell, but a repositioned sidebar (slide-in panel, drawer, overlay) rendered transparent. Now .app-shell-side paints var(--bg-paper) unconditionally; the light-mode warm override still wins under body:not([data-theme="dark"]).

Notes

  • Pair with @magicblocksai/css@1.8.0.
  • All changes are CSS-only, additive React props, or new defaults — no breaking changes for existing consumers (the scrollbar styling change is the only consumer-visible default; opt out via data-scrollbars="native").

[1.7.1] — 2026-05-02

Patch — Spark Round 4 (early hot-fix). Two issues caught during Round 3 close-out, both shipped together.

Fixed

  • R4-1: <AppShell scrollMode="page"> clobbered both overflow axes on .app-shell-main. v1.7.0 set overflow: visible, which let a horizontally-scrolling child (e.g. <Kanban direction="horizontal"> on /deals) expand the grid column past viewport width and drag the sticky topbar off-screen. Now the page-scroll mode keeps overflow-x: hidden to contain horizontal scrollers and only releases overflow-y so vertical page scroll bubbles to <html>/<body>. Also added min-width: 0 so the 1fr grid track actually constrains (default min-width: auto makes it grow to intrinsic content width).
  • R4-2: DataTable recipe in docs/DataTable.md used invented prop names. The original recipe used id / header / accessor / sortValue / rowKey / selected / onSelectedChange — none of which exist on the actual exported types. Rewrote the recipe against the real API: key / label / render / rowId / selectable / selectedIds / onSelectChange plus separate sortBy: string and sortDir: "asc" | "desc". Added a note that sortable always sorts on row[key] directly (no per-column sortValue accessor in v1.7.x — flag as a gap if real CRM cases need it).

Notes

  • Pure CSS + docs change; no React API change. Pair with @magicblocksai/css@1.7.1.
  • All consumers on v1.7.0 should bump — R4-1 affects anyone using scrollMode="page" with a horizontally-scrolling child; R4-2 affects anyone copy-pasting from the DataTable recipe.

[1.7.0] — 2026-05-02

Spark CRM Round 3 — 7 issues addressed (2 dark-mode bugs, 2 layout-mode props, 1 new component, 1 row-action affordance, 1 docs recipe).

Added

  • <SectionCard> (R3-5) — kit-chrome primitive for the canonical CRM detail-page section pattern. Warm-banded header (title · count · action) over an unpadded body region (so list primitives like <Inbox> / <DataTable> sit edge-to-edge). padBody prop for prose / KV-pair bodies; emptyState prop for the no-content case. Documented at packages/ui/docs/SectionCard.md.
  • <Kanban direction="horizontal"> (R3-3) — opt-in horizontal-scroll layout for pipelines with many stages. Default "grid" mode unchanged. Pair with --kanban-col-count (column count token, default 4) for the grid mode and --kanban-col-width (default 280px) for the horizontal mode.
  • <AppShell scrollMode="page"> (R3-4) — opt-in for whole-page scrolling. Default "columns" mode unchanged. In "page" mode the sidebar becomes position: sticky; top: 0; align-self: start; max-height: 100vh; overflow-y: auto, the topbar stays sticky, and <html>/<body> own the scrollbar — the right shape for long content where browser scroll-restoration, mobile scroll-into-view, and "scroll-to-top" should behave the way users expect from a webpage.
  • <InboxRow secondaryAction> (R3-6){ icon, label, onClick } slot for a row-level secondary action button alongside complete/snooze. The discoverable, touch-friendly alternative to onContextMenu for "open in side tray" / "copy link" / etc.
  • DataTable sortable-CRM-table recipe (R3-7) — full code recipe in packages/ui/docs/DataTable.md covering the canonical /leads / /broadcasts / /sequences shape: sortable + sticky header, click-row-to-navigate, bulk-select column, mobile horizontal-scroll, server-side sort/pagination notes.

Fixed

  • R3-1: dark-mode --bg-warm collapsed to --bg-paper. Both tokens were #2A3050, so any warm-tinted band (table headers, list section heads, panel-head, footer chrome) read as flat in dark mode. Lifted --bg-warm to #323858 (~+5% over paper) so warm bands read as elevated. Light mode unchanged.
  • R3-2: .app-shell-side pinned ink-coloured tokens regardless of theme. Sidebar stayed warm-cream when the rest of the app went dark — broken for any product-app dark mode. The warm-pin block (--fg, --fg-soft, --fg-dim, --hair, background) is now scoped to body:not([data-theme="dark"]). Dark mode falls through to the global semantic tokens. Marketing chrome still gets the warm look; product apps get a properly-themed sidebar.

Notes

  • Pair with @magicblocksai/css@1.7.0 (lockstep — _shared.css mirror picks up the dark-mode + Kanban + AppShell + SectionCard CSS changes).
  • All fixes are CSS-only or additive prop / new-component changes — no breaking changes for existing consumers.

[1.6.0] — 2026-05-02

Compatibility — Tailwind v4 + React 19 + TypeScript 5.7.

Added

  • @magicblocksai/ui/theme.css — new export for Tailwind v4 consumers. CSS-first config: drop @import "@magicblocksai/ui/theme.css" after @import "tailwindcss" and every brand token (bg-paper, text-fg-soft, gap-s-4, rounded-lg, shadow-sh-2, font-display, …) becomes a utility class. Includes a @custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *)) so the kit's body[data-theme="dark"] strategy works under v4. Tailwind v3 consumers continue using tailwind.preset.cjs unchanged.
  • tailwindcss peerDep marked optional via peerDependenciesMeta — vanilla-CSS-only consumers (using @magicblocksai/css without React) no longer get a phantom Tailwind warning.

Changed

  • React 19 type compatibility. Bumped devDeps to react@^19, react-dom@^19, @types/react@^19, @types/react-dom@^19. The shipped .d.ts now resolves cleanly against React 19's stricter types. PeerDeps stay at >=18 so React 18 consumers still install — runtime is unchanged.
  • TypeScript 5.7 in devDeps. Authoring-only bump; no consumer impact.
  • Tailwind 4 added to devDeps for parity with the new theme.css path.
  • tsup bumped to ^8.5.1 (latest patch).

Fixed (React 19 type breaks)

  • Checklist: ChecklistProps now Omit<HTMLAttributes<HTMLDivElement>, "title" | "onToggle"> — React 19's HTMLAttributes types include the new HTML <dialog> toggle event, which collided with our onToggle: (id: string) => void prop signature.
  • KitCommandPalette: JSX.ElementReact.JSX.Element (the global JSX namespace was removed in React 19's types).

Notes

  • @magicblocksai/ui and @magicblocksai/css bumped together to 1.6.0 for clarity, even though the CSS package didn't change in source — version-lockstep keeps the install story simple.
  • Pair with @magicblocksai/css@1.6.0.

[1.5.2] — 2026-05-02

Patch — narrowed dark-mode elevation to overlays only.

Fixed

  • v1.5.1 over-applied the elevation cue. The drop shadow + inset top highlight were added to content containers (.card, .panel, .data-table, .calendar, .checklist, .inbox) along with true overlays. On detail pages every section ended up looking like a floating card. Narrowed the rule to overlays only: .modal, .drawer, .popover, .menu, .cmdk, .combobox-popover, .nav-chapters-panel. Content containers stay flat — they sit on the page with the lifted --bg-paper + 1px hair border for separation, no shadow.

Notes

  • Pure CSS change; no React component changes. Bumped in lockstep with @magicblocksai/css@1.5.2.
  • Light mode unchanged. The --bg-paper lift, --hair opacity bump, and shadow re-aliases from 1.5.1 all stay.

[1.5.1] — 2026-05-02

Patch — dark-mode contrast for elevated surfaces.

Fixed

  • Modals, popovers, list containers, and inboxes blended into the page in dark mode. The previous --bg-paper (#232842) was only ~6% lighter than the ink page background (#191E32) — modals like the "Pick a segment" dialog and list cards like the inbox / leads / tickets list visibly disappeared into the page. Lifted --bg-paper to #2A3050 (~12% lift, clearly elevated). Bumped --hair opacity 0.14 → 0.18 so list-row separators are visible against the new lifted paper. Added a subtle inset top highlight (inset 0 1px 0 rgba(255,255,255,0.05)) to elevated chrome (modal, drawer, popover, menu, cmdk, combobox-popover, nav-chapters-panel, inbox, checklist, data-table, calendar, card, panel) — standard "light-from-above" elevation cue.
  • Dark-mode drop shadows were nearly invisible. The --sh-1 through --sh-4 tokens used rgba(25, 30, 50, …) (the ink colour) which is essentially invisible against the ink page. Re-aliased in dark mode to use rgba(0, 0, 0, …) with deeper alpha (0.30 / 0.32 / 0.40 / 0.50). Modals now sit on a visible drop shadow.

Notes

  • Light mode unaffected. Paper white on warm-cream page is already plenty of contrast. All overrides are scoped to body[data-theme="dark"].
  • Pair with @magicblocksai/css@1.5.1 (the _shared.css mirror).

[1.5.0] — 2026-05-02

Spark CRM Round 2. Restored the marketing-icon family that v1.4.0 inadvertently dropped, added 3 task-type icons, and gave the <Inbox> row some breathing room based on visual feedback against the live demo.

Added — icons (13 new exports)

  • Restored 10 marketing-feature icons that v1.3 documented in JSDoc but v1.4.0 didn't ship: BoltIcon, InboxIcon, LinkIcon, MailIcon, MenuIcon, PlatformIcon, RouteIcon, ShieldIcon, SyncIcon, TargetIcon. Same monoline / 18×18 / 1.5px / currentColor style as the Round 1 CRM-nav set.
  • 3 task-type icons for CRM-style task lists: PhoneIcon (call), ClockIcon (sla_check / time tracking), EyeIcon (review / approval).

Changed — <Inbox> row vertical rhythm

Visual feedback against https://brand.magicblocks.ai/components/14-app-pipeline#inbox: the row felt cramped because it was a fixed height: 52px with two-line content (title + sub-line) crammed in, no vertical padding, 28px avatar pushing against the title, 2px title→sub gap.

  • heightmin-height so rows grow when content needs it
  • padding: 10px var(--s-4) — symmetric vertical breathing
  • Avatar 28→32, font 11→12 — reads at the same weight as the title now
  • Title→sub gap 2→4px
  • Sub-line line-height 1.2→1.3 (matches title)
  • Compact density override: padding: 6px var(--s-4) so body[data-density="compact"] still hits the ~36px target

Comfortable rows now feel ~60px tall vs the old fixed 52px — closer to a production CRM inbox.

Migration notes

  • The inbox change is technically breaking for consumers pinning the exact 52px height via custom CSS. min-height accommodates anything ≥52px, so most consumers inherit the new rhythm without code changes.
  • Pair with @magicblocksai/css@1.5.0 (the _shared.css mirror).

[1.4.1] — 2026-05-02

Patch — popover positioning bug for real this time.

Fixed

  • Popovers no longer park at viewport (0,0) when their content is portal-rendered. The v1.4.0 fix in usePopover.ts (rAF retry when refs are null) tried to catch the same bug but the timing assumptions about React render ordering didn't always hold. Spark hit it on every dropdown menu open. The real root cause was lib/Portal.tsx's deferred-mount pattern (useState(false) + useEffect) which left contentRef.current null during the consumer's first effect cycle. Two-file fix: - lib/Portal.tsx — switched to useState(() => typeof document !== "undefined") lazy init. Mounts synchronously on first client render; returns null on the server. Refs populate during the first commit. - lib/usePopover.ts — switched from useEffect to useLayoutEffect (with SSR fallback). Coords compute before paint, not after. Eliminates even the brief 0,0 flash. The rAF retry stays as a belt-and-braces safety net for consumers using a custom Portal target that isn't in the DOM yet at toggle time.

Affects every popover-based component without API changes: <Menu>, <DropdownMenu>, <Popover>, <Combobox>, <DatePicker>, <DateRangePicker>, <MergeTagInput>, <MergeTagTextarea>, <KitChapterNav> mega-dropdown, <KitCommandPalette>, <DialogHost>.

Notes

  • @magicblocksai/css unchanged — stays at 1.4.0.
  • Spark consumes via pnpm update @magicblocksai/ui@^1.4.1. Local PopoverMenu.tsx workaround can be deleted; <DropdownMenu> / <Menu> work directly with placement and offset doing what the docs say.

[1.4.0] — 2026-05

Spark CRM migration follow-up. One new primitive (<AppShell>), four bug fixes, and a small batch of CSS clean-ups. Mirrored in @magicblocksai/css@1.4.0 (the kit's _shared.css is the single source of truth so the CSS package gets bumped in lockstep).

Added — components

  • <AppShell> — full-viewport app chrome. Sibling to <DashboardShell>, but built for whole-app surfaces (100vh, no border, no border-radius) instead of embedded marketing demos. Two-column grid [sideWidth] 1fr with an optional sticky topbar slot in the main column. Sidebar fills viewport height with its own scroll; main column scrolls independently. sideWidth prop or --app-shell-side custom property to override. ARIA: <aside role="navigation"> + <main role="main">. Mobile (≤960px): sidebar hidden — consumers should layer their own hamburger drawer. Carve-out from the per-component chapter-demo gate via KIT_CHROME in scripts/check-coverage.mjs.

Added — Card prop

  • <Card narrow> — opt-in modifier that re-applies the legacy 320px max-width cap. Maps to the new .card-narrow class so HTML / Astro / 11ty consumers get the same hook. See "Fixed" below for why the default changed.

Fixed

  • usePopover no longer parks portal-rendered content at (0, 0). When the popover content was rendered via <Portal>, Portal's SSR-safe deferred-mount pattern meant contentRef.current was still null the first time usePopover's effect ran. The compute would bail out, no listeners fired again, and the menu sat at the viewport top-left forever. The compute now retries on requestAnimationFrame until both refs are populated. Affects <Menu> / <DropdownMenu> / <Popover> / anything else built on usePopover. (Fixes Spark issue #2.)
  • <DropdownMenu> now anchors to its trigger. Same root cause as the popover fix above — <DropdownMenu> is an alias of <Menu>, and the Spark CRM team was hitting the portal-late-mount race on every click. Default placement remains bottom-start.
  • <Card> no longer ships max-width: 320px by default. The cap was making <Card> useless as a list container. Drop the bare max-width from .card; restore it via a new opt-in .card-narrow class (or <Card narrow> from React). Chapter 5's three-card surface demo now sets card-narrow explicitly so the visual reference stays the same. (Fixes Spark issue #6.)
  • .dash-nav-item > span:first-child { flex: 1 } is gone. The bare-span selector ate every direct <span> child — a leading icon <span> would stretch to fill the row, and a trailing badge <span> would do the same. Replaced with an explicit .dash-nav-item-label class consumers wrap the label text in. <DashboardShell>'s default sidebar markup now emits <span class="dash-nav-item-label"> for every label; chapter 10's demo HTML and React snippet were updated to match. (Fixes Spark issue #7.)

Changed

  • .dash-nav-item vertical padding bumped from 8px → 7px, exposed as --dash-nav-item-py so consumers can override without rewriting the whole padding shorthand. Default is plenty of breathing room without making nav rows feel airy. (Fixes Spark issue #8.)
  • Small margin-top: 4px between a .dash-nav-label heading and the first .dash-nav-item underneath it — keeps group headings from sitting flush against the first row.

Removed

  • ./styles from packages/ui/package.json exports. The map referenced ./dist/styles.css, which never shipped. All CSS lives in @magicblocksai/css/dist/magicblocks.css (per the v1.3.0 handoff); the broken alias was just a footgun. (Fixes Spark issue #1.)

CSS

Net-new and changed CSS in components/_shared.css (mirrored verbatim in @magicblocksai/css@1.4.0):

  • Added under "v1.4.0 — AppShell": .app-shell + .app-shell-side + .app-shell-main + .app-shell-topbar. New custom properties --app-shell-side (default 248px) and --z-topbar (default 30).
  • Added .card-narrow (opt-in 320px cap, see above).
  • Added .dash-nav-item-label and .dash-nav-label + .dash-nav-item { margin-top: 4px }.
  • Changed .card no longer sets max-width: 320px.
  • Changed .dash-nav-item padding shorthand split into per-axis with padding-top: var(--dash-nav-item-py, 7px); padding-bottom: var(--dash-nav-item-py, 7px).
  • Removed .dash-nav-item > span:first-child { flex: 1 }.

Migration notes

  • Breaking-ish but quiet: .card no longer caps at 320px. If your layout depended on the cap (most marketing pricing-grid demos do), add card-narrow to the affected nodes — or pass narrow to <Card> if you're using the React component. Chapter 5's surface demo is the canonical example.
  • Breaking for .dash-nav-item consumers who wrote raw HTML: wrap the label text in <span class="dash-nav-item-label">…</span> so it absorbs the row's free space. Without the label class the label collapses to its content width and the row no longer pushes badges to the right edge. <DashboardShell>'s default sidebar markup already does this; the change shows up in the chapter 10 demo too.
  • No code change required for <DropdownMenu> / <Menu> / <Popover> consumers. The fix is internal to usePopover.

[1.2.0] — 2026-05

Tier-1 batch shipped to unblock the Spark CRM migration. Six consumer-facing primitives + four standalone hooks. No chapter demos — these are kit-chrome / consumer primitives that live alongside Modal / Popover / Toaster. Gate exemption added via KIT_CHROME in scripts/check-coverage.mjs.

Added — components

  • <DataTable> — sortable, sticky-header, cursor-paginated table built on a CSS grid (so the sticky header works inside any scrollable container). Selection column with indeterminate "select all", keyboard navigation (//Space/Enter), aria-sort on sortable headers, skeleton loading rows, and a "Load more" button driven by nextCursor. Default empty state is a centred "No results" message; pass empty to override.
  • <FilterChipGroup> — multi-select chip cluster with leading "All" chip and optional "+N more" overflow popover. Wraps the existing .chip styles; adds .chip-button, .chip-active, .chip-count, .chip-overflow modifiers. Uses <Popover> for the overflow.
  • <DatePicker> — single-date picker with single-month calendar in a popover. No external date library.
  • <DateRangePicker> — date-range picker with preset rail + two-month calendar. Six default presets exported as DEFAULT_DATE_RANGE_PRESETS (Last 7 / 30 / 90 days, This / Last month, This year). Trigger label switches to the active preset name when the selection matches a preset.
  • <DialogHost> + confirm() / alert() / prompt() — singleton dialog API on top of <Modal>. Mount one <DialogHost /> at the app root, then call confirm({...}) from anywhere (returns Promise<boolean>). destructive: true switches the confirm button to Button variant="danger". Esc / backdrop click resolve as cancelled.
  • useConfirm() — hook returning a stable callable with .alert() / .prompt() methods.

Added — hooks

  • useDebounce<T>(value, ms) — returns the input value after ms ms have elapsed without further changes. Cleans up on unmount.
  • useLocalStorage<T>(key, fallback)useState synced to window.localStorage. SSR-safe (returns fallback on the server and the first client render, hydrates on mount). Listens to the storage event for cross-tab sync.

Added — internal

  • lib/calendar.ts — tiny calendar helpers (startOfDay, addMonths, buildMonthMatrix, formatMonthYear, etc.) used by both pickers. No external date library — Date + Intl.DateTimeFormat only.
  • lib/CalendarGrid.tsx — internal calendar grid component shared by <DatePicker> and <DateRangePicker>. Not exported from the package barrel.
  • lib/dialogStore.ts — module-level singleton store powering confirm() / alert() / prompt(). Pub/sub pattern mirroring toastStore.

CSS

Net-new CSS appended to components/_shared.css under the heading "@magicblocksai/ui v1.2.0 — CRM primitives":

  • .data-table + .data-table-grid + .data-table-row + .data-table-cell + .data-table-head + .data-table-th + .data-table-sort + .data-table-skel-block (skeleton rows, animation suppressed under prefers-reduced-motion)
  • .filter-chip-group + .chip-button + .chip-active + .chip-count + .chip-overflow
  • .calendar + .calendar-head + .calendar-month + .calendar-nav + .calendar-weekdays + .calendar-weekday + .calendar-grid + .calendar-day (with .calendar-day-outside / .calendar-day-in-range / .calendar-day-selected modifiers)
  • .date-picker + .date-range-picker + .date-picker-trigger + .date-picker-trigger-ic + .date-picker-placeholder + .date-picker-popover + .date-range-popover + .date-range-presets + .date-range-preset + .date-range-preset-active + .date-range-calendars
  • .dialog-host + .dialog-body + .dialog-input

Toast — already shipped

The brief listed toast.success(...) / toast.error(...) / useToast() as Tier 1, but these were already shipped in 1.0+. No code change in this batch — the existing toast callable, useToast() hook, and toastStore singleton in Toaster.tsx already provide the documented API.

Notes for consumers

  • All ten additions are purely additive — no breaking changes.
  • Mount one <DialogHost /> at your app root (alongside <Toaster />) so confirm() / alert() / prompt() have somewhere to render.
  • <DataTable> uses CSS grid rather than <table> so the sticky header and column-width controls behave consistently across scroll containers. The wrapper still has role="table" and rows still have role="row" for screen readers.

[1.1.0] — 2026-04

Added

  • Drop-in chapter-demo defaults across ~80 components. Every demo-shaped component (<ChatCompare />, <JourneyMap />, <EngineBlock />, <HeroLiveDemo />, <HeroScene />, <DormantMine />, <IntegrationHub />, <ContrastPair />, <FeatureCluster />, <PricingPage />, <DashboardShell />, <DetailShell />, <SageDrawer />, <Kanban />, <Inbox />, <PipelineBar />, <Checklist />, <NotFoundPage />, <ConfettiTrigger />, <TopNav />, <SideNav />, <SiteFooter />, <Breadcrumbs />, <Tabs />, <Stepper />, <Timeline />, <Triptych />, <LogoWall />, <PressStrip />, <StatsStrip />, <TestimonialStrip />, <TeamGrid />, <OnboardingStep />, <Sparkline />, <PricingCard />, <ProductDashboard />, <KeyValue />, <KpiTile />, <DashboardTile />, <CalloutCard />, <EmptyAppCard />, <EmptyStatePage />, <MediaCard />, <CodeBlock />, <LeakCard />, <MrrChart />, <StatCard />, <Section />, <SettingsShell />, <StageChat />, <SLARing />, <DeviceFrame />, <Pagination />, <RiskBadge />, <StatBadge />, <HandoffCard />, <ProfileCard />, <KbSuggestionCard />, <ConversationPreview />, <ClusterMap />, <EdgeRace />, <RaceTimeline />, <Scoreboard />, <BrandTimeline />, <CounterRail />, <BulkBar />, <Carousel />, <Checklist />, <ComparisonTable />, <ActivityTimeline />, <EmptyState />, <Faq />, <FounderGrid />, <DemoTabs />, <ComposeDrawer />, <CommandPalette />, <FeatureCard />, <FeatureTable />, <GuardianShield />, <EcosystemRings />) now renders the chapter-demo content with zero props. Pass props to override; defaults take over only on the zero-prop call.
  • ChatCompareScenario switching. <ChatCompare scenario="auto" /> swaps the canonical mortgage refi conversation for the auto scenario in one prop. Six scenarios bundled: mortgage, insurance, solar, home-services, auto, fintech.
  • AGENTS.md "Drop-in defaults" section documents the zero-prop usage pattern and the zero-prop-only guard semantics for AI coding agents.
  • CHANGELOG.md (this file).

Fixed

  • Phantom-default content bug across 43 multi-prop components. The previous round used destructuring defaults (prop = DEFAULT_X), which filled in chapter content for every prop the consumer didn't pass — meaning <StatCard label="Handoffs" value="14%" /> rendered a phantom +94% delta from the chapter demo. Replaced with a "zero-prop-only" guard so consumers passing partial props get only what they passed; chapter content fills only when the consumer passes nothing. Reference fix: commit 9b274b4.
  • StatBadge.num and Sparkline.values were generic placeholders (42 and [22, 24, …, 41]) that broke the project's "real content, not placeholders" rule. Re-sourced from the canonical chapter demos: StatBadge from §11 Triptych "02 · Run" (Avg. / 5s / First response); Sparkline from §7.17's React snippet ([82, 81, 83, 84, 86, 86, 87]).
  • Markup-equivalence test gate now covers 23 fixtures (was 22). One net-new pass: CounterRail. Three fixtures added but skip-flagged with documented drift (BrandTimeline, ContrastPair, FeatureTable).

Notes for consumers

  • Behaviour is fully back-compatible. Every component still accepts the same props it accepted in 1.0; the only change is that previously-required props are now optional with a chapter-sourced default. Existing call-sites continue to work unchanged.
  • The "zero-prop-only guard" means consumers writing <HandoffCard initials="JS" /> will see only the initials they passed — the rest of the card (name, role, tags, facts) stays unrendered, matching "if missing, hide" semantics. To get the full chapter demo, call <HandoffCard /> with no props.
  • The AGENTS.md change is a docs-only update for AI coding agents reading the package; it doesn't affect runtime.

[1.0.0] — 2026-04

Added

  • Phase 5 milestone: full surface (chapters 02–16), modular CSS imports via @magicblocksai/css, all gates green.
  • Markup-equivalence regression gate (pnpm --filter @magicblocksai/ui test).
  • 'use client' directive coverage gate.
  • Authoring rules in CLAUDE.md and AUTHORING.md.

Older versions

See packages/ui/AGENTS.md § "Versioning" for the full milestone table back to 0.0.1.

@magicblocksai/css changelog

Format: Keep a Changelog · SemVer.

The CSS package mirrors components/_shared.css from the canonical brand-kit repo. It bumps in lockstep with @magicblocksai/ui whenever the shared stylesheet changes.


v4.13.0 — 2026-06-11

Agent Builder redesign Phase 4 — Templates + close-out (lockstep with @magicblocksai/ui@4.13.0). Additive — minor.

Added

  • .template-gallery + .template-card family (.is-selected / .is-blank, -head, -title, -badge, -desc, -shape, -block, -arrow, -meta) — the new-agent template gallery.
  • **.block-tpl-* family** (-list, -group, -group-label, -row, -text, -name, -sum, -insert) — the block-library drawer rows.

v4.12.0 — 2026-06-11

Agent Builder redesign Phase 3 — Sage (lockstep with @magicblocksai/ui@4.12.0). Additive — minor.

Added

  • .proposal-card family (.is-pending / .is-applied / .is-error tones, .proposal-eyebrow, -title, -summary, -old, -new, -diff + -diff-body, -undo) — Sage's unit of change; action buttons reuse the shipped .sage-prop-* styles.
  • .sage-dock family (.sage-dock-tabs, -tab + .is-active, -body) — the builder's Test + Sage right dock.

v4.11.0 — 2026-06-11

Agent Builder redesign Phase 2 — Studio (lockstep with @magicblocksai/ui@4.11.0). Additive — minor.

Added

  • .ab-leads family (.ab-leads-row, -label, -target + .is-muted, -rule, -action, -reached) — the leads-to / reached-from strip; the reached half reveals on :hover / :focus-visible / :focus-within.
  • .kf-line family (.kf-line-asked + .is-muted, -sep, -listened, -when) — the key-fact behaviour sentence.
  • .ab-rail-block-start / .ab-rail-block-hint — Start pill + needs-work coaching line on journey-rail blocks.
  • .journey-graph-edge[data-variant="dashed"] — the anytime layer on the journey map.
  • .action-list-desc — the framing line under the actions workbench head.

v4.10.0 — 2026-06-11

Agent Builder redesign Phase 1 — Frame (lockstep with @magicblocksai/ui@4.10.0). Additive — minor.

Added

  • .ws-nav-icon.is-accent — accent-tinted rail launcher (the pinned Ask Sage entry on the slim rail).
  • .golive-card family (.golive-head, -glyph, -title, -channels, -channel, -channel-icon, -channel-name, -channel-state + .is-live, -action) — the Overview go-live card.
  • .recent-changes family (.recent-changes-title, -list, .recent-change, -glyph + .is-sage, -text, -when, -undo) — the Overview accountability list with Sage-edit Undo.
  • .ab-frame-tabs — the five-tab agent-frame row beneath AgentBuilderHead (paper background + hairline under the head).

v4.9.0 — 2026-06-10

Knowledge lane class rename to the canonical vocabulary (lockstep with @magicblocksai/ui@4.9.0). Additive — minor.

Changed

  • **.knowledge-lane-*** — lane modifier classes renamed .knowledge-lane-relevant.knowledge-lane-semantic and .knowledge-lane-topic.knowledge-lane-conditional, matching the DB retrievalPolicy. <KnowledgeLane> emits the canonical class and normalises the deprecated relevant/topic kinds onto it.

v4.8.1 — 2026-06-10

Fixed

  • .knowledge-lane.source-row items nested inside a lane drew their own border + radius on top of the lane's, showing a double border with a rounded inner edge. Rows within a lane are now flattened (border / border-radius: 0) with a top hairline between them (none on the first); the lane keeps its single rounded border.

v4.8.0 — 2026-06-10

Knowledge editor lane + attach-picker CSS (operator surface; lockstep with @magicblocksai/ui@4.8.0). Additive — minor.

Added

  • **.knowledge-lane*** (operator) — the three-lane editor spine: a tinted header (dot + name + helper + count) over rows, toned by retrieval policy (always→accent, relevant→info, topic→success).
  • **.attach-lane-*** (operator) — the agent-attach picker: lane rows with item counts, a topic sub-selection area, and a summary line.

v4.7.0 — 2026-06-08

WorkspaceShell mobile-nav trigger (lockstep with @magicblocksai/ui@4.7.0). Additive — minor.

Added

  • .ws-mobile-trigger (operator surface) — the fixed hamburger button <WorkspaceShell> renders to open its mobile drawer. Hidden ≥961px and while the drawer is open; shown at ≤960px. Composes with the existing .app-shell[data-mobile-nav] drawer chrome (v1.12.0).

v4.6.0 — 2026-06-08

Canonical skeleton shimmer (core surface) + internal shimmer unification (lockstep with @magicblocksai/ui@4.6.0). Additive — minor.

Added

  • Skeleton shimmer (core): .skel, .skel-line, .skel-text (+ shortened last-child), .skel-av, .skel-r-{md,pill,full}, and @keyframes skel-shimmer, with a prefers-reduced-motion guard. Promoted from the chapter-9.8 demo so consumers can compose loading placeholders without re-deriving the shimmer.

Changed

  • Internal: the operator-region data-table / chat-transcript / source-card shimmers now reference the core skel-shimmer keyframe + gradient; their bespoke keyframes (data-table-skel, source-card-shimmer, chat-transcript-skel-pulse) are removed.

v4.5.0 — 2026-06-07

Operator CSS for the Sessions live takeover console + overview mobile cards (lockstep with @magicblocksai/ui@4.5.0). Additive — minor.

Added

  • New operator selectors: .takeover-console/.tc-* (control bar · transcript · guardrails-off composer · operator sidebar), .jbp-* (JourneyBlockPicker), .chat-msg.is-from-operator + .chat-msg-human (the operator message role), and .session-page[data-mode="live"] (live-mode hero). Plus a .ss-overview @media (max-width: 640px) block reflowing the session table to stacked cards. All token-driven, dark-mode-safe, prefers-reduced-motion honoured. Surface manifest regenerated (5197 entries).

v4.4.0 — 2026-06-07

Operator CSS for the Sessions read-path (lockstep with @magicblocksai/ui@4.4.0). Additive — minor.

Added

  • New operator selectors: .handover-outcome/.ho-*, .memories-dialog* + .memory-quote/.memory-type/.memory-history, .session-summary-band/.ssb-*, .session-page/.sp-*, and the Sessions overview .ss-* (date-range · stat strip · status flags · row actions). All token-driven, dark-mode-safe, prefers-reduced-motion honoured. Surface manifest regenerated (5147 entries).

v4.3.1 — 2026-06-06

Two operator-CSS polish fixes (lockstep with @magicblocksai/ui@4.3.1). Patch.

Fixed

  • .list-screen-head-actions now margin-left: auto — a list page's header actions (e.g. the Contacts KPI card + Add-Contact button) pin to the same right edge as the toolbar + table, instead of floating mid-header.
  • **.pip-* (PipelineBar) restyled** to match the §16.18 .lfb-* LifecycleBar: a thin 7px segment per stage (new .pip-seg) with the label below, accent fill + glow on the current stage — replacing the chunky filled-cell pill. Same data-state API; the per-stage metric now sits below the label.

v4.3.0 — 2026-06-06

The §16.15 Contacts-list surface, shipped (app-team R4). New CSS (operator): the .ct-* contacts-list chrome — .ct-add-contact (accent CTA), .ct-kpi-card (+ head / range / value / delta / meta), .ct-search, .ct-sync, .ct-pill, .ct-anon (+ sub), .ct-contact-meta, .ct-copy, .ct-segments (+ none), .ct-sessions-cell, .ct-list-foot-pages, .ct-head-stack, .list-screen.ct-list — plus the .tbl-check selection-column primitive. Promoted verbatim from the chapter so consumers compose the contacts list over the shipped stylesheet instead of copying CSS. Token-driven (dark-safe).


v4.2.0 — 2026-06-06

Lockstep with @magicblocksai/ui@4.2.0 — app-team additive primitives. New CSS: .ws-switcher-avatar-frame + .ws-switcher-badge (+ :empty) — the unread-badge affordance on the workspace-switcher avatar (a non-clipping frame plus an accent dot/count that mirrors .ws-nav-icon-badge). That's the only shared-stylesheet change; the new <SystemThemeIcon> and the change_stage / change_segment action types are TSX / registry-only and ship no CSS.


v4.1.2 — 2026-06-05

Keyboard-accessibility pass + a .badge fix; lockstep with @magicblocksai/ui@4.1.2. New CSS: .data-table-resize-grip:focus-visible + .data-table-th.is-grabbed (DataTable keyboard resize/reorder affordances) and .checklist-item:focus-visible + .checklist-item.is-grabbed (Checklist keyboard reorder). Fix: .badge now sets white-space: nowrap, so multi-word status pills ("Meeting booked") never wrap in narrow table cells.


v4.1.1 — 2026-06-05

Lockstep with @magicblocksai/ui@4.1.1 — contact-page polish. Thinner data-viz strokes, kit-wide: --spark-stroke 1.2→0.9px (light) / 1.5→1.2px (dark), and the <ScoreRing> donut --sr-stroke (lg 5→4px, md 4→3.5px, sm 3→2.5px, xl 6→5px) — so every Sparkline and ScoreRing in the kit reads lighter. Also sizes the <ContactDetailPage> AI-summary card icon (.cd-scard h4 svg, previously unconstrained and ballooning).


v4.1.0 — 2026-06-04

Lockstep with @magicblocksai/ui@4.1.0 — the AI-native contact page. New CSS in the operator surface: the contact-page primitive families (.lifecycle-bar, .quiet-hours, .memory-list, .key-fact-grid, .engagement-score, .session-card, .channel-chip, .trigger-agent) plus the <ContactDetailPage> page chrome (.cd-*), migrated out of chapter-private styles into the shipped stylesheet. Token-driven, dark-mode-safe, prefers-reduced-motion honoured, mobile-responsive (verified in a browser at a 390px phone viewport). Also a general kit-wide mobile fix: .section-title now wraps (flex-wrap) instead of overflowing the viewport on narrow screens.


v4.0.0 — 2026-06-04

Lockstep no-op with @magicblocksai/ui@4.0.0 — the explainability consolidation finale (name cleanup: CompactTrace removed, EvidenceQuoteSourcePassage alias). No CSS change.evidence-quote* ships on as a back-compat alias, and <Trace> renders the existing .compact-trace* / .trace-timeline* families.


v3.6.1 — 2026-06-04

Lockstep no-op with @magicblocksai/ui@3.6.1 — a ui-only fix restoring the <Trace> code missing from the 3.5.0 / 3.6.0 tags. No CSS change.


v3.6.0 — 2026-06-04

<JourneyGraph> edge hover. Additive. Lockstep with @magicblocksai/ui@3.6.0.

Added

  • .journey-graph-edge-hit + .journey-graph-edge-group[tabindex] hover / :focus-visible rules — the wide invisible hit-stroke + edge highlight behind JourneyGraphEdge.title (the hover-the-arrow affordance).

v3.5.0 — 2026-06-04

Lockstep no-op with @magicblocksai/ui@3.5.0 — the <Trace> unification (a rename of <CompactTrace><Trace> plus a new layout prop). <Trace> renders the existing .compact-trace* and .trace-timeline* class families. No CSS change.


v3.4.0 — 2026-06-04

Explainability consolidation, part 1. Additive. Lockstep with @magicblocksai/ui@3.4.0.

Added

  • **.source-passage*** (core) — the quoted source / evidence passage (figure + blockquote + mono cite line; <mark> highlight; six is-tone-*). Sits with .evidence-quote, the primitive it supersedes.
  • **.source-card*** (operator) — the load-on-open knowledge card: tone left-rule, label · value · meta head, expandable body with a reduced-motion loading shimmer and an error/retry affordance; per-tone --trace-tone vars set on the card so it reads standalone. Supersedes .trace-item-card.

Changed

  • .compact-trace width follows openwidth: fit-content (a discrete resting pill) by default; .compact-trace.is-open { width: 100% } fills the container when expanded. Pass an explicit width to override.

v3.3.1 — 2026-06-04

<CompactTrace> reads well at any width. Additive refinement. Lockstep with @magicblocksai/ui@3.3.1.

Changed

  • .compact-trace-step-detail is now a responsive grid (repeat(auto-fill, minmax(240px, 1fr))) — knowledge / detail cards flow into multiple columns as the panel widens (one column in a chat panel, two–three in a wide pane); bullets / code / custom children span the full width. The shell was already container-fluid; this lets the dense inner content reflow instead of stretching into long single-column rows.

v3.3.0 — 2026-06-04

.compact-trace — condensed reasoning trace. Additive. Lockstep with @magicblocksai/ui@3.3.0.

Added

  • **.compact-trace*** (operator) — the nested-disclosure trace family: -top / -phase / -step / -detail / -bullet / -code, tone dots (reusing the timeline's --trace-tone hexes), chevron, and compact (default) + .is-comfortable density. Built on semantic surface tokens so dark-mode is inherited; includes prefers-reduced-motion + a dark status-text contrast override. Backs <CompactTrace> in @magicblocksai/ui@3.3.0.

v3.2.0 — 2026-06-03

WorkspaceShell adoption — CSS bits. Additive. Lockstep with @magicblocksai/ui@3.2.0. App-team Round 20.

Added

  • .ws-nav-icon-badge (operator) — the notification dot / count on a rail item (top-right of the glyph; :empty = bare dot). Backs <WorkspaceNavIcon badge>.

Changed

  • .app-shell-main gains position: relative — a positioning context for the rail-only nav-progress sliver. No visual change.

Token bridge (theme.css)

  • Added --color-bg-canvas: var(--bg-canvas) so the bg-bg-canvas Tailwind utility resolves like its --color-bg-* siblings (the lone missing bridge from the v3.0.0 canvas token — NextGen's trivial consistency ask).

Brand mark (.logo)

  • .logo-wordmark now lays out an inline SVG (the real "magicblocks" logotype) instead of text — display: inline-flex + new .logo-wordmark svg { height: 1.4em } sizes the logotype to the glyph's height. The wordmark inherits currentColor, so .logo / .logo-dark drive light/dark exactly as before. (Paired with the <Logomark> / <Logo> artwork correction in @magicblocksai/ui@3.2.0.)

v3.1.1 — 2026-06-03

Lockstep no-op with @magicblocksai/ui@3.1.1 (a docs patch + brand-kit-site polish — redirect 301s + comment tidy). No CSS change.


v3.1.0 — 2026-06-03

NextGen WorkspaceShell CSS. Additive — the new operator top bar + the inner-section frame/header. Lockstep with @magicblocksai/ui@3.1.0. App-team Round 19.

Added

  • .ws-bar family (operator) — the optional compact operator top bar (.ws-bar · .ws-bar-title · .ws-bar-mid · .ws-bar-actions). Backs <WorkspaceBar>.
  • .section-view family (operator) — the inner-section frame (.section-view · -head · -body · -foot; [data-width="read"] reading cap). Backs <SectionView>.
  • .section-header family (operator) — the canonical section header (.section-header · -main · -back · -text · -eyebrow · -title · -subtitle · -actions · -tabs). Backs <SectionHeader>.

The existing rail chrome (.ws-switcher, .ws-nav*, .ws-user, .app-shell-side-*) already shipped; v3.1.0 ships the React components that compose it. No :root token or existing-selector change → the marketing website + Spark are unaffected.


v3.0.0 — 2026-06-03

Warm surface token ladder. Splits --bg's historical double-duty (page *canvas* vs. inset *well*) into two tokens and shifts the whole light-mode warm ladder one step lighter. App-team Round 18; root cause of the agent-builder "pane reads heavy" / white-card jobs reported in Round 17. Lockstep with @magicblocksai/ui@3.0.0.

> BREAKING — light-mode page background changes. body and every AppShell main column move from --warm-3 (#F4ECE4) to the lighter --warm-1 (#FBFAF7) canvas. Any CSS consumer that inherits the kit's body { background }including the marketing website — will see its page canvas lighten. This is intentional (the calm warm canvas is now the default), but because it changes a default it is a major. Dark mode is unchanged.

Added

  • --bg-canvas (tokens) — the page / app-shell-main / builder canvas token. var(--warm-1) in light; aliases back to the dark page bg (--ink) in dark, so dark surfaces are untouched.

Changed (light-mode :root token values)

  • --bg: --warm-3--warm-2 (#F6F4ED). The general bg + inset/well workhorse (.input, .panel-head, .card-sunk, menu hovers, the builder pane, ~25 components) now reads one step lighter — still correctly *darker* than the white cards it sits behind.
  • --bg-sunk / --bg-sunken: --warm-5--warm-3 (#F4ECE4). Deepest inset only.
  • --bg-deep: --warm-7--warm-5 (#E4DCD0). Rare true-deep, shifted up in proportion.
  • --bg-warm (--warm-2) and --bg-paper (--paper) unchanged.

Changed (wiring)

  • body background → var(--bg-canvas) (was var(--bg)).
  • .app-shell-main default → var(--app-page-bg, var(--bg-canvas)) (was …--bg-paper). The "cards float on a recessed warm canvas" pattern is now the default; pageWash is kept for API stability but resolves to the same --bg-canvas (a no-op in light). A dark override keeps the dark default at --bg-paper, and dark pageWash still flips to --inkdark behaviour is identical to v2.x.
  • .ab-builder canvas → var(--bg-canvas); the recessed pane (.ab-pane) stays var(--bg) = --warm-2, so the builder now reads as a calm canvas (warm-1) with a recessed pane (warm-2) and white floating cards — the surface-mix that prompted the request is gone.

Migration

  • Most consumers: no code change. Bump the dependency; the page canvas lightens (--warm-3--warm-1). If you *want* the old darker page, set body { background: var(--warm-3); } (or --bg-canvas: var(--warm-3)) in your app.
  • Marketing website: the page canvas lightens to --warm-1. No markup change required — apply the version bump and confirm hero/section bands still read as intended (those use their own --warm-* / *-soft tints, unaffected).
  • AppShell consumers who set pageWash explicitly (e.g. Next Gen apps/core): you can drop the prop — the canvas is now the default. Leaving it set is harmless (no-op in light).
  • Inset-heavy custom CSS that hard-coded --warm-3 as a "well" colour: switch to var(--bg-sunk) (now --warm-3) to track the ladder.

v2.4.0 — 2026-06-03

Agent-builder shell polish. Lockstep with @magicblocksai/ui@2.4.0. All changes are under .ab-* operator selectors — no :root token / body change; the website is unaffected.

Added

  • .ab-builder-head[data-density="compact"] (operator): tighter padding + smaller .ab-builder-head-back / .ab-h-pill for the ~44px compact topbar. Backs <AgentBuilderHead density="compact">.

Changed

  • .ab-job resting backgroundtransparent (hairline outline); --bg-paper lift moved to :hover + .is-dragging. On-pane harmonisation.
  • .ab-rail-group[data-group="journey"] .ab-rail-group-body — accent-wash radial gradient (and its dark override) removed; only the padding remains.
  • .ab-rail-block-name — explicit text-align: left.

Surface manifest regenerated (4726 entries); partition stays lossless.

v2.3.0 — 2026-06-01

Lockstep with @magicblocksai/ui@2.3.0.

Added

  • .action-opt option rows (operator surface): .action-opt (flex row), .action-opt-glyph (28px tinted icon chip), .action-opt-text / .action-opt-name / .action-opt-desc (dim --fg-soft, ellipsised). Backs <ActionPicker layout="dropdown">'s <Combobox> option rows (icon · name · one-line description).

Surface manifest regenerated (4723 entries); partition stays lossless.

v2.2.0 — 2026-06-01

Lockstep with @magicblocksai/ui@2.2.0.

Added

  • .action-row secondary line (operator surface): .action-row.has-meta (top-aligns the chip), .action-row-text (name + meta column), .action-row-meta (dim --fg-soft, single-line ellipsis). Backs <ActionList>'s new per-item meta.

Surface manifest regenerated (4717 entries); partition stays lossless.

v2.1.1 — 2026-06-01

Lockstep with @magicblocksai/ui@2.1.1.

Changed

  • .chat-composer.is-disabled .chat-composer-helper — helper text lifts to var(--fg-soft) when the composer is disabled (was the standard faint helper colour) so the disabled-state message stays legible beside the dimmed input row.

(The matching <TimeSeriesChart> SSR fix is JS-only in @magicblocksai/ui@2.1.1 — no CSS change.) Surface manifest regenerated (4713 entries); partition stays lossless.

v2.1.0 — 2026-06-01

Lockstep with @magicblocksai/ui@2.1.0.

Added

  • .dash-tile-scroll .dash-tile-body (operator surface) — max-height: var(--dash-tile-body-max-h); overflow-y: auto. Backs <DashboardTile maxHeight> (scrollable feed body; head + foot stay put).

Surface manifest regenerated (4712 entries); partition stays lossless.

v2.0.1 — 2026-06-01

Agent-builder bug fixes (app-team Round 12). Lockstep with @magicblocksai/ui@2.0.1.

Fixed

  • .chat-msg grid tracks 1frminmax(0, 1fr) (all four agent/user × normal/compact variants) so a bubble wraps at the container width instead of collapsing to its min-content in a narrow column.

Changed

  • .ab-rail-item .ab-rail-glyph svg + new .ab-rail-group-glyph svg rule sized to 16px (was 14px) — larger rail glyphs against the 13px labels.
  • .ab-pane reverted from the v1.72.0 warm gradient back to a flat var(--bg) fill (operator preference — flat surface backgrounds).

Surface manifest regenerated (4711 entries); partition stays lossless.

v2.0.0 — 2026-06-01

Breaking: the .app-shell-side sidebar now defaults to paper/white. Lockstep with @magicblocksai/ui@2.0.0.

Changed (breaking)

  • .app-shell-side — the light-mode warm-cream pin now applies only to .app-shell[data-sidebar-tone="warm"] .app-shell-side; with no tone attribute the sidebar uses the paper base (var(--app-shell-side-bg, var(--bg-paper))). A plain .app-shell sidebar is now white in light mode (was cream).
  • .dash-side (DashboardShell) — now defaults to the paper surface (var(--dash-side-bg, var(--bg-paper))) and follows the theme (white in light, navy in dark) instead of being pinned warm-cream in both. Uniform with AppShell.

Migration

  • Keep the v1.x cream AppShell rail: data-sidebar-tone="warm" (or <AppShell sidebarTone="warm">); override any colour with --app-shell-side-bg.
  • Keep the cream dashboard rail: set --dash-side-bg: var(--warm-3) on .dash (and re-apply the warm type-token rescope if you need ink-on-cream contrast tuning).

Surface manifest regenerated (4710 entries); partition stays lossless.

v1.72.0 — 2026-06-01

Agent builder polish + the AppShell paper-sidebar option. Lockstep with @magicblocksai/ui@1.72.0.

Added

  • sidebarTone="paper" support (core surface): .app-shell[data-sidebar-tone="paper"] .app-shell-side paints var(--app-shell-side-bg, var(--bg-paper)); the light-mode warm pin is now scoped to .app-shell:not([data-sidebar-tone="paper"]), and .app-shell-side reads --app-shell-side-bg so consumers can override either tone. Backs <AppShell sidebarTone>.

Changed

  • .ab-rail-block.is-active (operator): removed the box-shadow accent halo (kept the accent border + soft fill) — fixes the "funky glow" behind the active Journey block.
  • .ab-pane (operator): flat var(--bg) → a gentle top-down warm gradient (faint accent tint fading to flat), echoing the Journey group's wash.

Surface manifest regenerated (4710 entries); partition stays lossless.

v1.71.0 — 2026-06-01

Agent builder follow-up — Job cards reorder within the Jobs-to-Do tab. Lockstep with @magicblocksai/ui@1.71.0.

Added

  • .ab-job drag states (operator surface): .ab-job[draggable="true"] · .ab-job.is-dragging · .ab-job.is-drop-before / .is-drop-after · .ab-pane-body[data-dragging="true"] .ab-job — the same SortableList drag vocabulary the Journey blocks (.ab-rail-block) already use, so <JobCard>s wrapped in <SortableList> show drag + drop-indicator feedback. The ·· handle was already the affordance.

Surface manifest regenerated (4709 entries); partition stays lossless.

v1.70.0 — 2026-06-01

CSS for the agent builder shell family in @magicblocksai/ui@1.70.0 — ~100 ab-* rules promoted out of chapter 18.7's inline <style>. Lockstep.

Added

  • Agent builder shell (operator surface): .ab-builder (+ .has-resizer grid · --ab-rail-w) · .ab-rail-resizer · .ab-builder-head (.ab-version · .ab-unsaved · .ab-h-pill) · .ab-rail / .ab-rail-inner / .ab-rail-group / .ab-rail-item / .ab-rail-block (+ sortable drag states) / .ab-rail-add / .ab-rail-foot / .ab-mode-toggle · .ab-pane / .ab-pane-head / .ab-pane-tabs / .ab-pane-tab / .ab-pane-body / .ab-job / .ab-pane-foot. Backs the <AgentBuilderShell> family. Includes keyboard :focus-visible rings + a prefers-reduced-motion block; dark-mode-safe.

Surface manifest regenerated (4704 entries); partition stays lossless.

v1.69.0 — 2026-05-31

CSS for actions wizard config system Phase 3 in @magicblocksai/ui@1.69.0 — the recursive sub-action editor's nesting affordance. Lockstep.

Added

  • Sub-action nesting (operator surface): .action-button-item (wraps a button row + its sub-action editor) · .action-button-subaction (indented, hair-bordered nesting affordance) · .sub-action-field (restricted picker + recursive config stack) · .sub-action-config (the chosen sub-action's panel — warm grouped box). Backs <SubActionField> inside Add Buttons / Add Forms.

Surface manifest regenerated (4584 entries); partition stays lossless.


v1.68.0 — 2026-05-30

CSS for actions wizard config system Phase 2 in @magicblocksai/ui@1.68.0 — shared layout for the four new custom action panels. Lockstep.

Added

  • Phase-2 panel layout (operator surface): .action-config-stack (the vertical field stack shared by SwitchJourney / Buttons / Forms / HumanTakeover) · .action-config-section (grouped sub-cascade — the Switch Journey follow-up + the Human Takeover Slack block) · .action-button-rows / .action-button-row (the Add Buttons label repeater; the row's input flexes to fill beside the remove button).

Surface manifest regenerated (4580 entries); partition stays lossless.


v1.67.0 — 2026-05-30

CSS for the agent-builder actions wizard config system in @magicblocksai/ui@1.67.0 — the wizard shell + action picker + config-field families, promoted from chapter-18-inline. Lockstep.

Added

  • Actions wizard families (operator surface): .action-wizard (2-col shell) · .action-list* / .action-row* · .wizard-pane / -head / -body / -foot* · .wizard-steps / .wizard-step* (numbered indicator) · .action-picker / .action-tile* / .action-detail* (registry-driven type grid) · .action-config* + the .field-row / .field-label / .field-req / .field-help / .field-textarea config-field family · .message-field* (Select|Custom editor) · .calendar-config / .calendar-autofill* (the Add Calendar panel). Backs the chapter-18.12 actions components.

Surface manifest regenerated (4575 entries); partition stays lossless.


v1.66.0 — 2026-05-30

CSS for the agent-builder shell unify in @magicblocksai/ui@1.66.0 — the collapsed workspace icon-band, promoted from chapter-18.7-inline into the canonical <AppShell>. Lockstep.

Added

  • AppShell collapsed icon-band (operator surface) — keyed off .app-shell[data-sidebar-mode="collapsed"|"expanded"] .app-shell-side, so the base .app-shell-side (no sidebarMode) is unchanged. New families: .ws-nav / .ws-nav-icon (+ -label / -count) — the nav item, backs <WorkspaceNavIcon>; .ws-nav-section; .app-shell-side-head / -toggle / -spacer / -divider; .ws-switcher* (workspace switcher); .ws-user* (user menu); the slide-out .ws-nav-icon[data-tooltip]::after (+ dark-mode invert); and the .app-shell[data-sidebar-mode] width rules (56px / 220px) + a grid-track collapse transition that honours prefers-reduced-motion.

Surface manifest regenerated (4505 entries); partition stays lossless.

Added

  • .room-header — agent-builder room hero (eyebrow · display heading · lede). Backs <RoomHeader>.
  • .source-row family.source-row, .source-row-icon, .source-row-body, .source-row-name, .source-row-meta, .source-row-status, .source-row-trailing. Knowledge-source / tool row. Backs <SourceRow>.
  • .radio-card-list.is-grid + grid tile rules — N-column (--rcl-cols) radio-card grid with a :has(.radio-card-input:focus-visible) focus ring. Backs <RadioCardList layout="grid">.
  • .spark[data-tone="success|danger|info|warning"] — semantic sparkline line tones. Backs <Sparkline tone>.
  • .kpi-tile.is-clickable — hover/focus affordances for the link-tile drill-down. Backs <KpiTile href>.
  • .chat-transcript-empty.is-centered — vertically-centred empty state. Backs <ChatTranscript centerEmptyState>.

Changed

  • .chat-composer disabled — the opacity dim moved from the whole composer onto .chat-composer-row so .chat-composer-helper stays readable; pointer-events: none still on the composer.

Surface manifest regenerated; partition stays lossless.


v1.64.0 — 2026-05-29

Accessibility batch from the website team (BRAND_KIT_REQUESTS.md R033–R036). Lockstep with @magicblocksai/ui@1.64.0.

Added

  • --accent-text-strong (R034) — AA-safe accent text for small (<14 px) accent copy on light grounds: #B42463 (~5.4:1 on cream) vs. --accent pink (~3.1:1, fails AA). Surface-adaptive: aliases to bright --accent in dark mode and on ink surfaces (.section--ink, .ctab-ink, .ns-stage.dark, .stress-scoreboard).
  • --lk-accent-text (R036) — per-tone AA-safe partner to .leak-card's vivid --lk-accent, mapped to the *-text tokens (pink→--accent-text-strong, yellow→--warning-text, green→--success-text, blue→--info-text).

Changed

  • Repointed small-text accent selectors to --accent-text-strong (R034): .cta-eyebrow, .pg-eyebrow, .nf-eyebrow, .cp-panel-eyebrow, .mfg-eyebrow, .faq-eyebrow, .fc-card-deeplink, .feat-link, .media-tag. .leak-card .lk-cta--lk-accent-text (R036).
  • .mf-cols footer column-title styling moves from h5 to h2 (R035 — heading-order fix; visual styling unchanged).

v1.63.0 — 2026-05-26

CSS additions to back the per-section LEGO pieces in @magicblocksai/ui@1.63.0. All in the operator subpath surface.

.ag-card-channels family — agent-card channels-column primitive, promoted from chapter-18-inline. New selectors: .ag-card-channels, .ag-card-col-label, .ag-card-channels-row, .ag-channel-icon, .ag-channel-icon.is-on (+ the svg selector inside). Backs <AgentChannelStrip> + <AgentChannelIcon>.

.st-package family — radio-grid amount picker, promoted from chapter-25-inline. New selectors: .st-package-grid, .st-package, .st-package:hover, .st-package.is-selected (+ the responsive 3-column breakpoint at ≤880px). Backs <PackageGrid> + <PackageOption>.

Surface manifest regenerated; partition stays lossless.


v1.62.0 — 2026-05-26

No-op lockstep bump alongside @magicblocksai/ui@1.62.0 (page-shell LEGO — eight new TSX layout primitives that consume the already-shipped .list-screen-* and .sub-nav-* selectors). The CSS surface is unchanged from v1.61.0.


v1.61.0 — 2026-05-25

New CSS for @magicblocksai/ui@1.61.0 (app team Round 2):

  • .logo / .logomark family (core) — brand marks.
  • .dash-nav-group[data-mode="collapsed"] rules (operator) — icon-only sidebar rail layout; plus .app-shell-side width transition.
  • .tabs-rooms / .tabs-rooms-list / .tabs-rooms-trailing / .tabs-rooms-sticky / .tab-icon (core) — for <RoomTabs>.
  • .page-header-back (core) — back-link styling.
  • .kpi-tile[data-density="dense"] (core) — dense KpiTile variant.
  • .tabs.tabs-text (core) — text-only Tabs marker.
  • .card-ink (core) — dark editorial Card.
  • .icon-chip family (operator) — six tones.
  • .rate-card family (operator) — metric + tonal progress.

Lockstep with @magicblocksai/ui@1.61.0.


v1.60.0 — 2026-05-24

New CSS for @magicblocksai/ui@1.60.0 (app team Round 1):

  • .route-progress family (core surface) — sliding gradient + fade-out + reduced-motion fallback.
  • .tab.active / .tab[aria-current="page"] selectors added alongside the existing .tab.is-active (core), plus .tabs-link marker — enables URL-driven active state for <LinkTabs>.
  • .card-danger (core) — soft danger-zone surface.
  • .page-header-metadata (core) — horizontal metadata strip.
  • .sage-head .sage-row1-actions (operator) — flex group around the drawer's expand + close buttons.

Lockstep with @magicblocksai/ui@1.60.0.


v1.59.2 — 2026-05-23

  • Lockstep no-op bump with @magicblocksai/ui@1.59.2 (UI ESM build emits multi-file output for tree-shaking). No CSS changes.

v1.59.1 — 2026-05-22

  • Lockstep no-op bump with @magicblocksai/ui@1.59.1 (the UI package declared itself side-effect-free). No CSS changes.

v1.59.0 — 2026-05-22

  • Add .rich-text-content and .rte-pro-* rules (operator surface) for the <RichTextContent> renderer and the <RichTextEditorPro> editor shipped in @magicblocksai/ui@1.59.0.

Lockstep with @magicblocksai/ui@1.59.0.


v1.58.0 — 2026-05-22

  • R032 — re-tagged the section-surface family (.section--ink, .section--warm, .section-head, .section-head.is-stack, .section-title) from the non-emitted kit-site surface to core, so consumers importing core+marketing receive it. Added .faq-body ol/ul/li typography (previously only .faq-body p was styled).
  • R024.engine-sources/.engine-outputs gain align-self: stretch + justify-content: space-evenly so <EngineBlock> flow-dot origins line up with the source/output cards.
  • R031.hero-bloom-canvas::before changed from inset: 0 to inset: -8% so the hb-drift animation never exposes a hard canvas edge.
  • Build — added a surface-manifest snapshot gate: build.mjs emits a committed selector → surface manifest and fails on drift, making surface-assignment changes reviewable.

v1.57.0 — 2026-05-20

Per-surface CSS bundles — resolves R030 / Round W8.

Four new subpath exports split _shared.css by surface so consumers can ship only the CSS their surface uses:

  • @magicblocksai/css/core — shared component primitives (buttons, forms, cards, overlays, navigation, data display, feedback)
  • @magicblocksai/css/marketing — Marketing-surface components (content blocks, page templates, narrative systems, media, platform narrative)
  • @magicblocksai/css/operator — Operator-surface components (app shell, pipeline + CRM, AI surfaces, agent builder, conversation, explainability, designer toolkit, billing, workspace, integrations)
  • @magicblocksai/css/embed — Embed-surface components (chat widget + embed extras)

The existing @magicblocksai/css/tokens subpath (introduced in v1.17.0) is unchanged.

The monolith default (@magicblocksai/css) is byte-identical to importing all surfaces together. No existing consumer is affected by this release.

The split is implemented via /* @surface: NAME */ region markers in components/_shared.css. packages/css/build.mjs reads those markers directly and partitions the CSS at build time. A no-untagged-rule check fails the build if any CSS block is added outside a recognised surface region.

Gzipped bundle sizes

| Bundle | Subpath | gz size | |---|---|---| | tokens | @magicblocksai/css/tokens | 1,612 B | | core | @magicblocksai/css/core | 15,248 B | | marketing | @magicblocksai/css/marketing | 31,620 B | | operator | @magicblocksai/css/operator | 28,181 B | | embed | @magicblocksai/css/embed | 5,148 B | | magicblocks (monolith) | @magicblocksai/css | 82,201 B |

The website's tokens + core + marketing recipe totals 48,480 B gz (vs 82,201 B for the monolith — a 41 % reduction).

Note: per-component CSS (option (a) from the original R030 spec) is deferred to a future round. The surface-level split is the shipped approach.


v1.56.0 — 2026-05-20

Round D · Plan 2 — Visualisations CSS.

  • Add .journey-graph, .journey-node, .journey-graph-edge rule block (chapter 20.6).
  • Add .evaluations, .evaluations-summary, .evaluations-row rule block (chapter 15.10).
  • Add .reviewer-inbox, .reviewer-inbox-filter, .reviewer-inbox-row rule block (chapter 14.14).
  • Mobile breakpoints (≤480px) for visualisation collapse + reviewer inbox row reshape.

v1.55.0 — 2026-05-20

Round D · Plan 1 — Chapter 23 trio CSS.

  • Add .voice-player, .voice-player-* rule block (chapter 23.2).
  • Add .widget-persona-switcher, .widget-persona-card, .widget-persona-pill rule block (chapter 23.3).
  • Add .widget-channel-sandbox, .widget-channel-sandbox-tab, .widget-channel-sandbox-sms-bubble rule block (chapter 23.1).
  • Mobile breakpoints (≤480px) for tab strip rotation, card vertical-stack, touch-target floor.

[1.54.0] — 2026-05-19

Minor — Round C Plan 4: Integrations cluster CSS. Lockstep with @magicblocksai/ui@1.54.0. CSS for five new integrations primitives + chapter 26:

  • .integration-card family (4 status states: connected / disconnected / error / pending) + .integration-card-grid helper with 3→2→1 column reflow.
  • .integrations-grid family with optional category-filter chip row + ≤720px and ≤480px breakpoints.
  • .webhook-console family with mono-font tabular layout + 2xx/4xx/5xx status colouring + ≤480px 2-column reflow.
  • .oauth-callback-banner family with [data-intent] selector ladder (success / failure / pending) + ≤480px column-stack.
  • .integrations-page glue class for the page wrapper.

All blocks include mobile breakpoints where appropriate.


[1.53.0] — 2026-05-19

Minor — Round C Plan 3: Workspace cluster CSS. Lockstep with @magicblocksai/ui@1.53.0. CSS for six new workspace primitives + chapter 25:

  • .role-badge family (4 tint modifiers: error / accent / warning / neutral) + .role-badge-row helper.
  • .member-list + .member-row family with ≤480px 2-row grid reflow (avatar spans both rows, actions hidden on phone).
  • .invite-list + .invite-row family with ≤480px stack reflow.
  • .audit-log-stack + .audit-log-entry family with collapsible chevron (rotates on [aria-expanded="true"]) + before/after diff blocks tinted via --error-text / --success-text. Reduced-motion gate on the chevron transition.
  • .team-header-block family with mobile-stack reflow + count-pill chip.
  • .workspace-members-page glue class for the page wrapper.

All blocks include mobile breakpoints where appropriate.


[1.52.0] — 2026-05-19

Minor — Round C Plan 2: Billing cluster CSS. Lockstep with @magicblocksai/ui@1.52.0. CSS for six new billing primitives + chapter 24:

  • .plan-card family (three intensity variants + corner pill) with ≤480px column-stack reflow.
  • .usage-meter family (3-state colour ladder: accent / warning / error) with prefers-reduced-motion gate on the fill transition.
  • .invoice-row family (4 status pill states: paid / open / void / uncollectible) with ≤480px 2-column reflow.
  • .payment-method-card family with brand-glyph slot + ≤480px column-stack.
  • .billing-history-table family (filter chips + sort control) with ≤480px controls reflow.
  • .billing-page glue class for the page wrapper.

All blocks include mobile breakpoints where appropriate.


[1.51.0] — 2026-05-19

Minor — Round C Plan 1: Settings cluster CSS. Lockstep with @magicblocksai/ui@1.51.0. CSS for eight new settings primitives:

  • .settings-nav-rail family (rail + groups + items + count badge) with ≤720px horizontal-scroll reflow.
  • .settings-header-block family (text + actions) with ≤480px column stack.
  • .preference-toggle-row family.
  • .api-key-card family (head + token + meta + revoke) with ≤480px token-row stack.
  • .session-list + .session-row family with ≤480px grid reflow.
  • .danger-zone-block + .danger-zone-action family using --error-* tokens.
  • .unsaved-changes-bar family (portal-mounted sticky bottom bar) with ≤480px column stack + prefers-reduced-motion animation gate.
  • .settings-account-page glue rule.

All blocks include mobile breakpoints where appropriate.


[1.50.0] — 2026-05-18

Lockstep version bump alongside @magicblocksai/ui@1.50.0 (Round CS-2 — customer-success taxonomy unification). One CSS change: .inbox-row[data-state="done"] selectors added alongside the existing [data-state="completed"] ones. done is the new canonical "finished" state value (aligned with <Checklist>'s ChecklistItemState); completed is the deprecated alias and the kit normalises it to done at the React prop boundary, but the CSS keeps both selectors so consumers writing raw HTML with the pre-1.50 value still get the right treatment until v2.0.0.


[1.49.0] — 2026-05-18

Lockstep version bump alongside @magicblocksai/ui@1.49.0 (Round CS — customer-success taxonomy audit + fixes). No CSS rule changes this round; the audit ran against TSX components and doc surfaces. See the @magicblocksai/ui@1.49.0 entry for the full change list.


[1.48.0] — 2026-05-18

Lockstep version bump alongside @magicblocksai/ui@1.48.0 (Round W — the killer chat widget). New CSS rules for fourteen widget components — all namespaced under .widget-* selectors and the .widget-theme-scope wrapper so embedding into a customer's site stays clean. New tokens prefixed --w-* cover every visual lever (launcher / shell / messages user-side & ai-side / send / buttons primary-secondary-suggestion / feedback / composer / proactive / welcome disclaimer / legal disclaimer / debug surfaces / fonts / type system). Mobile-specific token overrides re-emit inside a @media (max-width: 540px) scope. No changes to existing selectors. No breaking changes.


[1.47.0] — 2026-05-18

Lockstep version bump alongside @magicblocksai/ui@1.47.0 (Round B3 closes Round B). New CSS rules for three independent shapes: .time-series-chart (multi-series chart w/ legend chips, gridlines, hover guide + tooltip, area fills, dashed lines), .structured-doc (sectioned editor w/ optional sticky TOC rail, numbered headings, autogrow textarea bodies), .live-chat-tester (composed Try-my-Agent shell w/ status-dot header + four status states). No changes to existing selectors. No breaking changes.


[1.46.0] — 2026-05-18

Lockstep version bump alongside @magicblocksai/ui@1.46.0 (TraceTimeline refinement). The v1.45.0 TraceTimeline CSS block is replaced with a refined version: calm neutral cards by default, confident 2px track (with brighten-on-hover transition), leading icons grown to 28px with a paper-cutout halo, hover affordance on event cards, section-break spacing rule, ghost-button Copy on code items, </> glyph pill on code-item leading slot, streaming pulse animation, live-indicator blink, and dark-mode tone saturation lifts per tone. No selector renames. No breaking changes for consumers using the package directly.


[1.45.0] — 2026-05-18

Lockstep version bump alongside @magicblocksai/ui@1.45.0 (Round B2 — the AI Reasoning Flow timeline). New CSS rules for .trace-timeline + .trace-event (and its 6 tone variants) + .trace-event-card + .trace-event-head / body / footer + three item shapes (.trace-item-bullet, .trace-item-card with expandable details, .trace-item-code with copy affordance). Dark-mode tone-saturation adjustments via body[data-theme="dark"] selectors. No changes to existing selectors. No breaking changes.


[1.44.0] — 2026-05-18

Lockstep version bump alongside @magicblocksai/ui@1.44.0 (Round B1 — the conversation surface of the MagicBlocks Next Gen platform-tour gap analysis). New CSS rules for five conversation primitives: .chat-msg (and its is-from-agent / is-from-user / is-from-system variants + reactions / actions / streaming / failed states), .chat-transcript (and skeleton + typing-dots + empty states), .chat-composer (and autogrow + spinner states), .message-trace-button (and dot-badge), .summary-banner (and accent / static variants). No changes to existing selectors. No breaking changes.


[1.43.0] — 2026-05-18

Lockstep version bump alongside @magicblocksai/ui@1.43.0 (Round A3 — cleanup batch of the MagicBlocks Next Gen platform-tour gap analysis). New CSS rules for ten product-app primitives: .multi-binding-list, .filter-popover, .time-picker, .saved-views-rail, .version-switcher, .help-bubble, .domain-list, .category-group-list, .expandable-edit-row, .source-freshness. No changes to existing selectors. No breaking changes.


[1.42.0] — 2026-05-18

Lockstep version bump alongside @magicblocksai/ui@1.42.0 (Round A2 of the MagicBlocks Next Gen platform-tour gap analysis). New CSS rules for nine product-app primitives: .qb / .qb-row (QueryBuilder), .snippet-textarea, .field-mapper, .variants-repeater, .radio-card-list / .radio-card, .two-path-chooser, .payload-preview, .test-connection, .kpi-delta-tile. No changes to existing selectors. No breaking changes.


[1.41.0] — 2026-05-18

Lockstep version bump alongside @magicblocksai/ui@1.41.0 (Round A1 of the MagicBlocks Next Gen platform-tour gap analysis). New CSS rules for six product-app primitives: .slider, .accordion / .accordion-group, .sync-status, .split-button, .duration-field, .kv-repeater. No changes to existing selectors. No breaking changes.


[1.40.0] — 2026-05-16

Lockstep version bump alongside @magicblocksai/ui@1.40.0 (narrative / long-form utilities harvested from "The Reliability Gap" research-report landing page). New CSS rules; no changes to existing selectors. No breaking changes.

Added

  • .reveal + .reveal.in + .reveal.d1.d5 — scroll-triggered fade/translate stagger utility. Default state is opacity: 0; transform: translateY(20px); .in flips to opacity: 1; transform: none over 700ms. Stagger classes add 80ms / 160ms / 240ms / 320ms / 400ms transition-delay. Honours prefers-reduced-motion (renders immediately, no animation).
  • .scroll-cue + .scroll-cue-line + scroll-cue-pulse keyframe — animated "scroll for more" indicator (mono uppercase label + pulsing vertical hairline). 2.4s ease-in-out loop. Reduced-motion-safe.
  • .dialogue-contrast + family (.dialogue-column, [data-tone="win"|"loss"], .dialogue-head, .dialogue-who, .dialogue-tag, .dialogue-status, .dialogue-msgs, .dialogue-msg, .is-us / .is-them, .dialogue-avatar, .dialogue-bubble, .dialogue-bubble-ellip, .dialogue-outcome with [data-tone="good"|"bad"], .dialogue-outcome-label) — two-column conversation comparison family. Collapses to 1 column below 720px.
  • .dot-matrix + .dot-matrix-cell (+ .is-highlight modifier) — proportion-viz grid. Driven by CSS custom properties on the root: --dm-cols (default 40), --dm-gap (default 3px), --dm-base (default var(--bg-warm)), --dm-highlight (default var(--accent)).

Notes

  • All new rules driven by existing tokens — dark-mode safe by inheritance.
  • Pair with @magicblocksai/ui@1.40.0.

[1.39.0] — 2026-05-15

Lockstep version bump alongside @magicblocksai/ui@1.39.0 (Wingman Round 1 — four new component primitives + Drawer bottom-sheet placement + ScoreRing xl). New CSS rules; no changes to existing selectors. No breaking changes.

Added

  • .score-ring--xl (W-008) — 140px ring + 8px stroke + 38px value font for hero-position scores.
  • .score-card + family (W-001) — .score-card-ring, .score-card-body, .score-card-eyebrow, .score-card-headline, .score-card-meta. data-fill="warm" and data-fill="ink" switch the surface fill; default is --bg-paper. Mobile breakpoint at 520px stacks ring above body for narrow viewports.
  • .narration-log + family (W-002) — .narration-log-item rows with [data-status="info" | "pass" | "warn" | "fail"] colour variants, .narration-log-item-glyph per-row glyph slot, .narration-log-toggle for the "show N earlier" expand button, data-anim="slide-fade" opt-in mount animation honouring prefers-reduced-motion.
  • .evidence-quote + family (W-003) — 3px accent left rail, italic body, mono uppercase caption with optional .evidence-quote-link "Open ↗" affordance. Token-driven (--accent, --bg-warm, --fg, --fg-dim, --accent-text).
  • .paste-recheck + family (W-004) — .paste-recheck-label, .paste-recheck-area, .paste-recheck-foot, .paste-recheck-status ([data-tone="success" | "error"] variants), .paste-recheck-delta (pop animation on mount, reduced-motion-safe).
  • .drawer-bottom (W-007) — bottom-anchored sheet placement; pins left: 0; right: 0; bottom: 0, full-width, max-height: 90dvh, top-rounded corners, dr-in-bottom slide-up keyframe (reduced-motion-safe).

Notes

  • All new rules driven by existing tokens — dark-mode safe by inheritance.
  • Pair with @magicblocksai/ui@1.39.0.

[1.38.0] — 2026-05-14

Lockstep version bump alongside @magicblocksai/ui@1.38.0 (Spark Round 39 — <ActivityTimeline hideFilters> prop). No CSS changes this round — the existing .act-filters rule still applies when the row is rendered. Bumped to keep the version pairing consumers reach for unchanged.


[1.37.0] — 2026-05-14

Lockstep version bump alongside @magicblocksai/ui@1.37.0 (Spark Round 39 — DataTable header shell uniformity, R37). One CSS rule body extended, one new modifier-class rule, no changes to existing selectors. No breaking changes.

Changed

  • .data-table-sort-ic (R37) — added min-width: 11px; display: inline-block; text-align: center so the caret slot reserves the same horizontal space on every header. Right-aligned labels now line up across the row even when some columns are sortable and some aren't (pre-1.37.0 non-sortable right-aligned headers omitted the slot and sat flush with the right edge, shifting the label by 11px relative to sortable neighbours).

Added

  • .data-table-sort.is-static (R37) — neutralises the hover recolour and pointer cursor on non-sortable header buttons (which the TSX now renders in the same shell as sortable headers for resize-grip hit-target uniformity).

Notes

  • Pair with @magicblocksai/ui@1.37.0.

[1.36.0] — 2026-05-14

Lockstep version bump alongside @magicblocksai/ui@1.36.0 (Spark Round R38 — <EmailThreadRow> primitive). One new CSS block, no changes to existing selectors. No breaking changes.

Added

  • .email-thread-row (R38) — Gmail-shaped row block sibling to .inbox-row. 5-column grid (28px 32px 1fr auto 28px), accent left-rail on .email-thread-row.is-unread via inset box-shadow, hover-discoverable .email-thread-row-archive button (opacity: 0 → 1 on :hover / :focus-within, always visible on (pointer: coarse)), pressed-state recolour on .email-thread-row-star[data-pressed="true"] (uses --warning). Composes inside .inbox for chrome-strip styling. All declarations driven by existing tokens (--accent, --bg-warm, --hair-soft, --fg-faint, etc.) — dark-mode safe.

Notes

  • Pair with @magicblocksai/ui@1.36.0.

[1.35.0] — 2026-05-13

Spark Round R-cmdk — scope the keyboard focus ring to opt-in classes + semantic-default focusables, instead of every bare form element. One CSS rule rewritten. No new selectors. No breaking changes for any consumer using the kit's .input / .btn / .icon-btn classes; does change behaviour for consumers who were relying on the kit auto-styling bare <input> / <button> / <textarea> / <select> (they'll now need to add the class or supply their own focus ring).

Changed

  • Focus ring scope (R-cmdk) — the global :focus-visible { box-shadow: var(--sh-focus) } and a, button, input, select, textarea, [role="button"], [tabindex] { :focus-visible … } rules near the top of the stylesheet are replaced by a single scoped rule covering a:focus-visible, [role="button"]:focus-visible, [tabindex]:focus-visible. Bare <input>, <button>, <textarea>, <select> no longer pick up the kit ring just by being focused — they need the kit's class (.input, .btn, .icon-btn, etc.), each of which still declares its own focus styling further down in the file. This unblocks chromeless surfaces like a command-palette search input where the kit's 3px pink halo was unavoidable.

Notes

  • All kit-internal demos use the opt-in classes; chapter HTML is unchanged.
  • .input:focus still paints the focus shadow (line 1629) — that's the on-class behaviour and is intentional, fires for both mouse and keyboard focus.
  • Pair with @magicblocksai/ui@1.35.0.

[1.34.0] — 2026-05-12

Lockstep version bump alongside @magicblocksai/ui@1.34.0 (Spark Round R36 — <DataTable tableKey> chrome auto-collapse). Two CSS rule changes; no new selectors. No breaking changes.

Changed

  • .data-table { position: relative } (R36) — added to the existing .data-table flex-column rule so the chrome overlay anchors against it. Pre-1.34.0 the wrapper had no positioning context.
  • .data-table-chrome (R36) — now position: absolute; top: var(--s-1); right: var(--s-1); z-index: 3; pointer-events: none; (was position: relative; margin-bottom: var(--s-2) flex-row band that took ~32px of dead vertical space when only the columns kebab populated it). New companion rule .data-table-chrome > * { pointer-events: auto } so the kebab + popover children are still interactive while the chrome wrapper stays click-through for the underlying header row.
  • .data-table-chrome z-index: 3 sits above the sticky header (z-index: 2) so the kebab isn't covered when the user scrolls.

Notes

  • The popover (.data-table-menu) anchor is preserved — position: absolute on the chrome still establishes a positioning context for absolute-positioned descendants.
  • No new selectors; no chapter HTML demos tableKey, so markup-equivalence is unaffected.
  • Pair with @magicblocksai/ui@1.34.0.

[1.33.0] — 2026-05-12

Lockstep version bump alongside @magicblocksai/ui@1.33.0 (Spark Rounds R33 + R34 + R35-B — six small additive props / glyphs from the Spark /admin/docs/* build-out). Three new CSS rules; everything else is TSX. No breaking changes.

Added

  • .section-card-head-text (R33-A) — column-flex wrapper around .section-card-title + .section-card-subtitle. Rendered by <SectionCard> only when subtitle is set; the historic flat markup (no wrapper) is preserved when subtitle is unset, so existing consumers see no DOM change.
  • .section-card-subtitle (R33-A) — small (12.5px), soft (var(--fg-soft)) subtitle line that sits one row below the title in the header band. Capped at 60ch so long copy wraps cleanly inside narrow cards.
  • .codeblk { position: relative } (R33-C) — added to the existing .codeblk rule so the new .codeblk-copy-corner overlay anchors inside the block. Pre-1.33.0 consumers wrapped CodeBlock in their own position: relative container; baking it onto the kit element lets the corner-positioned copy button anchor without consumer wrappers.
  • .codeblk-copy-corner (R33-C) — corner-positioned overlay copy button. Anchors top-right inside .codeblk via position: absolute; top: var(--s-2); right: var(--s-2). Sits above the <pre> text via z-index: 1. Backdrop is a tiny ink-tint + backdrop-filter: blur(4px) so the button reads cleanly against any code colour underneath. Mirrors the composition consumers were hand-rolling.
  • .data-table-grid[data-density="compact"] family (R35-B) — selector overrides for the new <DataTable density="compact"> mode. Halves horizontal cell padding from var(--s-4) (16px) → var(--s-2) (8px) on .data-table-cell, .data-table-th, and .data-table-sort (the inner sortable-header button). Vertical padding unchanged. Per-table; doesn't interact with the global body[data-density] mode.

Notes

  • All additions are opt-in. No selectors changed visible defaults; consumers see no behaviour change unless they pass the new props.
  • Pair with @magicblocksai/ui@1.33.0 for the new prop surfaces.

[1.32.0] — 2026-05-09

Lockstep version bump alongside @magicblocksai/ui@1.32.0 (Website Round W7 — five mobile-layout fixes across narrative components). All five fixes scoped to mobile breakpoints; desktop unchanged.

Fixed

  • .race-timeline mobile reorder (R025) — @media (max-width: 640px) block now uses CSS order to stack all .rt-left ticks under the bad heading, then all .rt-right ticks under the good heading. Replaces the pre-1.32.0 interleaved source-order rendering.
  • .cv flex-column anchor (R026) — .cv becomes display: flex; flex-direction: column; .cv-thread adds flex: 1 1 auto; min-height: 0. Layout is now deterministic inside any tall fixed-aspect parent (notably .device.phone .device-viewport).
  • .decay-curve mobile horizontal-scroll (R028) — @media (max-width: 640px) block adds overflow-x: auto + min-width: 480px on the SVG + right-edge mask cue. Axis labels stay legible while visitor swipes.
  • .engine-channel-strip family (R029) — new CSS family for the mobile-only HTML strip the EngineBlock React component renders. display: none on desktop (the SVG orbit owns the channels); display: flex at ≤720px with caption + chip pills.

Added

  • .kit-hero recipe utility class (R027) — wrap consumer hero markup in <section class="kit-hero"> with .kit-hero-text, .kit-hero-visual, .kit-hero-proof slot classes; recipe handles desktop side-by-side + mobile reflow (text → visual capped 360px centred → proof). Prevents stat-overlap-on-visual bugs at narrow viewports.

Notes

  • All five rules are mobile-scoped (or, for .kit-hero + .engine-channel-strip, additive). No breaking changes.
  • Pair with @magicblocksai/ui@1.32.0 for the EngineBlock TSX update.

[1.31.0] — 2026-05-09

Lockstep version bump alongside @magicblocksai/ui@1.31.0 (Spark Round 30 — R31 truncate + R32 user-customisable column layout).

Added

  • .data-table-cell.is-no-truncate (R31) — disables text-overflow: ellipsis and overflow: hidden for chip / pill / badge cells while preserving white-space: nowrap. Wired to the truncate: false flag on DataTableColumn.
  • .data-table-chrome + .data-table-menu-trigger + .data-table-menu family (R32) — kebab menu chrome above the grid for the user-facing column show/hide affordance. .data-table-menu uses the kit's existing --sh-2, --sh-focus, animation tokens; popover positions via position: absolute against the chrome bar.
  • .data-table-resize-grip (R32) — 8px-wide invisible-by-default hit target on the right edge of each header cell with a 2px tinted bar shown on hover via ::after. Cursor col-resize on the grip itself; the React component locks cursor: col-resize on body while dragging.
  • .data-table-th.is-reorderable + drag-state modifiers (.is-dragging, .is-drop-before, .is-drop-after) (R32) — visual feedback for drag-to-reorder. Drop indicator is a 2px var(--accent) bar at the target cell's leading or trailing edge.

Notes

  • All R32 chrome is gated on the React <DataTable tableKey> prop — vanilla CSS-class consumers see no behaviour change unless they author their own <DataTable>-shaped markup.
  • No breaking changes.
  • Pair with @magicblocksai/ui@1.31.0.

[1.30.0] — 2026-05-09

Lockstep version bump alongside @magicblocksai/ui@1.30.0 (Spark Round 29 — R30 chrome-strip selector lift).

Fixed

  • .section-card-body .inbox / .checklist / .calendar / .panel chrome-strip rule lifted from direct-child (>) to descendant. Consumers wrapping each list in a bucket div for header interleaving (e.g. a "LAST WEEK" <div class="spark-bucket-head"> between two <Inbox> groups) saw the wrapped inbox inherit the full card chrome — doubled up against the outer SectionCard. Same root-cause shape as R29 (DataTable), one step further. .data-table stays at > because DataTable's chrome lives on a known inner element (.data-table-grid).

Notes

  • No breaking changes. Descendant selector is strictly more permissive — every direct-child case still resolves; wrapped-grandchild cases now also resolve.
  • Pair with @magicblocksai/ui@1.30.0.

[1.29.0] — 2026-05-09

Lockstep version bump alongside @magicblocksai/ui@1.29.0 (Spark Round 28 — DataTable chrome-strip fix).

Fixed

  • .section-card-body > .data-table > .data-table-grid chrome stripped (R29). Sibling rule to the existing v1.9.3 R9-1 family. Pre-1.29.0 .data-table was correctly listed in the chrome-strip rule but the chrome lives on the inner .data-table-grid, so the rule was a no-op for DataTable specifically — every consumer composing <DataTable> inside <SectionCard padded={false}> saw a doubled-up frame.

Notes

  • No breaking changes. Standalone DataTable use (page-top-level inside a regular wrapper) keeps its full card chrome.
  • Pair with @magicblocksai/ui@1.29.0.

[1.28.0] — 2026-05-09

Lockstep version bump alongside @magicblocksai/ui@1.28.0 (Website Round W6 — three-item CSS polish batch).

Fixed

  • .section.is-warm / .section--warm rescope (R021) — extended to include --bg-paper and --hair-soft so kit components inside warm sections render correctly in dark mode. Previously only the foreground tokens were rescoped; --bg-paper fell through to the body-level dark-mode value (elevated ink) and produced dark blobs on cream surfaces.
  • .triptych margin-inline: auto (R022) — missed from the v1.25.0 W5-R018 narrative-component centring batch.
  • .ecosystem-rings .er-ring + .integration-hub .spokes line stroke (R023) — stroke-width: 11.6 and stroke colour shifted from --hair-tinted to ink-tinted (22% / 45% / 55% mixes) for clearer reads on cream marketing surfaces.

Notes

  • All three are CSS-only adjustments to existing rules. No breaking changes.
  • Pair with @magicblocksai/ui@1.28.0.

[1.27.0] — 2026-05-09

Lockstep version bump alongside @magicblocksai/ui@1.27.0 (Spark Round 27 — five-item batch).

Added

  • .input-label:has(> input, > select, > textarea, > .input-group, > .date-picker, > .combobox, > .multiselect, > .date-range-picker) + .input-label.is-stack modifier (R19) — flex column + 6px gap when .input-label wraps a kit form control as a child. The :has() selector covers the kit's own composite controls; .is-stack is the opt-in for consumer-defined controls.
  • .page-header-title-text .inline-headline + .page-header-title-text .inline-headline-text rules (R26) — width: auto; max-width: 100% on the headline wrapper plus white-space: nowrap; overflow: hidden; text-overflow: ellipsis; word-break: normal on the text node, scoped to <PageHeader>'s title slot only. Standalone <InlineHeadline> keeps its word-break: break-word for long-word safety.

Changed

  • .inbox-row grid template (R25) — base extended from 36px 1fr auto auto (4 tracks) → 36px 1fr auto auto auto (5 tracks). Selectable variant from 5 → 6 tracks. Trailing unused auto collapse to 0 width.
  • .sage-input max-height (R28) — now reads from --sage-input-max-rows custom property: max-height: calc(var(--sage-input-max-rows, 9) * 1.45em + 20px). Fallback 9 preserves pre-1.27.0 behaviour.

Notes

  • All four CSS changes are additive or strictly more permissive — no breaking changes for existing consumers.
  • Pair with @magicblocksai/ui@1.27.0.

[1.26.0] — 2026-05-08

Lockstep version bump alongside @magicblocksai/ui@1.26.0 (Spark Round 26 — <Inbox striped> prop + last-row cosmetic fix).

Fixed

  • Last-row hairline bleed through <SectionCard> rounded corners. Drops border-bottom on the last child of .section-card-body > .inbox, .section-card-body > .checklist > li, and .section-card-body .data-table-row — the row's own hairline was clipping through the card's rounded bottom corners as a stray sub-pixel arc.

Notes

  • No breaking changes. The deprecation of <Inbox banded> lives on the React side; the CSS class .inbox.inbox-banded is unchanged.
  • Pair with @magicblocksai/ui@1.26.0.

[1.25.0] — 2026-05-08

Lockstep version bump alongside @magicblocksai/ui@1.25.0 (Website Round W5 — narrative-component layout batch).

Changed

  • Every dark-mode selector now reads :is(html, body)[data-theme="dark"] instead of body[data-theme="dark"] (W5-R014). 46 rules updated en masse via the :is() pattern. Same specificity (0,1,1) as the original — no cascade reshuffle. Consumers' no-flash scripts can now set data-theme on <html> only and have all kit dark-mode rules fire correctly.
  • Narrative components centre by default (W5-R018). .scoreboard, .engine-block, .decay-curve, .happa-arc, .handoff-card all now ship margin-inline: auto alongside their existing max-width constraints. Aligns the family with the already-centred siblings (.ecosystem-rings, .integration-hub, .guardian-shield).

Added

  • .hero-bloom-canvas.is-bleed (W5-R015) — drops the canvas's default border-radius: var(--r-lg) for full-bleed marketing-page heroes.
  • Inline ⚠️ comment block above the .hbc-content rule documenting the fixed-aspect-frame constraint and pointing at HeroBloomCanvas.md (W5-R012).

Notes

  • No breaking changes. The dark-mode selector swap is strictly more permissive; the narrative-component centring change brings the family in line with siblings that already centred. The bleed modifier is opt-in.
  • Tokens-only subpath: 16064 → 16865 bytes raw, 4982 → 4992 minified — marginal increase from the wider dark-token selector.
  • Pair with @magicblocksai/ui@1.25.0.

[1.24.0] — 2026-05-08

Lockstep version bump alongside @magicblocksai/ui@1.24.0 (Website Round W4 — marketing-page chrome batch).

Added

  • .btn-ink (W4-R019) — ink-filled CTA variant. background: var(--ink); color: var(--paper). Hover mixes 8% accent into ink; active mixes 14%. Dark-mode auto-inverts to paper-on-ink-page so the CTA still reads as primary on dark surfaces.
  • .press-strip.is-bare (W4-R020) — chrome-free variant for marketing trust strips. Strips background, border, border-radius, and padding from the default .press-strip card chrome.
  • .section.is-ink + .section.is-warm (W4-R017) — surface-tone modifiers. Rescope --bg, --fg, --fg-soft, --fg-dim, --fg-faint, --hair, --hair-soft per the kit's existing .scoreboard.dark token-rescope idiom. BEM-style .section--ink / .section--warm aliases also accepted.
  • .section-head.is-stack (W4-R016/R013) — centred editorial variant of the dashboard .section-head row. Block layout, margin: 0 auto children, 64ch lede cap.
  • Inline ⚠️ reserved-class comments above the existing global .section-head rule pointing at packages/ui/docs/ReservedClassNames.md (matches the v1.19.0 .hero pattern).

Notes

  • No breaking changes. Every new rule is opt-in via a modifier class.
  • All four rules live above /* @TOKENS-END */ for the surface-mode tokens? No — the surface modifiers are component-scope rescopes, not global token additions, so they live below the sentinel and don't affect the tokens.css subpath.
  • Pair with @magicblocksai/ui@1.24.0.

[1.23.0] — 2026-05-08

Lockstep version bump alongside @magicblocksai/ui@1.23.0 (Spark Round 25 — R20 + R22 "DataTable + PageHeader composability" batch).

Added

  • .inline-headline CSS family (~70 lines) for the new <InlineHeadline> React primitive. Resting display matches .page-header-title typography (28px / 600 / display face); input mode swaps to a styled <input> with the same metrics so there's no visual jolt on click. Hover / focus / disabled / editing states all included. Below 640px the headline drops to 22px to match the <PageHeader> mobile reflow.

Notes

  • The <DataTable rowTarget> / rowRel props (R20) live entirely in the React component — no CSS changes for that fix.
  • Pair with @magicblocksai/ui@1.23.0.

[1.22.0] — 2026-05-08

Lockstep version bump alongside @magicblocksai/ui@1.22.0 (Spark Round 24 — R21-1 / R21-2 / R21-3 detail-page UX batch).

Added

  • .inbox-row.is-selectable { grid-template-columns: 22px 36px 1fr auto auto } at desktop (R21-1). The mobile equivalent (≤480px) already shipped in v1.13.0 — this is the missing desktop counterpart. Selectable rows now align cleanly with a leading 22px checkbox column ahead of the 36px avatar column.

Changed

  • .act-row .act-icon per-type rules (R21-3) now use background-image: linear-gradient(<tint>, <tint>) over an opaque background-color: var(--bg-paper) baseline. Pre-1.22.0 every per-type tint was alpha-channel rgba and the kit's vertical rail line bled through every chip. The change layers paper underneath the soft tint so the rail no longer shows.
  • note per-type tint retinted from warning-yellow (--warning-soft / --warning-text) to accent-soft / --accent-text. Notes are content, not alerts.
  • meeting per-type rule unchanged in palette (still color-mix(in oklab, var(--ink) 8%, transparent)) but now sits on the opaque paper baseline so the rail doesn't bleed through.

Notes

  • The CSS-class consumer surface for <SectionCard emptyState> (R21-2) is unchanged — that fix lives entirely in the React component's render-time gate, no CSS rule changes.
  • Pair with @magicblocksai/ui@1.22.0.

[1.21.0] — 2026-05-08

Lockstep version bump alongside @magicblocksai/ui@1.21.0 (Spark R19-1, P0 fix for <DataTable> priority-hidden columns leaving grid tracks behind).

Changed

  • .data-table-row grid-template-columns now reads from --data-table-cols-desktop (with 1fr fallback). The kit's React component sets three custom properties inline (--data-table-cols-desktop / -tablet / -mobile); CSS-class consumers who want the priority-aware behaviour set the same three variables on their .data-table-row elements.
  • Mobile media queries now switch grid-template-columns to var(--data-table-cols-tablet, …) at ≤720px and var(--data-table-cols-mobile, …) at ≤520px, with a fallback ladder all the way back to 1fr. The display: none rules on .data-table-cell[data-priority="tertiary"] / ["secondary"] stay (still useful for custom row layouts using the same convention) — the difference is that visible columns now share the full row width instead of leaving the hidden columns' tracks allocated.
  • Outer .data-table-grid rule clarified — it's a vertical stack of rows (grid-template-columns: 1fr); the column-track magic lives on .data-table-row only.

Notes

  • Visual change is scoped to ≤720px viewports — desktop renders identically to v1.20.0.
  • Pair with @magicblocksai/ui@1.21.0 for the React-side computation of the three grid templates from the column priority spec.

[1.20.0] — 2026-05-08

Lockstep version bump alongside @magicblocksai/ui@1.20.0 (Spark Round R24 — file-type icon set + <FileTypeIcon contentType> dispatcher). No source changes in this package — the new icons render via currentColor like every other kit icon and don't introduce new CSS rules. The _shared.css mirror is byte-identical to 1.19.0.

The companion @magicblocksai/ui ships seven new file-type glyphs in icons.tsx plus <FileTypeIcon> / getFileTypeIcon(...) / getFileTypeKind(...). See the @magicblocksai/ui@1.20.0 CHANGELOG for the full surface.


[1.19.0] — 2026-05-07

Minor — Website Round W3 lockstep CSS bump. _shared.css mirror picks up two new tokens, one new component-rule family, an inline reserved-class comment, and a token-fallback on .btn-primary. Full close-out at WEBSITE-FIXES-LOG.md Round W3.

Added

  • --on-accent token (W3-R006) — colour for text PLACED ON --accent background. Resolves to var(--paper) (cream) in light mode; var(--paper, #F4ECE4) in dark. Distinct from --accent-text (which means "accent-coloured text on a NEUTRAL background"). Lives above the /* @TOKENS-END */ sentinel so it ships in the tokens.css subpath too.
  • --f-italic token (W3-R008) — aliases to var(--f-serif) (Fraunces). Pre-1.19.0 the kit's <em> rules referenced var(--f-italic) but the token itself was undefined; this round fixes the orphan reference. Lives above /* @TOKENS-END */.
  • .press-row.is-scroll CSS family (W3-R011) — opt-in horizontal-scroll variant for <PressStrip layout="scroll">. flex-wrap: nowrap + overflow-x: auto + soft right-edge mask-image fade. Below 640px falls back to wrapping (horizontal scroll on phones is a footgun). The .press-row a rule gains flex: 0 0 auto so logo links don't shrink to fit.

Changed

  • .btn-primary text colour uses var(--on-accent, var(--paper)) (with --paper fallback for back-compat). Visually identical to the prior var(--paper) rule for any consumer on the kit's own tokens; semantically aligned with the new on-accent recipe documented in Tokens.md.

Documentation (in-CSS)

  • Naming-warning comment block on --accent-text explains the trap (consumers reading the token as "text-on-accent" by analogy with Material / Radix conventions). Points at Tokens.md and the new --on-accent.
  • Reserved-class comment block above .hero points at the new packages/ui/docs/ReservedClassNames.md and explicitly warns against consumers naming their page sections class="hero".

Notes

  • No breaking changes. Every existing rule keeps its behaviour; new rules are opt-in (.is-scroll modifier on .press-row) or new tokens that no kit rule yet depends on except .btn-primary's new --on-accent with a --paper fallback.
  • Tokens-only subpath unchanged in shape — both new tokens live above /* @TOKENS-END */ and ship in tokens.css automatically.
  • Pair with @magicblocksai/ui@1.19.0.

[1.18.0] — 2026-05-07

Minor — Website Round W2 lockstep CSS bump. _shared.css mirror picks up two new component families for the website team's <IndustryBar> (W2-R001) and <CostCompare> (W2-R002). Full close-out at WEBSITE-FIXES-LOG.md Round W2.

Added

  • .industry-bar family (~95 lines) — slim full-width sub-nav strip. Two surface variants (.industry-bar warm default + .industry-bar.is-ink ink) + .is-compact modifier. Mono / uppercase / letter-spaced 0.06em label styling per spec. Hover lifts text to --accent with a 1px underline; .is-current link gets the same treatment plus a leading .industry-bar-dot. Below 768px the inner .industry-bar-track becomes horizontally scrollable with a mask-image right-edge fade. Reduced-motion drops the colour transition.
  • .cost-compare family (~70 lines) — pricing-page-hero comparison bars. Full-width .cost-compare-bar.is-high (muted) + ratio-width .cost-compare-bar.is-low (accent) with optional .cost-compare-gap mono caption between. The low-bar width comes from --cost-compare-ratio custom property (default 5%); set inline on the bar element to scale the visual proportion.

Notes

  • All new rules live below the /* @TOKENS-END */ sentinel so the tokens-only subpath (@magicblocksai/css/tokens) stays unchanged.
  • No existing rules touched.
  • Pair with @magicblocksai/ui@1.18.0 for the React components.

[1.17.0] — 2026-05-07

Minor — Website Round W1-R003 partial ship: new tokens subpath export. First round serving the magicblocks-website build team (in addition to the existing Spark CRM stream). Full close-out at WEBSITE-FIXES-LOG.md Round W1.

Added

  • @magicblocksai/css/tokens subpath export — design-token surface only (:root light defaults + body[data-theme="dark"] overrides). ~14 KB raw / ~4.9 KB minified vs the full bundle's 543 KB / 371 KB minified — meaningful win for consumers who only need the design language without the component layer (custom JSX with token-driven inline styles, pages that defer component imports to interaction-time, etc.).

``css @import "@magicblocksai/css/tokens"; /* …consumer's own stylesheet uses --ink, --accent, --warm-3, --s-*, etc. */ ``

  • **/* @TOKENS-END */ sentinel in _shared.css** — marks the boundary between the token surface (everything above) and the component CSS (everything below). The packages/css/build.mjs build slices through this sentinel so the tokens subpath stays in sync with the canonical source automatically. Moving or renaming the sentinel without updating the build script will fail the build (with a clear error pointing at the fix) — it can't silently drift.

Changed

  • @magicblocksai/css/tokens.css subpath now points at the new tokens-only file, not the full bundle. Pre-1.17.0 this subpath was a placeholder mapping (./tokens.css → ./dist/magicblocks.css) shipped as scaffolding for the eventual real split. Any consumer who was importing @magicblocksai/css/tokens.css and getting the whole bundle will now correctly get tokens-only. If you need the full bundle, import @magicblocksai/css (the default) or @magicblocksai/css/magicblocks.css (explicit). The ./base.css and ./components.css subpaths still point at the full bundle (also placeholders; will be split out in future rounds when there's clear consumer demand).

Notes

  • Per-chapter CSS exports (R003 full ask) deferred. PostCSS purge at the consumer's build gives strictly better tree-shaking than chapter-level exports could (granular at the rule level vs the chapter level). The tokens subpath is the genuinely useful intermediate split — beyond that, the recommendation is "use PostCSS purge."
  • Full bundle unchanged. The default @magicblocksai/css import still ships magicblocks.css (543 KB raw / 371 KB minified) — no consumer is forced to switch.
  • The _shared.css mirror grows by ~10 lines (the sentinel comment block + its rationale). No CSS rules added or removed.
  • Pair with @magicblocksai/ui@1.17.0 for the documentation shipped in the same round.

[1.16.0] — 2026-05-07

Lockstep with @magicblocksai/ui@1.16.0 (Spark Round 20 R20-1 — structured <SectionCard> empty states).

Added

  • New empty-state slot classes inside .section-card-empty: - .section-card-empty-content — flex-column wrapper with max-width: 44ch cap on copy and centered alignment. - .section-card-empty-icon — 40px circular chip tinted with --warm-3 (light) / --bg-sunk (dark). - .section-card-empty-title — 14px display-font headline. - .section-card-empty-description — 13px body-font supporting copy in --fg-dim. - .section-card-empty-action — flex row for CTA buttons / links with --s-3 gap.
  • Sizing is intentionally smaller than the chapter-7 .empty family (which is sized for full-page surfaces — 72px icon, 20px title) so an empty section card doesn't dominate its parent layout.

Notes

  • The bare-text path (children rendered directly inside .section-card-empty) is unchanged — single-line "No tasks attached."-style messages still render as before.
  • Dark-mode behaviour: the icon-chip swaps to --bg-sunk background + dimmed --fg colour mix (mirrors the .empty-ic rule).

[1.15.0] — 2026-05-06

Lockstep version bump alongside @magicblocksai/ui@1.15.0 (Spark Round 19 R19-2 — <SectionCard padded> prop with default-flip). No source changes in this package — the _shared.css mirror is byte-identical to 1.14.0. The .section-card-body.is-padded modifier already existed; the round only changes which default the React component emits.

CSS-class consumers see no behaviour change — <div class="section-card-body"> is still flush; <div class="section-card-body is-padded"> is still padded. Only @magicblocksai/ui consumers see the default flip on the React <SectionCard> component.


[1.14.0] — 2026-05-06

Minor — Spark Round 18 mobile audit. Lockstep with @magicblocksai/ui@1.14.0. Every mobile rule listed below lives in the mirrored _shared.css and ships in this package's dist/magicblocks.css + dist/magicblocks.min.css.

Added

  • .sortable-handle family (R18-1) — full CSS family for the new <SortableHandle> primitive: .sortable-handle, .sortable-handle-grip, .sortable-handle-actions, .sortable-handle-action, .sortable-handle-menu, .sortable-handle-menu-item. Default behaviour: opacity: 0 at rest; revealed by parent :hover / :focus-within on mouse (@media (pointer: fine)); opacity: 1 always on touch (@media (pointer: coarse)) with a 32px tap target. Modifiers: .is-always-visible (Linear-style permanent grip), .is-menu-open (keep visible while popover is open).
  • [data-priority] cell modifiers on .data-table-cell (R18-3)[data-priority="tertiary"] hides ≤720px; [data-priority="secondary"] hides ≤520px. Plus a mask-image: linear-gradient(...) scroll cue on .data-table-grid ≤720px so users see there's more content off-screen.
  • [data-mobile-layout] attribute on .kanban (R18-2) — controls mobile layout strategy. The default (no attribute, or "scroll") becomes a horizontal flex row with scroll-snap-type: x mandatory and flex: 0 0 var(--kanban-col-width, 280px) columns at ≤720px. [data-mobile-layout="stack"] opts into single-column vertical stack (back-compat for triage kanbans).

Changed

  • 44pt tap-target sweep (R18-4) — under @media (pointer: coarse), compact controls (.btn-sm, .btn-icon-sm, .modal-close, .drawer-close, .section-card-action, .section-card-count, .dropdown-menu-item, .menu-item, .inbox-row .ix-action, .demo-tabs button) bump to 44px / 36px tap targets via padding + negative-margin (visual size on mouse unchanged).
  • .modal mobile keyboard (R18-5).modal is now a flex column capped at calc(100dvh - var(--s-5) * 2); .modal-body scrolls independently with -webkit-overflow-scrolling: touch. Below 600px .modal-foot becomes position: sticky; bottom: 0; background: var(--bg-sunken).
  • .drawer-head shrink ≤600px (R18-6) — vertical padding shrinks; .drawer-title font-size drops to 18px.
  • **.page-header-* mobile reflow (R18-8)** — at ≤640px the text block (.page-header-text) takes flex-basis: 100%; .page-header-summary loses its max-width cap; .page-header-actions takes flex-basis: 100% and flex-wrap: wrap with flex: 1 1 auto on children.
  • .inbox-row mobile reflow ≤480px (R18-9) — grid collapses from 5-column to 36px 1fr with chips stacked below body. Selectable variant follows the same rule with a leading checkbox slot.

Notes

  • All mobile rules use @media (max-width: ...) or @media (pointer: coarse) — they don't fire on desktop and don't change desktop visuals.
  • dvh requires Safari 15.4+ / Chrome 108+ / Firefox 101+; older browsers fall back to vh semantics (no JS shim required).
  • The _shared.css mirror is now larger by ~140 lines (mostly the .sortable-handle family + the four mobile-rule blocks).

[1.13.4] — 2026-05-06

Lockstep version bump alongside @magicblocksai/ui@1.13.4 (docs-only correction round). No source changes in this package — the _shared.css mirror is byte-identical to 1.13.3.

The companion @magicblocksai/ui ships a documentation correction to the v1.13.3 close-out (Spark intentionally kept their local <LifecyclePill> shadow after the kit's LifecycleValue widening because its role evolved during the round — see the ui CHANGELOG for the full story).


[1.13.3] — 2026-05-06

Patch — chapter-11 narrative fixes (no token / class additions; rule rebalancing only).

Changed

  • .stress-scoreboard .ss-row span — replaced blanket .is-fail span { background: var(--error) } with per-dot [data-state="pass"] / [data-state="fail"] selectors. Both rows now show a green/red mix proportional to the actual completion stat instead of MagicBlocks-green vs single-prompt-red flat walls. Inline JS in chapter 11 sets s.dataset.state = i < solid ? "pass" : "fail" on each dot.

Added

  • .dormant-mine .dm-last-stack family — stacked-spans pattern + 14s dm-last-cold / dm-last-fresh keyframes for animating the "last touch" cell from the cold-state value to "0 days" when the row's pill transitions through Sent → Replied. Mirrors the existing .dm-pill-lbl shape — same --i per-row delay keeps both columns in lock-step.

Notes

  • Pair with @magicblocksai/ui@1.13.3.
  • Light + dark unchanged for existing tokens.

[1.13.2] — 2026-05-06

Patch — Spark Round 15 follow-ups (R15-4 + R15-5).

Added

  • .modal[data-size="sm" | "md" | "lg" | "xl"] selectors with viewport-min() width guards. Pre-1.13.2 the kit shipped only the bare .modal { max-width: 460px } rule, so <Modal size="…"> was a silent no-op. R15-4 — see ui CHANGELOG for the per-size widths.
  • .section-card-icon — leading icon cell in .section-card-head. Powers <SectionCard icon> from @magicblocksai/ui. R15-5.

Notes

  • Pair with @magicblocksai/ui@1.13.2.
  • The [data-size="md"] rule (560px) takes precedence over the bare .modal (460px) because of higher selector specificity, so default-size React modals jump from 460px → 560px on upgrade. Raw-HTML consumers using <div class="modal"> without a data-size attribute keep the 460px fallback.

[1.13.1] — 2026-05-06

Patch — Spark Round 15 R15-3.

Added

  • --f-sans token — alias of --f-body. The kit's canonical name stays --f-body (semantic — "the body face"), but --f-sans is what most consumers reach for first via muscle memory. Both names point at the same DM Sans stack so font-shorthand declarations using either resolve identically. Spark hit this assuming --f-sans existed and lost a few minutes to silent fallback.

Notes

  • Pair with @magicblocksai/ui@1.13.1 (which adds the matching font-sans Tailwind utility for both v3 + v4 consumers).
  • Single token addition — no breaking changes, no visual changes.
  • The full CSS-variables reference is at brand.magicblocks.ai/tokens#variables (filterable list of every custom property the kit declares).

[1.13.0] — 2026-05-05

Spark CRM Round 14 — CSS surface for two new primitives. Mirrors @magicblocksai/ui@1.13.0.

Added

  • .inbox-row .ix-select + .ix-select-input — leading checkbox column for bulk-selection. Custom appearance (16px square, 1.5px hair border, --accent filled-state with paper checkmark via ::after). Pairs with [data-inbox-row-select] data attr that the row's click handler reads to ignore checkbox clicks.
  • .inbox-row.is-selected + .inbox-row[data-selected="true"] — selected-row tint via color-mix(in oklab, var(--accent-soft) 60%, transparent). Survives the .inbox-banded alternation (selected wins).
  • .inline-date-field family — edit-in-place date field chrome. Classes: .inline-date-field, .inline-date-field-display (with [data-tone] for default / warning / error), .inline-date-field-label, .inline-date-field-placeholder, .inline-date-field-input, .inline-date-field-clear. Powers <InlineDateField>.

Notes

  • Pair with @magicblocksai/ui@1.13.0.
  • Light + dark unchanged for existing tokens.
  • Existing .inbox-row semantics ([data-state], :hover, [data-state="overdue"] left-rail accent) all continue to work alongside the new selection slot.

[1.12.0] — 2026-05-05

Spark CRM Round 13 — CSS surface for nine items shipped together. Mirrors @magicblocksai/ui@1.12.0.

Added

  • .multi-select family — chip-rendered multi-value selector chrome (~20 classes). Powers <MultiSelect>.
  • .page-header family — eyebrow / title (with optional icon slot) / summary / actions layout. Powers <PageHeader>. Mobile breakpoint at ≤640px drops title font-size + actions wrap full-width.
  • .mini-card-list + .mini-card-row + .mini-card + .mini-card-icon + .mini-card-body + .mini-card-meta — linked-entity row chrome for <SectionCard> body slots.
  • .agenda-list family + .agenda-row family + .date-band — time-grouped list chrome with date-band heading. Powers <AgendaList> / <AgendaRow> / <DateBand>.
  • Mobile drawer chrome on .app-shell[data-mobile-nav] — slide-in animation, scrim overlay, --app-shell-drawer-w token (default min(280px, 84vw)). Activates only at ≤960px.
  • .dash-nav-group[data-force-open] semantics — handled in TSX via the <DashboardNavGroup forceOpen> prop; no new CSS class.
  • .inbox.inbox-banded — opt-in row alternation for long inbox lists.
  • .inbox.inbox-actions-on-hover + .inbox-row[data-actions="hover"] — opt-back-into hover-only secondary-action visibility (legacy Gmail-style).
  • .av-tone-{accent,info,success,warning,error} — tint modifiers on the avatar shape.

Changed

  • .av default fillbackground: var(--bg-warm); color: var(--fg-soft). Pre-1.12.0 .av was background-less; initials disappeared on same-tone page washes. Image children clip via overflow: hidden so the new fill is invisible behind avatars rendered as <img>.
  • .inbox-row .ix-action rest state — now opacity: 0.7; color: var(--fg-faint). Was effectively invisible at rest. Hover lifts to opacity: 1.

Notes

  • Pair with @magicblocksai/ui@1.12.0.
  • Two visible defaults changed (.av fill + .ix-action rest opacity). Both opt-out via class.

[1.11.1] — 2026-05-05

Patch — Spark Round 12 R12-1.

Fixed

  • .act-divider + .act-row::before { top: 50% } — clips the activity-timeline rail at the icon centre on the first row of each day group. Pre-1.11.1 the top: 0 stub extended into the row's top padding and visually bridged across date dividers, making the rail look continuous past day boundaries.

Notes

  • Pair with @magicblocksai/ui@1.11.1.
  • Light + dark unchanged — pure layout fix.

[1.11.0] — 2026-05-05

Spark CRM Round 11 — CSS surface for the collapsible nav-group. Mirrors @magicblocksai/ui@1.11.0.

Added

  • .dash-nav-group<details>-based collapsible nav-group. Wraps a <summary class="dash-nav-label"> heading + .dash-nav-item children. Custom chevron via CSS-only ::after pseudo-element (rotates -45deg45deg on toggle, honours prefers-reduced-motion: reduce). Default <summary> disclosure marker hidden in every browser. Hover background, focus-visible ring (var(--sh-focus)). When collapsed, drops the heading's bottom margin so adjacent collapsed groups stack tightly.

Powers the typed <DashboardNavGroup> from @magicblocksai/ui. Works on a plain <details> element without any JS — useful for vanilla / SSR pages.

Notes

  • Pair with @magicblocksai/ui@1.11.0.
  • Light + dark unchanged for existing tokens.
  • Existing .dash-nav-label + .dash-nav-item { margin-top: 4px } rule continues to work when the label is inside a <details> (additional .dash-nav-group > summary.dash-nav-label + .dash-nav-item selector covers the new shape).

[1.10.0] — 2026-05-04

Spark CRM Round 10 — CSS surfaces for two new primitives. Mirrors @magicblocksai/ui@1.10.0.

Added

  • .mention-input + .mention-picker + .mention-picker-item (with .is-highlighted modifier) + .mention-picker-item-label + .mention-picker-item-sub — anchored-popover @-mention picker chrome. Powers <MentionInput>.
  • .email-thread family (~25 classes) — full email viewer chrome. Includes: - .email-thread, .email-thread-subject, .email-thread-messages, .email-thread-actions, .email-thread-composer — root structure - .email-message, .email-message-collapsed, .email-message-expanded — per-message variants - .email-message-collapsed-from, .email-message-collapsed-preview, .email-message-collapsed-time — collapsed row layout - .email-message-head, .email-message-head-body, .email-message-head-line, .email-message-head-meta, .email-message-head-side — expanded header layout - .email-message-from, .email-message-from-email, .email-message-to, .email-message-time — header text styles - .email-message-details-toggle, .email-message-collapse-button — chevron buttons - .email-message-details<dl> inside the details flyout - .email-message-tracking, .email-message-tracking-icon, .email-message-tracking-count — opens / clicks / replied icons - .email-message-body — sanitised-HTML container with default typography for <p>, <a>, <img>, <ul>, <ol> - .email-message-quoted, .email-message-quoted-toggle, .email-message-quoted-body — auto-collapsing quoted history - .email-message-attachments, .email-message-attachment, .email-message-attachment-icon, .email-message-attachment-name, .email-message-attachment-size — chip rail at the bottom of each body

Notes

  • Pair with @magicblocksai/ui@1.10.0.
  • Light + dark unchanged for existing tokens.

[1.9.3] — 2026-05-04

Patch — Spark Round 9. Two CSS-only fixes.

Fixed

  • R9-1: .section-card-body > .inbox, > .checklist, > .data-table, > .calendar, > .panel now strip their own border / border-radius / background / overflow / box-shadow when nested directly inside a section card. Prevents chrome doubling.
  • R9-2: .inbox-row[data-state="overdue"] swapped the red ::before dot at left: 0 (whose halo was clipped by the rounded .inbox border) for a Linear-style 3px inset left-rail accent. No layout shift; clipped cleanly at the panel's rounded corners.

Notes

  • Pair with @magicblocksai/ui@1.9.3.
  • Light + dark unchanged.

[1.9.2] — 2026-05-03

Patch — Spark Round 8 R8-1.

Fixed

  • .section-card-action .btn now overrides the default .btn 11×20px padding to a compact 5×12px so kit <Button> instances slotted as the section-card action don't push the header band taller than its text-only height.

Notes

  • Pair with @magicblocksai/ui@1.9.2.
  • Light + dark unchanged — pure layout fix.

[1.9.1] — 2026-05-03

Patch — Spark Round 7 R7-1.

Fixed

  • .section-card-hero now sets flex-direction: row to override the .section-card { flex-direction: column } inheritance. Pre-1.9.1 hero tiles rendered avatar stacked above the title instead of beside it.

Notes

  • Pair with @magicblocksai/ui@1.9.1.
  • Light + dark unchanged — pure layout fix.

[1.9.0] — 2026-05-03

Spark CRM Round 6 — surface hierarchy. Mirrors @magicblocksai/ui@1.9.0.

Added

  • --app-page-bg token (default var(--bg-paper)) drives .app-shell-main. Set on .app-shell[data-page-wash="true"] it flips to var(--warm-1) in light / var(--ink) in dark.
  • .section-card-hero family — clickable hero context tile sibling to .section-card. Classes: .section-card-hero (root, hover lift to --sh-2 + accent border + focus ring), .section-card-hero-body (text column), .section-card-hero-title, .section-card-hero-sub.
  • .card-flat modifier — opt out of the new default --sh-1 on .card.

Changed

  • .card now ships box-shadow: var(--sh-1) by default (the standard "card feels like a card" treatment). Marketing-grid contexts that depend on flat cards add .card-flat.

Notes

  • Pair with @magicblocksai/ui@1.9.0.
  • Light + dark unchanged for --bg-paper and other tokens; only the card shadow default + new --app-page-bg token are surface-affecting.

[1.8.0] — 2026-05-03

Spark CRM Round 5 — CSS surface for the round shipped here. Mirrors @magicblocksai/ui@1.8.0.

Added

  • Refined default scrollbars — slim 10px rounded thumbs, transparent track, theme-aware tint (color-mix(in oklab, var(--ink) 18%, transparent) in light, cream-tint in dark), hover-lift to 32% / 38% alpha. Applied via body:not([data-scrollbars="native"]) *::-webkit-scrollbar* and scrollbar-color on the body. Opt out: <body data-scrollbars="native"> restores the OS default.
  • .kanban[data-fill-height] — fill-height variant for <Kanban>. Kanban root becomes height: 100%, columns become height: 100% with min/max caps lifted.
  • --kanban-col-min-h / --kanban-col-max-h tokens — drive .kb-col's height caps. Defaults 360px / 520px (matching the chapter-14 demo). Override per-app for fit-to-container behaviour.
  • --app-topbar-h token — declared on .app-shell, default 56px. Lets consumers write calc(100vh - var(--app-topbar-h)) instead of magic numbers.
  • --app-shell-side-w — alias of --app-shell-side for naming consistency with --app-topbar-h.

Changed

  • .app-shell-side now paints background: var(--bg-paper) unconditionally (v1.7.0 scoped the bg to light mode only — dark-mode in-grid sidebars were fine but repositioned ones rendered transparent). The light-mode warm-cream override under body:not([data-theme="dark"]) still wins in light theme.

Fixed

  • .app-shell[data-scroll-mode="page"] .app-shell-side rules (position: sticky; max-height: 100vh) gated to @media (min-width: 961px) so they don't fight consumer-side mobile slide-in nav drawers (which need position: fixed). Pre-1.8.0 the sticky rule survived past the mobile display: none breakpoint, forcing !important overrides on position, max-height, width, and top.

Notes

  • Pair with @magicblocksai/ui@1.8.0.
  • Light + dark behaviour unchanged for everything except the new scrollbar default.

[1.7.1] — 2026-05-02

Patch — Spark Round 4 R4-1.

Fixed

  • .app-shell[data-scroll-mode="page"] .app-shell-main now keeps overflow-x: hidden (only releases overflow-y), and adds min-width: 0. v1.7.0 used overflow: visible on both axes, which let horizontally-scrolling children (e.g. .kanban[data-direction="horizontal"]) expand the main grid column past viewport width and drag the sticky topbar off-screen.

Notes

  • Pair with @magicblocksai/ui@1.7.1.
  • Light + dark unchanged — pure layout fix.

[1.7.0] — 2026-05-02

Spark CRM Round 3 — CSS surface for the round shipped here. Mirrors @magicblocksai/ui@1.7.0.

Added

  • .section-card family — new "warm-headed list card" primitive: .section-card, .section-card-head, .section-card-title, .section-card-count, .section-card-action, .section-card-body, .section-card-body.is-padded, .section-card-empty. Powers the typed <SectionCard> from @magicblocksai/ui.
  • .kanban[data-direction="horizontal"] — horizontal-scroll kanban variant for pipelines with many stages. Columns become flex: 0 0 var(--kanban-col-width, 280px). Default grid mode unchanged.
  • --kanban-col-count token — drives the grid-mode kanban column count. Default 4. Set to stages.length for any non-4-stage pipeline.
  • .app-shell[data-scroll-mode="page"] — opt-in whole-page scroll variant for <AppShell>. Sidebar becomes sticky-with-internal-overflow; main column drops overflow-y: auto; root drops height: 100vh; overflow: hidden.

Changed

  • Dark-mode --bg-warm lifted from #2A3050 to #323858 (~+5% over paper). Pre-1.7.0 it was an alias of paper, so any warm-tinted band (table headers, panel-head, list section heads) collapsed against its body in dark mode.
  • .app-shell-side warm-pin scope — the warm-cream sidebar background + ink token pins now apply only under body:not([data-theme="dark"]). In dark mode the sidebar falls through to the global semantic tokens so it goes dark with the rest of the app. Marketing chrome still gets the warm look.

Notes

  • Pair with @magicblocksai/ui@1.7.0.
  • Light mode unchanged.

[1.6.0] — 2026-05-02

Lockstep version bump alongside @magicblocksai/ui@1.6.0 (Tailwind v4 + React 19 + TypeScript 5.7 compatibility round). No source changes in this package — the _shared.css mirror is byte-identical to 1.5.2.

The companion @magicblocksai/ui ships a new ./theme.css export for Tailwind v4 consumers; this CSS package continues to be drop-in compatible with any stack (Tailwind v3, v4, none, Astro, 11ty, raw HTML, …) since it only ships finished CSS classes + custom properties.


[1.5.2] — 2026-05-02

Fixed — narrowed dark-mode elevation to overlays only

Follow-up to 1.5.1. The previous patch added a drop shadow + inset top highlight to too many surfaces, including content containers (.card, .panel, .data-table, .calendar, .checklist, .inbox). On detail pages every section ended up looking like a floating overlay.

  • box-shadow (inset top highlight + var(--sh-3)) now applied only to true overlays in dark mode: .modal, .drawer, .popover, .menu, .cmdk, .combobox-popover, .nav-chapters-panel
  • Content containers stay flat — they sit on the page and get separation from the lifted --bg-paper (#2A3050) + 1px hair border alone
  • Light mode unchanged; the lifted --bg-paper, hair-opacity bump, and shadow re-aliases from 1.5.1 all stay

Pair with @magicblocksai/ui@1.5.2.


[1.5.1] — 2026-05-02

Fixed — dark-mode elevation contrast

User feedback: modals, popovers, and list containers (inbox, leads, tickets) blended into the page in dark mode. Lifted surface tokens + added elevation cues:

  • --bg-paper #232842#2A3050 (~12% lift over ink, clearly elevated)
  • --bg-warm matched (alias)
  • --bg-sunk / --bg-sunken #141828#11152A, --bg-deep #0F1221#0B0E1B to keep the depth hierarchy
  • --hair 0.140.18 opacity so list-row separators are visible against lifted paper
  • --sh-1 through --sh-4 re-aliased in dark mode from rgba(25,30,50, …) (ink-colour, invisible against ink) to rgba(0,0,0, …) with deeper alpha — modals now sit on a visible drop shadow
  • Added inset top highlight (inset 0 1px 0 rgba(255,255,255,0.05)) to elevated chrome in dark mode: modal, drawer, popover, menu, cmdk, combobox-popover, nav-chapters-panel, inbox, checklist, data-table, calendar, card, panel

Light mode unaffected — all overrides scoped to body[data-theme="dark"].


[1.5.0] — 2026-05-02

Changed — <Inbox> row vertical rhythm

Spark CRM Round 2: live demo at /components/14-app-pipeline#inbox felt cramped. Switched the row from a fixed height: 52px to min-height + symmetric padding: 10px var(--s-4). Avatar 28→32, font 11→12. Title→sub gap 2→4px, sub-line line-height 1.2→1.3. Added body[data-density="compact"] override (padding: 6px var(--s-4)) so compact mode still hits the legacy ~36px target.

Comfortable rows now feel ~60px tall — closer to production CRM inboxes.

Notes

  • Pair with @magicblocksai/ui@1.5.0.
  • No new tokens added; pure layout tweak under .inbox-row and friends.

[1.4.0] — 2026-05

Spark CRM Round 1 follow-up. Mirrors @magicblocksai/ui@1.4.0.

Added

  • .app-shell family — root grid, .app-shell-side, .app-shell-main, .app-shell-topbar. New custom properties: --app-shell-side (default 248px), --z-topbar (default 30). Pair with <AppShell> from @magicblocksai/ui.
  • .card-narrow — opt-in 320px max-width modifier. Replaces the previous default behaviour of .card.
  • .dash-nav-item-label — wrap nav labels in this so they absorb the row's free space without growing icon / badge wrappers.
  • --dash-nav-item-py — custom property for tuning sidebar nav row vertical padding (default 7px).
  • .dash-nav-label + .dash-nav-item { margin-top: 4px } — small breathing room between group headings and the first row underneath.

Changed

  • .dash-nav-item padding shorthand split into per-axis: padding-top / padding-bottom use var(--dash-nav-item-py, 7px); left/right unchanged.

Removed

  • .card { max-width: 320px } — was making <Card> useless as a list container. Restore via .card-narrow if you needed the cap.
  • .dash-nav-item > span:first-child { flex: 1 } — too broad, ate icon and badge wrappers. Use .dash-nav-item-label on the label span instead.

Migration notes

  • Card max-width is gone by default. If you depended on it (most marketing pricing-grid demos do), add card-narrow to the affected nodes — or pass narrow to <Card> if using React.
  • Sidebar nav rows need a label class. Wrap label text in <span class="dash-nav-item-label">…</span> so it absorbs the row's free space. <DashboardShell>'s default markup already does this; chapter 10's demo does too.

[1.3.0] — 2026-05

Spark CRM Tier 2 + Tier 3 primitives. Mirrors @magicblocksai/ui@1.3.0.

Added

  • .combobox family (-trigger, -popover, -search, -list, -option, -empty, -loading)
  • .avatar-group + .avatar-group-more (with [data-size] variants matching the existing <Avatar> sizes)
  • .dropzone family (+ .is-dragging, .is-disabled, .dropzone-input, .dropzone-headline, .dropzone-hint, .dropzone-cta); .file-upload-list, .file-upload-row, .file-upload-row-progress; .file-upload-thumbnails, .file-upload-thumbnail
  • .sortable-list, .sortable-item (+ .is-dragging, .is-grabbed); .sortable-indicator (2px var(--accent) line)
  • Pill tints: .pill-warm, .pill-muted, .pill-purple, .pill-urgent (powers the typed <LifecyclePill>, <TicketStatusPill>, <DealStagePill>, <RiskPill>)
  • .consent-toggle, .consent-events
  • .merge-tag-* (popover + sample-preview chrome)
  • .masked-image* (single-image and message-block forms)
  • .rich-text-editor, .rte-toolbar, .rte-content, .rte-link-popover, .rte-tag-popover

All animations honour prefers-reduced-motion: reduce.


[1.2.0] — 2026-05

Spark CRM Tier 1 primitives. Mirrors @magicblocksai/ui@1.2.0.

Added

  • .data-table — CSS-grid table chrome (sticky header, sortable columns with ▲/▼ indicators, selectable rows, skeleton loading)
  • .chip-button — multi-select chip variant of the existing .chip
  • .calendar — month-grid calendar (week header, day cells, range/single-select states, hover + focus)
  • .date-picker, .date-range-picker — trigger + popover chrome
  • .dialog-host — host wrapper for confirm/alert/prompt overlays

[1.1.0] — 2026-04

Changed

  • Warm token retune: --warm-1 #FBF6F0#FBFAF7, --warm-2 #F9F1E8#F6F4ED, --warm-5 #E8DBCB#E4DCD0, --warm-7 #D2BFAA#C9B89F. --warm-3 (the canonical page background) unchanged at #F4ECE4. --warm-1 and --warm-2 promoted to first-class surfaces in the foundations swatches.

Added

  • .cmdk-overlay — backdrop wrapper for the kit-site command palette (powers <KitCommandPalette>)
  • Mobile menu + TOC scroll cap at viewport-minus-topnav so long lists stay reachable on phones
  • .nav-search — topnav search button (powers KitChapterNav's showSearch prop)

Fixed

  • A11y: TOC sidebar heading promoted h4 → h2 to kill the h1→h4 cascade skip
  • Menu hover state aligned with combobox (pink accent-soft, tighter radius)

[1.0.0] — 2026-04

Phase 6G milestone. Initial publish. Full kit chrome (chapters 02–16) shipped as a single dropped-in CSS file. Token system, semantic colour layer (--bg, --fg, --accent, etc.), spacing scale (--s-1--s-9), radii (--r-sm--r-pill), typography stack (Bricolage / DM Sans / Fraunces / JetBrains Mono), motion tokens, dark-mode flip via body[data-theme="dark"].