Skip to content

Block Library

Block styles are reusable content units placed inside page templates.

AI Agent Guidance

When building a deck slide, follow this decision order:

  1. Choose a page template - determines the layout structure (see docs/page-templates.md)
  2. Choose block styles - fill the content areas with reusable blocks
  3. Configure props - customize title, data, appearance

Rules for AI agents: - Prefer reusable block styles over bespoke slide code - Check src/blocks/registry.ts for the canonical list - Only add a new block style when the content pattern is reusable across multiple decks - Blocks are configured in deck intent .md files under docs/decks/


Current Block Styles

# Block ID Component Status
1 bullet-list BulletListBlock ✅ Ready
2 numbered-list NumberedListBlock ✅ Ready
3 callout CalloutBlock ✅ Ready
4 stat-cards StatCardsBlock ✅ Ready
5 key-value-table KeyValueTableBlock ✅ Ready
6 comparison-table ComparisonTableBlock ✅ Ready
7 image-gallery ImageGalleryBlock ✅ Ready
8 media-bullet-list MediaBulletListBlock ✅ Ready
9 flow-diagram FlowDiagramBlock ✅ Ready
10 venn-chart VennChartBlock ✅ Ready
11 line-chart LineChartBlock ✅ Ready
12 bar-chart BarChartBlock ✅ Ready
13 pie-chart PieChartBlock ✅ Ready
14 area-chart AreaChartBlock ✅ Ready
15 scatter-chart ScatterChartBlock ✅ Ready
16 radar-chart RadarChartBlock ✅ Ready

1. bullet-list - Bullet List Block

Purpose: Unordered point list for concise statements.

Source: src/blocks/BulletListBlock.tsx

Interface:

interface BulletListProps {
  items: string[];
  title?: string;
}

Props:

Prop Type Required Default Description
items string[] - Array of bullet point texts
title string undefined Optional block title

Example:

import { BulletListBlock } from '../blocks/BulletListBlock';

<BulletListBlock
  title="Key Features"
  items={[
    'IP67 waterproof rating',
    'Integrated GPS + GLONASS',
    '2-year warranty included'
  ]}
/>

When to use: - Feature lists - Benefits enumeration - Key takeaways - Any unordered points

Page template compatibility: bullet-list, two-column, single-block


2. numbered-list - Numbered List Block

Purpose: Ordered list for sequences and procedures.

Source: src/blocks/NumberedListBlock.tsx

Interface:

interface NumberedListProps {
  items: string[];
  title?: string;
}

Props:

Prop Type Required Default Description
items string[] - Array of numbered step texts
title string undefined Optional block title

Example:

import { NumberedListBlock } from '../blocks/NumberedListBlock';

<NumberedListBlock
  title="Installation Steps"
  items={[
    'Unpack and inspect all components',
    'Mount the charging station to wall',
    'Connect power and verify indicators',
    'Pair with mobile app and test'
  ]}
/>

When to use: - Step-by-step procedures - Sequential instructions - Ranked lists - Process flows (when Flow Diagram is overkill)

Page template compatibility: bullet-list, two-column, single-block


3. callout - Callout Block

Purpose: Emphasis block for critical notes.

Source: src/blocks/CalloutBlock.tsx

Interface:

interface CalloutProps {
  title: string;
  body: string | string[];
  tone?: 'info' | 'success' | 'warning';
}

Props:

Prop Type Required Default Description
title string - Callout heading
body string \| string[] - Content (string or array of paragraphs)
tone 'info' \| 'success' \| 'warning' 'info' Visual tone/color

Tone variants: - info - Blue accent (neutral information) - success - Green accent (positive outcome) - warning - Amber accent (caution)

Example:

import { CalloutBlock } from '../blocks/CalloutBlock';

<CalloutBlock
  title="Important Safety Notice"
  body={[
    'Always disconnect power before servicing.',
    'Wear insulated gloves when handling terminals.'
  ]}
  tone="warning"
/>

When to use: - Safety warnings - Critical notes - Key takeaways (highlighted) - Regulatory compliance reminders

Page template compatibility: bullet-list, two-column, single-block, four-block-matrix


4. stat-cards - Stat Cards Block

Purpose: Compact KPI cards for metrics.

Source: src/blocks/StatCardsBlock.tsx

Interface:

interface StatCard {
  label: string;
  value: string;
}

interface StatCardsProps {
  cards: StatCard[];
}

Props:

Prop Type Required Default Description
cards StatCard[] - Array of label-value pairs

Card object: - label (string, required) - Metric name (e.g., "Range") - value (string, required) - Metric value (e.g., "70km")

Example:

import { StatCardsBlock } from '../blocks/StatCardsBlock';

