Documentation

Build Your Deck

Everything you need to create, test, and publish an ExouDeck dashboard. From starter kit to marketplace in minutes.

Quick Start

Download the starter kit, open it in your browser, and you have a working deck. No build tools, no dependencies, no server required.

Three steps to a working deck:

  1. 1
    UnzipExtract the starter kit to any folder
  2. 2
    OpenOpen index.html in a browser — the example widgets render immediately with demo data
  3. 3
    CustomizeEdit deck.json to change the name and theme, add widgets to the widgets/ folder

Deck Structure

A deck is a flat folder (zipped for upload) containing these files. Only deck.json and your widgets are required — everything else is auto-injected on upload if missing.

Files
index.htmlEntry point — loads the runtime and themeNo
deck-runtime.jsEngine — layout, polling, widgets, license, hot-reloadNo
deck.jsonConfig — API endpoint, theme, layout, widget registryYes
theme.cssVisual theme using CSS custom propertiesYes
sample-data.jsonDemo data so the deck works offlineYes
widgets/*.htmlOne self-contained file per widgetCreate new
AGENT_BRIEF.mdStructured spec for AI agentsNo

Auto-injection: When you upload a zip with just deck.json and your widgets/ folder, the platform automatically adds the runtime, index.html, default theme, sample data scaffold, and agent brief.

Configuration — deck.json

This is the control file. It tells the runtime how to lay out widgets, where to fetch data, and how to style the dashboard.

jsonc
{
  "exoudeck_version": "1.0",
  "name": "My Dashboard",
  "description": "What this deck does",
  "api": {
    "base_url": "http://localhost:8800",
    "poll_interval_ms": 10000,
    "endpoints": { "dashboard": "/dashboard" },
    "demo_mode": true
  },
  "theme": {
    "file": "theme.css",
    "variables": {
      "--accent": "#f3bd7d",
      "--bg-0": "#050608",
      "--text": "#f4efe7"
    }
  },
  "layout": {
    "columns": 12,
    "widgets": [
      {
        "id": "my-widget",
        "widget": "my-widget",
        "position": { "col": 1, "span": 6, "row": 1 },
        "config": {}
      }
    ]
  },
  "widget_registry": {
    "my-widget": {
      "file": "widgets/my-widget.html",
      "name": "My Widget",
      "data_path": "my_key"
    }
  }
}

Key fields

api.demo_modeWhen true, loads sample-data.json instead of polling an API. Set to false for production.
api.base_urlThe root URL of your data API. Only used when demo_mode is false.
layout.widgets[].positionGrid placement — col (1-12), span (width), row (visual grouping).
widget_registry[].data_pathKey in the API response to extract and pass to this widget.

Creating Widgets

Each widget is a single .html file in the widgets/ folder. It runs inside a Shadow DOM — fully isolated styles, no conflicts.

html
<style>
  .card {
    padding: 18px;
    min-height: 160px;
    border: 1px solid var(--stroke);
    border-radius: var(--radius);
    background: var(--glass);
    color: var(--text);
    font-family: "Plus Jakarta Sans", system-ui, sans-serif;
  }
  h3 { margin: 0 0 14px; font-family: "Space Grotesk", sans-serif; }
</style>

<div class="card" data-widget="my-widget">
  <h3>My Widget</h3>
  <div id="value">Loading...</div>
</div>

<script>
(function() {
  // Init: called once when widget loads
  window['__exoudeck_my-widget'] = function(shadow, config) {
    // setup canvas, bindings, etc.
  };

  // Data: called every time new data arrives
  var host = document.currentScript.getRootNode().host;
  if (host) {
    host.addEventListener('deck-data', function(e) {
      var data = e.detail;
      if (!data) return;
      var el = host.shadowRoot.getElementById('value');
      if (el) el.textContent = data.count;
    });
  }
})();
</script>

How it works

1. RegistrationAdd the widget to widget_registry in deck.json with a file path and data_path.
2. InitializationThe runtime calls window['__exoudeck_' + widgetId](shadow, config) once after loading.
3. Data updatesThe runtime dispatches a deck-data CustomEvent on the host element each poll cycle. The event's detail contains the data extracted via data_path.

Tip: The starter kit includes two example widgets — a metric card and an animated chart. Use them as templates for your own.

Sample Data

When demo_mode is true in deck.json, the runtime loads sample-data.json instead of hitting an API. This means your deck works offline — open index.html and everything renders.

json
{
  "budget": {
    "daily_budget_usd": 5.00,
    "spent_today_usd": 3.42,
    "remaining_today_usd": 1.58,
    "calls_today": 127,
    "tokens_today": 184200
  },
  "jobs": {
    "total": 45,
    "active": 3,
    "stalled": 0,
    "failed": 1,
    "completed": 41
  },
  "status": {
    "focus": "System operational",
    "state": "Healthy"
  }
}

Structure rules

  • Top-level keys must match the data_path values in your widget registry
  • Each widget gets passed only its own slice — if data_path is "budget", it receives the budget object
  • Use realistic values so the deck looks good in previews and during review

Theming

Themes are driven by CSS custom properties. Set them in deck.json → theme.variables and the runtime applies them to the document root. Widgets inherit them automatically.

Available variables
--bg-0Page background
--bg-1Card background
--glassGlass fill
--strokeBorder color
--textPrimary text
--mutedSecondary text
--accentPrimary accent
--accent-2Secondary accent
--okSuccess / healthy
--warnWarning
--critCritical / error
--radiusBorder radius

Tip: Use CSS var(--accent, #f3bd7d) with fallbacks inside your widgets so they still render if theme variables are missing.

Uploading Your Deck

When your deck is ready, zip the folder and upload it through the creator dashboard. The platform validates, fills in any missing boilerplate, and queues it for review.

What happens on upload

  1. 1
    ValidationThe zip is parsed and checked for a valid deck.json with matching widget files.
  2. 2
    Auto-injectionMissing standard files (runtime, index.html, theme, sample-data scaffold, agent brief) are injected automatically.
  3. 3
    Re-validationThe completed deck is validated again to ensure everything is correct.
  4. 4
    StorageThe final zip is stored and linked to your product listing.
  5. 5
    Review queueYour product status changes to Pending — the admin team reviews it before it goes live.

Minimum upload: You only need a deck.json and your widgets/ folder. Everything else is added for you. Maximum file size is 10MB.