CrispDeck Logo CrispDeck Documentation

CrispDeck Schema Guide

CrispDeck is a client-side declarative presentation engine. It parses structured JSON generated by Large Language Models (LLMs) and renders it into pixel-perfect, responsive, native D3-driven slides instantly—no server or API required.

Pro Tip: CrispDeck relies on a strict TypeScript schema. Copy and paste the schema provided below directly into your AI assistant's system prompt (ChatGPT, Claude, Gemini) to guarantee perfectly compliant presentation arrays every time.

AI System Schema (For LLMs)

To optimize for AI generation, provide this exact TypeScript definition to your LLM. It maps perfectly to CrispDeck's internal layout and component engine.

// 1. Global Deck Configuration
type ThemeDef = { bg?: string; surface?: string; border?: string; text?: string; inverted?: string; accent?: string; kicker?: string; head?: string; fontHeading?: string; fontBody?: string; };
type Deck = { deckTitle: string; footerText: string; theme?: ThemeDef; slides: Slide[]; };

// 2. Slide Level Configurations
type Slide = { 
    id: string; 
    layout: "title" | "divider" | "hero" | "canvas" | "multi-row" | "multi-col" | "dynamic-grid" | "split-30-70" | "split-70-30"; 
    gridColumns?: number; // Only required for 'dynamic-grid'
    kicker?: string; 
    title: string; 
    subtitle?: string; 
    tracker?: string; 
    zoneStyles?: { flex?: string | number, width?: string, height?: string, justifyContent?: string, alignItems?: string }[]; 
    zones?: Component[][]; 
};

// 3. Base Component & Styling Overrides
type BaseComponent = { 
    style?: { flex?: string | number, width?: string, height?: string, maxWidth?: string, maxHeight?: string, textAlign?: string, margin?: string, padding?: string } 
};

// 4. Core Components
type Component = BaseComponent & (
| { type: "text", variant: "markdown" | "quote" | "poster", content: string }
| { type: "html", content: string }
| { type: "list", variant: "toc" | "comparison", items: ({ text: string, positive?: boolean } | string)[] }
| { type: "indicator", variant: "kpi", label: string, value: string | number, trend?: string }
| { type: "indicator", variant: "rag", label: string, value: "red" | "amber" | "green" }
| { type: "indicator", variant: "harvey", label: string, value: number }
| { type: "chevron-flow", steps: { label: string, active: boolean }[] }
| { type: "timeline", events: { date: string, title: string, desc?: string }[] }
| { type: "logic-tree", title: string, desc?: string, children?: any[] } // Recursive tree structure
| { type: "matrix-2x2", xAxis: string, yAxis: string, quadrants: { name: string, items: string[] }[] }
| { type: "data-table", headers: string[], rows: (string | number)[][] } 
| { type: "gantt", timeline: string[], tasks: { label: string, start: number, duration: number, desc?: string, status?: "done" | "progress" | "delayed" }[] }
| { type: "image", url?: string, imageQuery?: string, caption?: string, alt?: string }
| { type: "media-block", variant: "profile" | "icon", title: string, subtitle: string, role?: string, iconName?: string, url?: string }
| ChartComponent
);

// 5. D3.js Chart Components
type ChartComponent = BaseComponent & (
| { type: "chart", variant: "bar" | "pie" | "donut" | "line" | "area" | "waterfall" | "funnel" | "treemap", title?: string, data: { label: string, value: number, isTotal?: boolean }[] }
| { type: "chart", variant: "stacked-bar", title?: string, data: { label: string, series: { name: string, value: number }[] }[] }
| { type: "chart", variant: "scatter", title?: string, data: { x: number, y: number, r: number, label: string }[] }
| { type: "chart", variant: "radar", title?: string, features: string[], data: { values: number[] }[] }
| { type: "chart", variant: "heatmap", title?: string, xLabels: string[], yLabels: string[], data: number[][] }
| { type: "chart", variant: "map", region?: "world" | "us" | "europe" | "asia" | "africa" | "middle_east" | "india" | "south_america" | "canada" | "oceania", title?: string, data: { label: string, value: number }[], pins?: { label: string, lat: number, lng: number, value?: string | number }[] }
);