<StatCardsBlock
  cards={[
    { label: 'Range', value: '70km' },
    { label: 'Charge Time', value: '3.5h' },
    { label: 'Weight', value: '22kg' },
    { label: 'Warranty', value: '2 years' }
  ]}
/>

When to use: - KPI dashboards - Product specifications - Performance metrics - Comparison tables (simplified)

Page template compatibility: bullet-list, two-column, single-block, four-block-matrix


5. key-value-table - Key-Value Table Block

Purpose: Structured mapping of fields and values.

Source: src/blocks/KeyValueTableBlock.tsx

Interface:

interface KeyValueRow {
  key: string;
  value: string;
}

interface KeyValueTableProps {
  title?: string;
  rows: KeyValueRow[];
}

Props:

Prop Type Required Default Description
title string undefined Optional table title
rows KeyValueRow[] - Array of key-value pairs

Row object: - key (string, required) - Field name (left column) - value (string, required) - Field value (right column)

Example:

import { KeyValueTableBlock } from '../blocks/KeyValueTableBlock';

<KeyValueTableBlock
  title="Technical Specifications"
  rows={[
    { key: 'Motor', value: '250W mid-drive' },
    { key: 'Battery', value: '48V 14Ah Li-ion' },
    { key: 'Frame', value: 'Aluminum alloy' },
    { key: 'Brakes', value: 'Hydraulic disc' }
  ]}
/>

When to use: - Technical specifications - Configuration details - Product comparisons (side-by-side with two-column) - Structured data display

Page template compatibility: bullet-list, two-column, single-block


6. comparison-table - Comparison Table Block

Purpose: Multi-column structured comparison for technical or positioning content.

Source: src/blocks/ComparisonTableBlock.tsx

Interface:

interface ComparisonTableRow {
  cells: string[];
}

interface ComparisonTableProps {
  title?: string;
  columns: string[];
  rows: ComparisonTableRow[];
  headerTones?: HeaderTone[];
  columnWidths?: string[];
}

Props:

Prop Type Required Default Description
title string undefined Optional table title
columns string[] - Column headers
rows ComparisonTableRow[] - Row data (each row = array of cell values)
headerTones HeaderTone[] ['default'] * columns Color tone per column header
columnWidths string[] repeat(columns.length, 'minmax(0, 1fr)') CSS grid column widths

HeaderTone type: 'default' | 'primary' | 'accent' | 'muted'

Row object: - cells (string[], required) - Array of cell values (length must match columns.length)

Example:

import { ComparisonTableBlock } from '../blocks/ComparisonTableBlock';

<ComparisonTableBlock
  title="E3Pro vs E3HPro Comparison"
  columns={['Feature', 'E3Pro', 'E3HPro']}
  rows={[
    { cells: ['Motor', '250W mid-drive', '350W mid-drive'] },
    { cells: ['Battery', '48V 14Ah', '48V 17.5Ah'] },
    { cells: ['Range', '70km', '90km'] },
    { cells: ['Weight', '22kg', '24kg'] }
  ]}
  headerTones={['default', 'primary', 'accent']}
  columnWidths={['120px', '1fr', '1fr']}
/>

When to use: - Product comparisons - Competitive analysis - Feature matrices - Technical specifications (multi-item)

Page template compatibility: bullet-list, two-column, single-block


Purpose: Compact image grid with optional lead text.

Source: src/blocks/ImageGalleryBlock.tsx

Interface:

interface GalleryImage {
  src: string;
  alt: string;
}

interface ImageGalleryProps {
  title?: string;
  images: GalleryImage[];
  lead?: string;
  columns?: number;
}

Props:

Prop Type Required Default Description
title string undefined Optional block title (uppercase, muted)
images GalleryImage[] - Array of image objects
lead string undefined Optional lead text below gallery
columns number 2 Number of columns in grid

Image object: - src (string, required) - Image path (resolved via resolveAssetPath) - alt (string, required) - Accessibility alt text

Example:

import { ImageGalleryBlock } from '../blocks/ImageGalleryBlock';

<ImageGalleryBlock
  title="Product Gallery"
  images={[
    { src: '/images/e3pro-front.jpg', alt: 'E3Pro front view' },
    { src: '/images/e3pro-side.jpg', alt: 'E3Pro side view' },
    { src: '/images/e3pro-detail.jpg', alt: 'E3Pro motor detail' },
    { src: '/images/e3pro-charging.jpg', alt: 'E3Pro charging port' }
  ]}
  lead="All images show production-intent design."
  columns={2}
/>

When to use: - Product showcases - Design reviews - Reference images - Visual comparisons

Page template compatibility: bullet-list, two-column, single-block, four-block-matrix


8. media-bullet-list - Media Bullet List Block

