Block Library¶
Block styles are reusable content units placed inside page templates.
AI Agent Guidance¶
When building a deck slide, follow this decision order:
- Choose a page template - determines the layout structure (see
docs/page-templates.md) - Choose block styles - fill the content areas with reusable blocks
- 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
7. image-gallery - Image Gallery 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:
-
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> ); }; -
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'; -
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'] } } }; -
Export from
src/blocks/index.ts:export { blockRegistry, type BlockType } from './registry'; -
Document in
docs/block-library.md(this file) with: - Purpose
- Source file
- Interface
- Props table
- Example
- When to use
-
AI agent guidance
-
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:
- Read this documentation to understand available block styles
- Check
src/blocks/registry.tsfor the canonical, up-to-date list - Match content to block style using the decision tree above
- Validate props against the TypeScript interfaces
- 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 |
Related 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)