Global Rules & Styling Overrides

To ensure beautiful outputs without manual formatting, adhere to these engine rules:

  • Smart Paste Auto-Cleaning: CrispDeck automatically extracts JSON from conversational filler. You can paste the raw LLM output (including the ```json markdown backticks) directly into the editor and the engine will clean it automatically.
  • The 30/70 Spatial Rule: Place dense components (like Gantt charts or multi-series tables) in wider columns (70%), and tall/narrow components in smaller columns (30%).
  • Title/Hero Overrides: Slides using the title, canvas, or hero layouts do not support standard zones. They act as full-bleed cinematic states.
  • Markdown Support: The engine uses marked.js under the hood. All standard string fields in components (like content, desc, table cells, or timeline events) natively support markdown formatting like **bold**, *italic*, and links.
  • Custom Fonts: You can inject ANY Google Font or local font via the Theme UI. If defined in the JSON theme object (e.g., "'Space Grotesk', sans-serif"), CrispDeck handles the external CSS fetch automatically.

Component-Level Styling

Every component in the system accepts a style object that maps to standard CSS properties. Use this to override the default layout engine rules (e.g., restricting the height of a specific chart to let another component grow).

{
    "type": "chart",
    "variant": "pie",
    "data": [...],
    "style": { "height": "200px", "margin": "0 auto" }
}

Slide Layouts

Every slide object requires an id, a title, and a layout string. The engine maps these layouts into optimized CSS Flexbox and Grid structures dynamically.

Cinematic Layouts

title, hero, canvas, divider

Grid Layouts

multi-col, multi-row, split-30-70, split-70-30, dynamic-grid

{
    "id": "slide_01",
    "layout": "split-30-70",
    "kicker": "Market Analysis",
    "title": "Q3 Growth Trajectory",
    "zones": [ ... ]
}

Zones & Columns

If using a Grid Layout, the zones array defines your columns. It is an array of arrays. Each inner array represents a column containing your components.

You can also pass an optional zoneStyles array at the slide level to control the CSS Flexbox/Grid properties of each specific column.

"zoneStyles": [
    { "justifyContent": "space-between" },
    { "padding": "20px" }
],
"zones": [
    [ 
        // Column 1 Components
        { "type": "text", "content": "..." } 
    ],
    [ 
        // Column 2 Components
        { "type": "chart", "variant": "bar", "data": [...] } 
    ]
]

Typography & Lists

Text Component

The text component supports rich markdown rendering. Variants alter the display context.

  • markdown: Standard body text. Supports headers, bold, italics, code, and lists.
  • quote: Stylized blockquote intended for Hero layouts or emphasis.
  • poster: Full-bleed overlay text designed specifically for canvas layouts.

List Component

Variants include toc (Table of Contents numbering) and comparison (Check/X marks).

{
    "type": "list",
    "variant": "comparison",
    "items": [
        { "text": "Auto-scaling typography", "positive": true },
        { "text": "Manual pixel pushing", "positive": false }
    ]
}

Media & HTML Embeds

CrispDeck supports dynamic assets and raw code injections for edge cases.

  • image: You can provide a direct url, or use imageQuery (e.g., "corporate, boardroom"). The engine will automatically format the keywords and fetch a dynamic stock image via LoremFlickr (with a built-in fallback to Picsum).
  • media-block (Profile): Generates an automated avatar with initials via ui-avatars.com if no url is provided or if a placeholder URL is detected.
  • media-block (Icon): The iconName field natively supports any standard Lucide Icon string.
  • html: Allows you to break out of the JSON schema by embedding external libraries via secure iFrames, such as Plotly 3D surfaces, Vis.js network graphs, or custom Leaflet Maps. The PDF export engine automatically attempts to resize, flatten, and capture these webGL canvases during generation.
{
    "type": "media-block",
    "variant": "profile",
    "title": "Jane Doe",
    "role": "Lead Engineer",
    "subtitle": "Architect of the new data pipeline."
}

Indicators & Status

Use the indicator type to build data-dense dashboards.

  • kpi: Large numerical metrics with an optional trend field (e.g., "↑ 12%"). Sequential KPIs in the same zone will automatically wrap into a horizontal flex-group.
  • rag: Red/Amber/Green traffic light status for system health tracking.
  • harvey: Consulting-grade Harvey Balls. The engine parses values mathematically (0, 25, 50, 75, 100).

Structural Frameworks

Render complex logic diagrams seamlessly natively without HTML manipulation.

Gantt & Timelines

Use timeline for chronological steps, or gantt for complex workstream rendering. Gantt tasks use a 0-based start index corresponding to the length of the timeline array.

Matrices & Flow Diagrams

  • matrix-2x2: Generates a quadrant matrix. quadrants must be an array of 4 objects containing a name and an array of string items.
  • logic-tree: A recursive component that builds hierarchical trees. Nodes contain a title, optional desc, and a children array containing more nodes.
  • chevron-flow: A sequential chevron process diagram supporting active/inactive states.

Data Tables

Tables in CrispDeck are highly intelligent. They automatically scan columns for numeric values and right-align them for proper financial readability.

Harvey Ball Injection: You can seamlessly inject Harvey Balls directly into any table cell using the string token macro: [harvey: 75].

{
    "type": "data-table",
    "headers": ["Feature", "Completion"],
    "rows": [
        ["Authentication", "[harvey: 100] Native"],
        ["Database Sync", "[harvey: 25] Partial"]
    ]
}

Standard D3 Charts

All charts are rendered dynamically using native D3.js. They automatically adopt your brand theme colors and scale flawlessly to fill their designated container zone.

Variants: bar, pie, donut, line, area, waterfall, funnel, treemap.

{
    "type": "chart",
    "variant": "waterfall",
    "title": "Effort Walkdown",
    "data": [
        { "label": "Base Effort", "value": 100, "isTotal": true },
        { "label": "Auto-Layout", "value": -40 },
        { "label": "D3 Charts", "value": -25 },
        { "label": "Final Polish", "value": 35, "isTotal": true }
    ]
}

Advanced D3 Charts

Advanced multi-series charts utilize slightly different data array structures to accommodate deeper data dimensionality.

Stacked Bar

Requires a nested series array for each label grouping.

{
    "type": "chart",
    "variant": "stacked-bar",
    "data": [
        {
            "label": "Q1",
            "series": [
                { "name": "Revenue", "value": 40 },
                { "name": "Costs", "value": 30 }
            ]
        }
    ]
}

Scatter Plot

Requires specific x, y, and r (radius) coordinates for each point.

{
    "type": "chart",
    "variant": "scatter",
    "data": [
        { "x": 10, "y": 85, "r": 20, "label": "Hero" },
        { "x": 40, "y": 90, "r": 15, "label": "Split" }
    ]
}

Radar / Spider Chart

Requires a features array defining the axes, and a data array containing matching values.

{
    "type": "chart",
    "variant": "radar",
    "features": ["Speed", "Design", "Data Viz", "Ease of Use"],
    "data": [
        { "values": [95, 90, 95, 85] }, 
        { "values": [30, 80, 50, 95] }  
    ]
}

Heatmap

Requires xLabels, yLabels, and a two-dimensional data matrix containing the values.

{
    "type": "chart",
    "variant": "heatmap",
    "xLabels": ["Text", "Lists", "Charts"],
    "yLabels": ["Ease", "Power"],
    "data": [
        [10, 8, 2],
        [3, 5, 10]
    ]
}

Geospatial Maps

Maps fetch TopoJSON on the fly and colorize regions based on data values. You can overlay specific location pins using explicit Longitude/Latitude coordinates.

Supported Regions: world, us, europe, asia, africa, middle_east, india, south_america, canada, oceania.

{
    "type": "chart",
    "variant": "map",
    "region": "europe",
    "data": [ 
        { "label": "Germany", "value": 100 }, 
        { "label": "France", "value": 80 } 
    ],
    "pins": [ 
        { "label": "Berlin", "lat": 52.5200, "lng": 13.4050 } 
    ]
}