Purpose: Bullet list paired with a reference image for side-by-side comparison.

Source: src/blocks/MediaBulletListBlock.tsx

Interface:

interface MediaBulletListProps {
  eyebrow?: string;
  title: string;
  items: string[];
  imageSrc: string;
  imageAlt: string;
  lead?: string;
}

Props:

Prop Type Required Default Description
eyebrow string undefined Small uppercase text above title
title string - Block title (large, bold)
items string[] - Bullet list items
imageSrc string - Image path (right side)
imageAlt string - Image alt text
lead string undefined Optional lead text below title

Layout: CSS Grid, 2 columns (content + image), image fixed 420px width.

Example:

import { MediaBulletListBlock } from '../blocks/MediaBulletListBlock';

<MediaBulletListBlock
  eyebrow="Product Overview"
  title="E3Pro Key Features"
  items={[
    '250W mid-drive motor',
    '48V 14Ah removable battery',
    'Hydraulic disc brakes',
    'Integrated GPS + GLONASS'
  ]}
  imageSrc="/images/e3pro-hero.jpg"
  imageAlt="E3Pro electric bike hero shot"
  lead="Flagship model with premium components."
/>

When to use: - Product overviews with hero image - Feature lists with visual reference - Side-by-side content + image layouts - Marketing slides

Page template compatibility: two-column (left column), single-block


9. flow-diagram - Flow Diagram Block

Purpose: Node-and-arrow process or architecture diagram.

Source: src/blocks/FlowDiagramBlock.tsx

Interface:

type FlowDiagramLayout = 'horizontal' | 'vertical';
type FlowDiagramNodeKind = 'default' | 'primary' | 'accent' | 'mechanical' | 'output';
type FlowDiagramEdgeKind = 'default' | 'signal' | 'mechanical' | 'integrated';

interface FlowDiagramNode {
  id: string;
  label: string;
  subtitle?: string;
  kind?: FlowDiagramNodeKind;
  row?: number;
}

interface FlowDiagramEdge {
  from: string;
  to: string;
  label?: string;
  kind?: FlowDiagramEdgeKind;
}

interface FlowDiagramProps {
  title?: string;
  layout?: FlowDiagramLayout;
  nodes: FlowDiagramNode[];
  edges: FlowDiagramEdge[];
  footerNote?: string;
  width?: number;
  height?: number;
}

Props:

Prop Type Required Default Description
title string undefined Optional diagram title (uppercase, muted)
layout 'horizontal' \| 'vertical' 'horizontal' Node layout direction
nodes FlowDiagramNode[] - Array of diagram nodes
edges FlowDiagramEdge[] - Array of edges (connections)
footerNote string undefined Optional note below diagram
width number 760 Diagram width in pixels
height number 420 Diagram height in pixels

Node object: - id (string, required) - Unique node ID (used in edges) - label (string, required) - Node text - subtitle (string, optional) - Secondary text below label - kind (FlowDiagramNodeKind, optional) - Node color tone - row (number, optional) - Layout row (for vertical layout)

Edge object: - from (string, required) - Source node ID - to (string, required) - Target node ID - label (string, optional) - Edge label - kind (FlowDiagramEdgeKind, optional) - Edge color

Node kinds (color tones): - default - Blue tone - primary - Darker blue tone - accent - Green tone - mechanical - Muted tone - output - Blue tone (same as default)

Edge kinds (color coding): - default - Light blue - signal - Light blue (same as default) - mechanical - Orange - integrated - Green

Example:

import { FlowDiagramBlock } from '../blocks/FlowDiagramBlock';

<FlowDiagramBlock
  title="Charging Process Flow"
  layout="horizontal"
  nodes={[
    { id: 'input', label: 'AC Input', kind: 'default' },
    { id: 'rectifier', label: 'Rectifier', subtitle: 'AC to DC', kind: 'mechanical' },
    { id: 'controller', label: 'Controller', subtitle: 'PWM Control', kind: 'primary' },
    { id: 'battery', label: 'Battery', subtitle: '48V Li-ion', kind: 'output' }
  ]}
  edges={[
    { from: 'input', to: 'rectifier', kind: 'signal' },
    { from: 'rectifier', to: 'controller', kind: 'mechanical' },
    { from: 'controller', to: 'battery', kind: 'integrated' }
  ]}
  footerNote="All connections use CAN bus protocol."
  width={760}
  height={420}
/>

When to use: - Process flows - System architecture diagrams - Data pipelines - State machines - Decision trees

Page template compatibility: bullet-list, two-column, single-block


10. venn-chart - Venn Chart Block

Purpose: Three-set overlap chart for concept intersections.

Source: src/blocks/VennChartBlock.tsx

Interface:

interface VennSet {
  label: string;
  color: string;
}

interface VennChartProps {
  title?: string;
  sets: [VennSet, VennSet, VennSet];
}

Props:

Prop Type Required Default Description
title string undefined Optional chart title
sets [VennSet, VennSet, VennSet] - Exactly 3 sets (fixed)

Set object: - label (string, required) - Set name (appears below circle) - color (string, required) - CSS color value (with opacity applied)

Limitations: - Fixed 3 circles (cannot add/remove sets) - Fixed SVG dimensions (520x320) - No responsive behavior - No animation - No interactive features

Example:

import { VennChartBlock } from '../blocks/VennChartBlock';

<VennChartBlock
  title="Product Positioning"
  sets={[
    { label: 'Performance', color: '#2e6fd8' },
    { label: 'Affordability', color: '#3fb76f' },
    { label: 'Durability', color: '#f4a261' }
  ]}
/>

When to use: - Concept overlap visualization - Product positioning (3 dimensions) - Venn diagram use cases - Simple set intersections

Page template compatibility: bullet-list, two-column, single-block, four-block-matrix

⚠️ Note: This block is basic (hardcoded SVG). Consider upgrading to Recharts or a proper Venn library for production use.


Block Selection Decision Tree

Start: What content type are you displaying?

├─ List of points?
│  ├─ Ordered steps? → Use: numbered-list
│  └─ Unordered points? → Use: bullet-list
│
├─ Metrics/KPIs?
│  └─ Use: stat-cards
│
├─ Structured data?
│  ├─ Key-value pairs? → Use: key-value-table
│  └─ Multi-column comparison? → Use: comparison-table
│
├─ Process/architecture?
│  └─ Use: flow-diagram
│
├─ Images?
│  ├─ Gallery (multiple)? → Use: image-gallery
│  └─ Single image + list? → Use: media-bullet-list
│
├─ Concept overlap?
│  └─ Use: venn-chart
│
├─ Critical note?
│  └─ Use: callout
│
└─ None of above?
   └─ Consider: bullet-list (most flexible) or request new block

Combining Blocks with Page Templates

Pattern 1: Single block per page

// bullet-list page with stat-cards block
<BulletListSlideTemplate
  title="Performance Metrics"
  items={[...]}
  variant="default"
/>
// Block rendered inside page template
<StatCardsBlock cards={[...]} />

Pattern 2: Two blocks side-by-side (two-column page)

<TwoColumnSlideTemplate
  title="Comparison"
  leftContent={<StatCardsBlock cards={[...]} />}
  rightContent={<ComparisonTableBlock columns={[...]} rows={[...]} />}
/>

Pattern 3: Multiple blocks in single-column page

<BulletListSlideTemplate
  title="Product Overview"
  items={[
    'Feature 1',
    'Feature 2'
  ]}
/>

// Followed by stat-cards block
<StatCardsBlock cards={[...]} />

Pattern 4: Flow diagram with callout

<FlowDiagramBlock
  title="Process Flow"
  nodes={[...]}
  edges={[...]}
/>

<CalloutBlock
  title="Note"
  body="This process takes 3-5 business days."
  tone="info"
/>


Registry

Location: src/blocks/registry.ts

The registry exports: - BlockType - Union type of all valid block type IDs - BlockDefinition - Interface for registry entries - blockRegistry - Record mapping type IDs to definitions

Programmatic access:

import { blockRegistry } from '../blocks/registry';

// Get all block types
const availableBlocks = Object.keys(blockRegistry);
// → ['bullet-list', 'numbered-list', 'callout', 'stat-cards', 'key-value-table', 'comparison-table', 'image-gallery', 'media-bullet-list', 'flow-diagram', 'venn-chart']

// Get block definition
const bulletListDef = blockRegistry['bullet-list'];
// → { type, title, description, component, requiredProps, defaultProps }


8. flow-diagram - Flow Diagram Block

Purpose: Node-and-arrow process or architecture diagram using React Flow.

Source: src/blocks/FlowDiagramBlock.tsx

Interface:

interface FlowDiagramProps {
  title?: string;
  nodes: Array<{ id: string; label: string; kind?: 'default' | 'primary' | 'accent' | 'mechanical' | 'output' }>;
  edges: Array<{ from: string; to: string; kind?: 'signal' | 'mechanical' | 'integrated' }>;
  layout?: 'horizontal' | 'vertical';
  height?: number;
}

Props:

Prop Type Required Default Description
title string undefined Block title
nodes Array<{id, label, kind}> - Diagram nodes
edges Array<{from, to, kind}> - Connections between nodes
layout 'horizontal' \| 'vertical' 'horizontal' Layout orientation
height number 350 Diagram height (px)

