agentleFS
d-padmanabhand-padmanabhan/agent-engineering-handbook/skills/single-file-dashboard/SKILL.md

single-file-dashboard

Skill17 starsChanged 56 days agoMITView on GitHub

Build single-file zero-dependency interactive HTML dashboards for analytical artifacts that must be emailable, offline-viewable, or airgap-safe. Inline vanilla-SVG charts, no CDN, no remote scripts, no web fonts, no images. Useful for client deliverables, security audit reports, executive dashboards, post-mortems, technology consolidation analyses, and anything that needs to render identically in a browser, an email client, an airgapped laptop, or a regulator review. Use when the user asks for a "dashboard", "interactive HTML report", "shareable analysis", "offline dashboard", "airgap-safe report", or wants to turn an analytical workbook / dataset into a presentable HTML view.

1 other project uses a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

9 rules. About 0 are common, 8 are distinctive, and 1 is very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈8

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  • could be automatic: a setting could enforce it
  1. Distinctive:

    Use when the user asks for:

    line 20
  2. Distinctive:

    Do *not* use for:

    line 28
  3. Distinctive:

    One file. Zero dependencies: No CDN, no remote scripts, no web fonts, no images.

    line 39
  4. Very specific:

    Inline vanilla SVG for charts: No D3, Chart.js, ECharts, or React.

    line 40
  5. Distinctive:

    Escape user content: Every string pulled from data must be HTML-escaped.

    line 41
  6. Distinctive:

    Renders identically offline: Test by disabling network.

    line 42
  7. Distinctive:

    Re-runnable, not editable: Never edit generated HTML.

    line 44
  8. Distinctive:

    Keep interactivity minimal and offline-friendly:

    line 249
  9. Distinctive:

    Do not implement: drag-and-drop, infinite scroll, persistence (localStorage is fine.

    line 257

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Single-File Dashboard

How to ship analytical artifacts as single-file, fully self-contained, zero-dependency HTML dashboards that render identically online, offline, on an airgapped laptop, and inside an email client.

Companion rules:

  • 800-markdown.mdc - markdown formatting for prose deliverables
  • 810-documentation.mdc - documentation patterns
  • 815-reactflow-diagrams.mdc - interactive React Flow canvases (the other dashboard pattern; see contrast below)

When to invoke

Use when the user asks for:

  • "Build a dashboard" / "interactive HTML report" / "shareable analysis"
  • "Offline dashboard" / "airgap-safe" / "for the client" / "for the regulator"
  • "Visualize this workbook" / "turn this CSV into a presentable view"
  • An executive-style deliverable for client / leadership / regulator review
  • Anything that must render without internet access

Do not use for:

  • React applications (use a real frontend stack)
  • Embedded analytics inside a webapp (use a charting library and your build pipeline)
  • Real-time / streaming dashboards (use Grafana / Datadog / Looker)
  • Interactive architecture canvases with custom nodes / edges - use 815-reactflow-diagrams.mdc and the React Flow skill instead

The Six Golden Rules

  1. One file. Zero dependencies. No CDN, no remote scripts, no web fonts, no images. Inlined CSS, inlined JS, inlined data, inlined SVG.
  2. Inline vanilla SVG for charts. No D3, Chart.js, ECharts, or React. Vanilla SVG renders everywhere; libraries do not.
  3. Escape user content. Every string pulled from data must be HTML-escaped. Never bypass.
  4. Renders identically offline. Test by disabling network; the dashboard must look unchanged.
  5. Renders identically in email. Open the file in Gmail / Outlook / Apple Mail (as an attachment); layout must hold.
  6. Re-runnable, not editable. Never edit generated HTML. Edit the generator and re-run.

When to choose this pattern vs alternatives

NeedUse this skillUse React FlowUse real BI toolUse Markdown
Client / executive deliverable; must be emailableyesnonomaybe
Airgap / regulated environmentyespossibly (still SPA)noyes
Interactive architecture canvas with custom nodesnoyesnono
Real-time / streaming datanonoyesno
Analytical workbook -> shareable HTMLyesnomaybeno
Live KPIs across many sourcesnonoyesno
Static narrative documentnononoyes

Architecture

+--------------------------+
| Source artifact          |
| (xlsx / csv / json /     |
|  parquet / api dump)     |
+----------+---------------+
           |
           v
+--------------------------+
| Build script (Python)    |
|  - parse source          |
|  - shape model           |
|  - render HTML shell     |
|  - inline CSS, JS, SVG   |
|  - inline data as JSON   |
|    in <script> tags      |
+----------+---------------+
           |
           v
+--------------------------+
| Single-file HTML output  |
|  - no external requests  |
|  - opens with file://    |
+--------------------------+

The build script is the source of truth. The HTML is the artifact.


Workflow

  1. Locate the source. Excel workbook, CSV, JSON dump, parquet - whatever the upstream produced. If multiple candidates exist, ask the user.
  2. Define the dashboard sections. Typical executive layout:
    • Overview (KPIs + thesis sentence)
    • Key decisions / findings
    • Charts (1-3 per section, never more)
    • Detail tables (sortable / filterable)
    • Risks / next steps
    • Methodology / source links
  3. Run the generator.

    python3 build_dashboard.py --source <input>.xlsx --output <output>.html
  4. Verify offline. Disable network; reload in browser. Anything that breaks (missing font, blank chart) violates the zero-dep guarantee.
  5. Verify in email client. Save and reopen as an attachment in Gmail / Outlook. Layout must hold.
  6. Report back. Summarize counts (sections rendered, items per section, charts), the absolute output path, and a open <path> command for macOS.

Design system (executive palette)

A consistent design system makes outputs presentable without manual adjustment.

Palette

TokenHexUse
navy#051C2CHeadlines, top rule, hero
slate#54758CSecondary text, axis labels
line#E1E6EBBorders, gridlines
accent blue#2251FFPrimary action, links, key bars
positive green#0F6E3EPositive deltas, "what worked" callouts, retain decisions
negative red#B33A3ANegative deltas, "horror story" callouts, decommission decisions
warning amber#C18100Cautionary emphasis, contested decisions
neutral grey#6B7280Disabled / N/A states

Typography

  • System sans only (Inter / SF / Segoe UI / Helvetica). No web fonts.
  • Display 44px / Headline 28px / Subhead 20px / Body 15px / Caption 12px.
  • All-caps 11px eyebrows with wide letter-spacing for section labels.
  • Tabular nums for all numeric tables.

Layout

  • 1320px container, 56px gutters, 72px band padding.
  • Top sticky nav (anchor links to sections).
  • Horizontal-rule section dividers (no boxed cards everywhere).
  • Tables: navy top rule, sticky headers, hover row highlight, no vertical gridlines.

Chips & callouts

Standard chip styles for repeated semantics (decisions, severities, deltas, advantages):

<span class="chip chip--retain">RETAIN</span>
<span class="chip chip--phase-out">PHASE OUT</span>
<span class="chip chip--severity-high">HIGH</span>
<span class="chip chip--shift-up">+0.42 vs balanced</span>

Green / amber / red callouts for evidence pairs (e.g., what-worked / horror-story from the multi-perspective-review skill).


Charts (vanilla SVG)

The discipline that distinguishes this skill from "throw in a Chart.js script tag". Implement a small set of charts in pure SVG so the output never reaches for the network.

Chart inventory (cover 90% of executive dashboards)

ChartUse
Horizontal barDecision mix, score comparison, ranked items
Vertical barPeriod comparison, category counts
Multi-series lineKPIs over time, success metrics
GanttRoadmap, phase plan
Stacked barComposition over time, mix shifts
Donut / pieAvoid in executive dashboards; use horizontal bar instead

Skeleton

// Vanilla SVG bar chart helper - inline in the dashboard, no imports.
function svgEl(name, attrs) {
  const el = document.createElementNS('http://www.w3.org/2000/svg', name);
  for (const [k, v] of Object.entries(attrs)) el.setAttribute(k, v);
  return el;
}

function renderHbar(containerId, data, opts = {}) {
  const c = document.getElementById(containerId);
  const w = c.clientWidth, barH = opts.barH ?? 24, pad = 12;
  const max = Math.max(...data.map(d => d.value));
  const labelW = opts.labelW ?? 200;
  const svg = svgEl('svg', { width: w, height: data.length * (barH + pad) + pad });
  data.forEach((d, i) => {
    const y = pad + i * (barH + pad);
    svg.appendChild(svgEl('text', {
      x: 0, y: y + barH * 0.7, fill: 'var(--slate)',
      'font-size': '13', 'font-family': 'inherit',
    })).textContent = d.label;
    const barW = ((w - labelW - 60) * d.value) / max;
    svg.appendChild(svgEl('rect', {
      x: labelW, y, width: barW, height: barH,
      fill: opts.fill ?? 'var(--accent-blue)',
    }));
    svg.appendChild(svgEl('text', {
      x: labelW + barW + 6, y: y + barH * 0.7, fill: 'var(--navy)',
      'font-size': '13', 'font-family': 'inherit',
      'font-variant-numeric': 'tabular-nums',
    })).textContent = d.value.toLocaleString();
  });
  c.appendChild(svg);
}

window.addEventListener('resize', () => {
  document.querySelectorAll('[data-chart]').forEach(el => {
    el.innerHTML = '';
    renderChart(el.id);  // user-supplied dispatcher
  });
});

Embedding the data

Inline as JSON in <script type="application/json"> tags. Parse on init.

<script type="application/json" id="decisionMixData">
  [
    {"label": "Retain", "value": 7},
    {"label": "Retain (Scoped)", "value": 3},
    {"label": "Phase out", "value": 2},
    {"label": "Decommission", "value": 1}
  ]
</script>

<div id="decisionMixChart" data-chart="hbar"></div>

<script>
  const data = JSON.parse(document.getElementById('decisionMixData').textContent);
  renderHbar('decisionMixChart', data);
</script>

The chart renders from JSON in the same file. No fetch. No network.


Tooltips and interactions

Keep interactivity minimal and offline-friendly:

  • Hover tooltip - one shared dark tooltip (custom div positioned at cursor); no library.
  • Click filters - filter chips that show/hide rows in tables; pure CSS classes toggled with one event handler.
  • Sortable tables - small <th> click handler that re-renders rows from the inlined JSON.
  • Section nav - anchor links with smooth-scroll behavior via CSS.
  • No router - URL fragments only.

Do not implement: drag-and-drop, infinite scroll, persistence (localStorage is fine; no backend).


Build script structure (Python)

# build_dashboard.py
from pathlib import Path
import argparse
import html
import json

PALETTE = {
    "navy": "#051C2C", "slate": "#54758C", "line": "#E1E6EB",
    "accent_blue": "#2251FF", "positive": "#0F6E3E",
    "negative": "#B33A3A", "warning": "#C18100", "neutral": "#6B7280",
}

HTML_SHELL = """<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>__TITLE__</title>
<style>
:root {
  --navy: __NAVY__;
  --slate: __SLATE__;
  /* ... */
}
body { margin: 0; font-family: Inter, system-ui, sans-serif; color: var(--navy); }
/* ... full CSS inlined here ... */
</style>
</head>
<body>
<nav class="topnav">__NAV__</nav>
<main>
  __OVERVIEW__
  __DECISIONS__
  __CHARTS__
  __DETAILS__
  __RATIONALE__
</main>
<script>
__JS__
</script>
</body>
</html>
"""

def render(source: Path, output: Path) -> None:
    data = parse_source(source)
    body = HTML_SHELL
    body = body.replace("__TITLE__", html.escape(data["title"]))
    for k, v in PALETTE.items():
        body = body.replace(f"__{k.upper()}__", v)
    body = body.replace("__OVERVIEW__", render_overview(data))
    body = body.replace("__DECISIONS__", render_decisions(data))
    body = body.replace("__CHARTS__", render_charts(data))
    body = body.replace("__JS__", JS_HELPERS)
    output.write_text(body, encoding="utf-8")

def render_overview(data):
    # All user content goes through html.escape()
    thesis = html.escape(data["thesis"])
    return f'<section><h1>{thesis}</h1></section>'

JS_HELPERS = """
function svgEl(name, attrs) { /* ... */ }
function renderHbar(id, data, opts) { /* ... */ }
"""

if __name__ == "__main__":
    p = argparse.ArgumentParser()
    p.add_argument("--source", required=True, type=Path)
    p.add_argument("--output", type=Path)
    a = p.parse_args()
    render(a.source, a.output or a.source.with_suffix(".html"))

The script writes a single string to disk. Test it by opening the output in a browser with the network disabled.


Anti-patterns

  1. Linking to a CDN for any asset. The dashboard breaks on airgap and in email; the guarantee is gone.
  2. Web fonts (Google Fonts, Adobe Fonts). Falls back to system fonts in email anyway; just use system fonts.
  3. External images (logos hosted on a CDN). Either inline as base64 SVG or omit.
  4. Charting library (D3, Chart.js, ECharts, Plotly). Each adds 100-500KB and requires either a bundler or a CDN. Vanilla SVG is enough for executive charts.
  5. Generated HTML edited in place. Make changes in the generator and re-run; otherwise the artifact and the source diverge.
  6. No HTML escaping. A user-supplied product name with <script> in it becomes XSS in the dashboard.
  7. Donut / 3D / sunburst charts. Hard to read at executive density. Use bar charts.
  8. More than 3 charts in a single section. Cognitive load over signal.
  9. Localizing into the HTML ("English en", "French fr") - use one language per output; multiple outputs for multiple languages.

Testing checklist

  • Open with network disabled; everything renders
  • Open in Gmail / Outlook attachment view; layout holds
  • Open in Safari / Chrome / Firefox / Edge; layout holds
  • User-supplied strings with <, >, &, ", ' render as text, not HTML
  • Charts re-render correctly on window resize
  • Sticky nav remains usable when scrolling
  • Sortable tables sort correctly on every column
  • Filter chips show/hide expected rows
  • Print preview (Cmd-P) produces a usable PDF
  • File size under 2 MB (data + chrome). If over, paginate or shrink the data.

Distribution

  • Email - attach. The recipient opens locally; no server.
  • Shared drive - drop the HTML next to the source workbook.
  • Wiki / SharePoint - upload as an attachment, not as a page (preserves the single-file guarantee).
  • GitHub Pages - works, but the airgap-safe property is moot; consider a real SPA if you need GitHub Pages anyway.
  • Pinning a snapshot - rename to <analysis>-<YYYYMMDD>.html so the artifact is self-dating.

Related

  • Rule: 800-markdown.mdc - markdown formatting for non-HTML deliverables
  • Rule: 810-documentation.mdc - documentation patterns
  • Rule: 815-reactflow-diagrams.mdc - interactive React Flow canvases (different niche; SPA, not single-file)
  • Skill: multi-perspective-review - panel review whose output renders cleanly into the Advisory section of this dashboard
  • Skill: documentation-standards - companion patterns for narrative documentation

Attribution

Pattern crystallized from an executive-style consolidation dashboard generator that produces single-file HTML output (no CDN, no fonts, no images, inline vanilla SVG) sized at <2 MB and renders identically in Chrome, Outlook, and on an airgapped laptop. The single-file/zero-dependency guarantee is the feature.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.