Node kinds: default (gray), primary (blue), accent (purple), mechanical (orange), output (green)

Edge kinds: signal (blue dashed), mechanical (orange), integrated (purple)

Example:

import { FlowDiagramBlock } from '../blocks/FlowDiagramBlock';

<FlowDiagramBlock
  title="Data Pipeline"
  height={400}
  nodes={[
    { id: 'a', label: 'Ingest', kind: 'default' },
    { id: 'b', label: 'Process', kind: 'primary' },
    { id: 'c', label: 'Store', kind: 'accent' },
    { id: 'd', label: 'API', kind: 'output' },
  ]}
  edges={[
    { from: 'a', to: 'b', kind: 'signal' },
    { from: 'b', to: 'c', kind: 'mechanical' },
    { from: 'c', to: 'd', kind: 'integrated' },
  ]}
  layout="horizontal"
/>

When to use: - Process workflows - Data pipelines - System architecture - Decision flows

Page template compatibility: two-column, single-block


9. venn-chart - Venn Chart Block

Purpose: Three-set overlap chart for concept intersections.

Source: src/blocks/VennChartBlock.tsx

Interface:

interface VennChartProps {
  title?: string;
  sets: Array<{ label: string; color?: string }>;
  width?: number;
  height?: number;
}

Props:

Prop Type Required Default Description
title string undefined Block title
sets Array<{label, color}> - 3 sets with labels and colors
width number 520 SVG width
height number 320 SVG height

⚠️ Limitation: Only supports exactly 3 sets. For 2-set comparisons, use comparison-table block.

Example:

import { VennChartBlock } from '../blocks/VennChartBlock';

<VennChartBlock
  title="Skill Overlap"
  sets={[
    { label: 'Frontend', color: '#667eea' },
    { label: 'Backend', color: '#ff6b6b' },
    { label: 'DevOps', color: '#4CAF50' },
  ]}
  width={520}
  height={320}
/>

When to use: - Concept intersection analysis - Skill/competency overlap - Feature set comparisons (3 items max) - Venn diagram is the clearest way to show overlap

Page template compatibility: two-column, single-block


10. line-chart - Line Chart Block

Purpose: Multi-series line chart for trends over a dimension (e.g. time).

Source: src/blocks/LineChartBlock.tsx

Interface:

interface LineChartBlockProps {
  title?: string;
  description?: string;
  data: Array<Record<string, any>>;
  xAxisKey: string;
  lines: Array<{ dataKey: string; color?: string; name?: string }>;
  height?: number;
  variant?: 'default' | 'dark';
}

Props:

Prop Type Required Default Description
title string undefined Block title
description string undefined Subtitle below title
data Array<Record> - Chart data array
xAxisKey string - Key for X-axis categories
lines Array<{dataKey, color, name}> - Line series definitions
height number 300 Chart height (px)
variant 'default' \| 'dark' 'default' Visual style

Example:

import { LineChartBlock } from '../blocks/LineChartBlock';

<LineChartBlock
  title="Monthly Revenue"
  description="Jan–Jun 2026"
  data={[
    { month: 'Jan', revenue: 120, costs: 80 },
    { month: 'Feb', revenue: 150, costs: 90 },
    { month: 'Mar', revenue: 180, costs: 100 },
  ]}
  xAxisKey="month"
  lines={[
    { dataKey: 'revenue', color: '#667eea', name: 'Revenue' },
    { dataKey: 'costs', color: '#ff6b6b', name: 'Costs' },
  ]}
  height={300}
/>

When to use: - Time-series trends - Multiple series comparison over same dimension - Revenue/cost/profit over time - Any continuous trend data

Page template compatibility: two-column, single-block, bullet-list (with description)


11. bar-chart - Bar Chart Block

Purpose: Bar chart for categorical comparison. Supports vertical and horizontal layouts.

Source: src/blocks/BarChartBlock.tsx

Interface:

interface BarChartBlockProps {
  title?: string;
  description?: string;
  data: Array<Record<string, any>>;
  xAxisKey: string;
  bars: Array<{ dataKey: string; color?: string; name?: string }>;
  layout?: 'horizontal' | 'vertical';
  height?: number;
  variant?: 'default' | 'dark';
}

Props:

Prop Type Required Default Description
title string undefined Block title
description string undefined Subtitle below title
data Array<Record> - Chart data array
xAxisKey string - Key for X-axis categories
bars Array<{dataKey, color, name}> - Bar series definitions
layout 'horizontal' \| 'vertical' 'horizontal' Bar orientation
height number 300 Chart height (px)
variant 'default' \| 'dark' 'default' Visual style

Example:

import { BarChartBlock } from '../blocks/BarChartBlock';

// Vertical bars (default)
<BarChartBlock
  title="Quarterly Profit"
  data={[
    { quarter: 'Q1', profit: 40 },
    { quarter: 'Q2', profit: 60 },
    { quarter: 'Q3', profit: 80 },
  ]}
  xAxisKey="quarter"
  bars={[{ dataKey: 'profit', color: '#4CAF50', name: 'Profit' }]}
  height={280}
/>

// Horizontal bars
<BarChartBlock
  title="Feature Comparison"
  data={[
    { feature: 'Battery', ours: 90, theirs: 70 },
    { feature: 'Motor', ours: 80, theirs: 85 },
  ]}
  xAxisKey="feature"
  bars={[
    { dataKey: 'ours', color: '#667eea', name: 'Ours' },
    { dataKey: 'theirs', color: '#ff6b6b', name: 'Theirs' },
  ]}
  layout="vertical"
/>

When to use: - Categorical comparison - Horizontal layout for long category labels - Vertical layout for time-based categories - Multi-series bar comparison

Page template compatibility: two-column, single-block


12. pie-chart - Pie / Donut Chart Block

Purpose: Proportional chart for part-to-whole data. Set innerRadius > 0 for donut variant.

Source: src/blocks/PieChartBlock.tsx

Interface:

interface PieChartBlockProps {
  title?: string;
  description?: string;
  data: Array<{ name: string; value: number }>;
  colors?: string[];
  height?: number;
  variant?: 'default' | 'dark';
  innerRadius?: number;   // > 0 → donut
  outerRadius?: number;
}

Props:

Prop Type Required Default Description
title string undefined Block title
description string undefined Subtitle below title
data Array<{name, value}> - Pie segments
colors string[] 8-color palette Segment fill colors
height number 300 Chart height (px)
variant 'default' \| 'dark' 'default' Visual style
innerRadius number 0 Inner radius (0=pie, >0=donut)
outerRadius number 100 Outer radius

Example:

import { PieChartBlock } from '../blocks/PieChartBlock';

// Pie chart
<PieChartBlock
  title="Revenue by Region"
  data={[
    { name: 'North', value: 35 },
    { name: 'South', value: 25 },
    { name: 'East', value: 20 },
    { name: 'West', value: 20 },
  ]}
  height={320}
/>

// Donut chart
<PieChartBlock
  title="Market Share"
  data={[
    { name: 'Ours', value: 42 },
    { name: 'Competitor A', value: 28 },
    { name: 'Competitor B', value: 30 },
  ]}
  innerRadius={60}
  outerRadius={120}
  colors={['#667eea', '#ff6b6b', '#4CAF50']}
/>

When to use: - Market share breakdown - Budget allocation - Survey response distribution - Any part-to-whole proportional data

Page template compatibility: two-column, single-block, bullet-list (with description)


13. area-chart - Area Chart Block

Purpose: Multi-series area chart for trends with fill opacity. Supports stacked mode.

Source: src/blocks/AreaChartBlock.tsx

Interface:

interface AreaChartBlockProps {
  title?: string;
  description?: string;
  data: Array<Record<string, any>>;
  xAxisKey: string;
  areas: Array<{ dataKey: string; color?: string; name?: string; fillOpacity?: number }>;
  stacked?: boolean;
  height?: number;
  variant?: 'default' | 'dark';
}

Props:

Prop Type Required Default Description
title string undefined Block title
description string undefined Subtitle below title
data Array<Record> - Chart data array
xAxisKey string - Key for X-axis categories
areas Array<{dataKey, color, name, fillOpacity}> - Area series definitions
stacked boolean false Stack areas on same scale
height number 300 Chart height (px)
variant 'default' \| 'dark' 'default' Visual style

Example:

import { AreaChartBlock } from '../blocks/AreaChartBlock';

// Basic area chart
<AreaChartBlock
  title="Revenue Trend"
  description="Jan–Jun 2026"
  data={[
    { month: 'Jan', revenue: 120, costs: 80 },
    { month: 'Feb', revenue: 150, costs: 90 },
    { month: 'Mar', revenue: 180, costs: 100 },
  ]}
  xAxisKey="month"
  areas={[
    { dataKey: 'revenue', color: '#667eea', name: 'Revenue', fillOpacity: 0.3 },
    { dataKey: 'costs', color: '#ff6b6b', name: 'Costs', fillOpacity: 0.2 },
  ]}
  height={300}
/>

// Stacked area chart
<AreaChartBlock
  title="Stacked Revenue Breakdown"
  data={[...]}
  xAxisKey="month"
  areas={[...]}
  stacked={true}
/>

When to use: - Trends with emphasis on volume (filled area draws eye to magnitude) - stacked metrics (e.g., revenue breakdown by product) - Time-series data where area better shows proportion than lines alone

Page template compatibility: two-column, single-block, bullet-list (with description)


14. scatter-chart - Scatter Chart Block

Purpose: Scatter plot for correlation analysis. Supports multi-series.

Source: src/blocks/ScatterChartBlock.tsx

Interface:

interface ScatterChartBlockProps {
  title?: string;
  description?: string;
  data: Array<Record<string, any>>;
  xAxisKey?: string;
  yAxisKey?: string;
  series?: Array<{ dataKey: string; color?: string; name?: string }>;
  height?: number;
  variant?: 'default' | 'dark';
}

Props:

Prop Type Required Default Description
title string undefined Block title
description string undefined Subtitle below title
data Array<Record> - Chart data array
xAxisKey string 'x' Key for X-axis values
yAxisKey string 'y' Key for Y-axis values
series Array<{dataKey, color, name}> undefined Series defs (one <Scatter> per series)
height number 300 Chart height (px)
variant 'default' \| 'dark' 'default' Visual style

Data format (single series, no series prop):

data = [
  { x: 10, y: 20 },
  { x: 15, y: 35 },
  { x: 12, y: 28 },
]

Data format (multi-series, with series prop):

data = [
  { A_y: 20, B_y: 35, x: 10 },
  { A_y: 25, B_y: 40, x: 15 },
]
// series = [{ dataKey: 'A_y', name: 'Series A' }, { dataKey: 'B_y', name: 'Series B' }]

Example:

import { ScatterChartBlock } from '../blocks/ScatterChartBlock';

// Single series
<ScatterChartBlock
  title="Price vs. Range Correlation"
  data={[
    { x: 3500, y: 70 },
    { x: 4200, y: 85 },
    { x: 2800, y: 55 },
  ]}
  xAxisKey="x"
  yAxisKey="y"
  height={320}
/>

// Multi-series
<ScatterChartBlock
  title="Competitor Comparison"
  data={[
    { x: 3500, Ours: 70, Theirs: 65 },
    { x: 4200, Ours: 85, Theirs: 80 },
  ]}
  xAxisKey="x"
  series={[
    { dataKey: 'Ours', color: '#667eea', name: 'Our Product' },
    { dataKey: 'Theirs', color: '#ff6b6b', name: 'Competitor' },
  ]}
/>

When to use: - Correlation analysis (does X correlate with Y?) - Outlier detection - Clustering visualization - Any data where both axes are continuous numerical values

Page template compatibility: two-column, single-block


15. radar-chart - Radar Chart Block

Purpose: Multi-axis radar chart for comparative profiling across multiple dimensions.

Source: src/blocks/RadarChartBlock.tsx

Interface:

interface RadarChartBlockProps {
  title?: string;
  description?: string;
  data: Array<Record<string, any>>;
  metrics: string[];           // keys for each axis (e.g. ['speed', 'power', 'range'])
  radars: Array<{ dataKey: string; color?: string; name?: string }>;
  height?: number;
  variant?: 'default' | 'dark';
}

Props:

Prop Type Required Default Description
title string undefined Block title
description string undefined Subtitle below title
data Array<Record> - Chart data (one row per entity)
metrics string[] - Keys for each radar axis
radars Array<{dataKey, color, name}> - Series definitions (one <Radar> per series)
height number 300 Chart height (px)
variant 'default' \| 'dark' 'default' Visual style

Data format:

data = [
  { metric: 'Speed', A: 80, B: 60 },
  { metric: 'Power', A: 70, B: 90 },
  { metric: 'Range', A: 90, B: 70 },
  { metric: 'Weight', A: 60, B: 80 },
]
metrics = ['Speed', 'Power', 'Range', 'Weight']
radars = [
  { dataKey: 'A', color: '#667eea', name: 'Model A' },
  { dataKey: 'B', color: '#ff6b6b', name: 'Model B' },
]

Example:

import { RadarChartBlock } from '../blocks/RadarChartBlock';

<RadarChartBlock
  title="Product Comparison Radar"
  description="OVES E3Pro vs Competitor X"
  data={[
    { metric: 'Range', Ours: 85, Theirs: 70 },
    { metric: 'Speed', Ours: 90, Theirs: 85 },
    { metric: 'Price', Ours: 70, Theirs: 90 },
    { metric: 'Warranty', Ours: 95, Theirs: 60 },
    { metric: 'Service', Ours: 88, Theirs: 75 },
  ]}
  metrics={['Range', 'Speed', 'Price', 'Warranty', 'Service']}
  radars={[
    { dataKey: 'Ours', color: '#667eea', name: 'E3Pro' },
    { dataKey: 'Theirs', color: '#ff6b6b', name: 'Comp X' },
  ]}
  height={350}
/>

When to use: - Multi-dimensional comparison (5-8 dimensions) - Product/feature profiling - Skill/competency assessment - Any comparison where single-axis charts (bar, line) can't show the full picture

Page template compatibility: two-column, single-block


Adding a New Block Style

Prerequisites: 1. The content pattern must be reusable across multiple decks 2. Existing block styles cannot express the content cleanly 3. You have a clear React.FC<PropsInterface> component

Steps:

  1. Create component in src/blocks/:

    // MyNewBlock.tsx
    import React from 'react';
    import { Box, Text } from 'spectacle';
    import { ovesTheme } from '../shared/design-system/theme';
    import type { MyNewProps } from './types';
    
    export const MyNewBlock: React.FC<MyNewProps> = ({ title, data }) => {
      return (
        <Box>
          {title && <Text>{title}</Text>}
          {/* Your block implementation */}
        </Box>
      );
    };
    

  2. Add interface to src/blocks/types.ts:

    export interface MyNewProps {
      title?: string;
      data: string[];
    }
    
    // Also add to BlockType union:
    export type BlockType =
      // ... existing types
      | 'my-new';
    

  3. Register in src/blocks/registry.ts:

    import { MyNewBlock } from './MyNewBlock';
    
    export const blockRegistry: Record<BlockType, BlockDefinition> = {
      // ... existing entries
      'my-new': {
        type: 'my-new',
        title: 'My New Block',
        description: 'Description for AI agents and humans.',
        component: MyNewBlock,
        requiredProps: ['data'],
        defaultProps: { data: ['Item 1', 'Item 2'] }
      }
    };
    

  4. Export from src/blocks/index.ts:

    export { blockRegistry, type BlockType } from './registry';
    

  5. Document in docs/block-library.md (this file) with:

  6. Purpose
  7. Source file
  8. Interface
  9. Props table
  10. Example
  11. When to use
  12. AI agent guidance

  13. Test with npm run build:update:deck -- <your-test-deck>


Common Patterns

Pattern 1: Specifications Page

// Page: bullet-list
<BulletListSlideTemplate
  title="Technical Specifications"
  description="E3Pro electric bike"
/>

// Block 1: key-value-table
<KeyValueTableBlock
  rows={[
    { key: 'Motor', value: '250W mid-drive' },
    { key: 'Battery', value: '48V 14Ah' }
  ]}
/>

// Block 2: stat-cards
<StatCardsBlock
  cards={[
    { label: 'Range', value: '70km' },
    { label: 'Weight', value: '22kg' }
  ]}
/>

Pattern 2: Comparison Page

// Page: two-column
<TwoColumnSlideTemplate
  title="E3Pro vs E3HPro"
  leftContent={
    <ComparisonTableBlock
      columns={['Feature', 'E3Pro']}
      rows={[...]}
    />
  }
  rightContent={
    <ImageGalleryBlock
      images={[...]}
      columns={1}
    />
  }
/>

Pattern 3: Process + Notes

// Block 1: flow-diagram
<FlowDiagramBlock
  title="Manufacturing Process"
  nodes={[...]}
  edges={[...]}
/>

// Block 2: callout
<CalloutBlock
  title="Quality Check"
  body="Each unit undergoes 3-stage testing."
  tone="success"
/>

AI Agent MCP Integration

When an AI agent (e.g., WorkBuddy) receives a request to create or modify a deck, it should:

  1. Read this documentation to understand available block styles
  2. Check src/blocks/registry.ts for the canonical, up-to-date list
  3. Match content to block style using the decision tree above
  4. Validate props against the TypeScript interfaces
  5. Use example props from registry as starting point

MCP tool usage: - Use read_file to fetch src/blocks/registry.ts - Use read_file to fetch individual block implementations - Use edit_file to update deck intent .md files - Use execute_command to run npm run build:update:deck after changes

Error prevention: - Always check that the block type exists in blockRegistry - Always provide required props (check requiredProps in registry) - Always match prop types (TypeScript interfaces are authoritative) - For comparison-table, ensure rows[n].cells.length === columns.length


Source Files

File Purpose
src/blocks/registry.ts Canonical registry (type definitions, registry object)
src/blocks/types.ts TypeScript interfaces for all blocks
src/blocks/index.ts Public exports
src/blocks/*.tsx Implementations
docs/block-library.md This documentation

  • Page Templates: docs/page-templates.md (layout primitives that wrap blocks)
  • Deck Templates: docs/deck-templates.md (purpose-specific deck configurations)
  • Deck Intents: docs/deck-intents.md (how to define deck structure with blocks)
  • System-Wide Layout: docs/system-wide-layout.md (header, footer, content area rules)