Html Report logo

Html Report

OrganizationPopular
ginlix-ai
html-report

Self-contained styled HTML reports written to the task directory: PDF-exportable research documents with inline data, charts, and theme-aware CSS

Overview

Publisherginlix-ai
RepositoryLangAlpha
Skill namehtml-report
Stars
1.8K
Forks
288
Bundled files
Instructions only
LicenseApache-2.0
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by ginlix-ai on GitHub. Read the source before you install it.

Installation

Install the Html Report AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/ginlix-ai/LangAlpha.git /tmp/LangAlpha
mkdir -p .claude/skills
cp -r /tmp/LangAlpha/plugins/langalpha_deliverables/skills/html-report .claude/skills/html-report
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Html Report in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Html Report on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Html Report is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

HTML Report

Author a styled, self-contained HTML document and write it into the task directory (e.g. work/amd_analysis/report.html). The file panel renders it with full browser semantics — JavaScript runs, CDN libraries load, relative assets resolve, and the user can view it fullscreen, open it in a new tab, download it, or export it to PDF.

This is the right output when the user wants a deliverable they can keep, share, or print — an equity research note, an earnings recap, a screen writeup — not a throwaway answer.

Read .agents/skills/ui-design/SKILL.md before authoring. It defines the typography, color, and composition standards that keep the report looking like a research desk artifact rather than a generic AI page. This skill covers the mechanics; that one covers the taste.

User preferences override these defaults. Anything the user has told you — in this conversation, in your long-term memory, or in their saved preferences/memos — outranks every rule in this skill. If they want a different structure, no charts, a specific set of sections, or a particular file layout, do that. (Visual taste — fonts, color, accent, light/dark — is .agents/skills/ui-design/SKILL.md's domain; that skill defers to the user's stated style.) Treat the rules here as sensible defaults for when the user hasn't specified.

Decide: Which Output?

A report from this skill can be interactive (sortable tables, tab/filter controls, hover- and zoomable charts — see Interactivity, below). So interactivity is not what separates it from a dashboard. The real divide is self-contained snapshot file vs. live served app:

WantUseWhy
A document the user keeps, shares, or exports to PDF — even one that's interactive within itselfhtml-report (this skill) — .html in work/<task_name>/One file on disk, served with real semantics, PDF-exportable. Interactivity runs client-side over an embedded data snapshot.
A quick visualization inside the chat (one chart, a metric row, a table)inline-widget (ShowWidget)Appears inline between text; no file, no panel
A live served app — refreshing data, server-side compute, multi-page routing, or a dataset too large to embedinteractive-dashboard (GetPreviewUrl)A running app with a backend, not a static file. Needed when the data must be fetched live, not embedded.
A simple, short answerplain markdownA styled HTML document is overkill for a one-paragraph reply

Self-Contained by Default

Write one complete HTML file. Everything inline — no external CSS/JS files, no build step.

python
import json

data = {"labels": ["Q1", "Q2", "Q3", "Q4"], "revenue": [2.1, 2.4, 2.6, 3.0]}

html = f"""<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Acme Q4 Revenue Review</title>
  <style>/* all CSS inline here */</style>
</head>
<body>
  <main>...</main>
  <script>const DATA = {json.dumps(data, ensure_ascii=False)};</script>
  <script>/* render charts from DATA */</script>
</body>
</html>"""

with open("work/<task_name>/report.html", "w", encoding="utf-8") as f:
    f.write(html)

Rules:

  • Full <!DOCTYPE html> document with <head>/<body> (unlike inline-widget, which is a bare fragment).
  • All CSS in a <style> block, all JS in <script> blocks — nothing external except allowlisted CDN libraries.
  • Embed data via <script>const DATA = {json.dumps(data, ensure_ascii=False)};</script> — never inline raw Python dicts, never fetch() a local file. ensure_ascii=False keeps non-ASCII (names, currencies, CJK) readable and correctly encoded.
  • Sample or aggregate large datasets before embedding. A report doesn't need every tick — downsample to a sensible resolution, aggregate to the reporting period. Keep the embedded payload lean (target well under ~1MB).

Multi-File When Warranted

The viewer serves files with real relative-path semantics, so a report can reference sibling assets and they resolve correctly:

work/amd_analysis/
  report.html              # references charts/revenue.png as a relative path
  charts/
    revenue.png
    margins.png

The charts are already there: work/<task_name>/charts/ is where you saved them during the analysis, and the report sits beside that directory. Nothing is copied.

html
<img src="charts/revenue.png" alt="Quarterly revenue" style="width:100%;max-width:720px;">

Use multi-file for image-heavy reports — e.g. when you've generated high-quality static charts with matplotlib/plotly savefig and want to embed them rather than redraw client-side.

Rules:

  • Keep all asset paths relative (charts/revenue.png, never a leading slash, never an absolute filesystem path).
  • Keep every asset inside the workspace and under the report's own task directory. Do not reference files outside the workspace.
  • Prefer self-contained when the charts can reasonably be drawn client-side from embedded DATA; reach for multi-file when raster images give materially better output.

CDN Allowlist

Only these origins are reachable from the rendered document. Anything else (including arbitrary fetch()) is blocked.

  • cdnjs.cloudflare.com
  • cdn.jsdelivr.net
  • unpkg.com
  • esm.sh
  • Google Fonts: fonts.googleapis.com + fonts.gstatic.com

Load chart libraries, fonts, and helpers from these only. Do not call out to data APIs from the document — embed the data instead.

Theme Variables (Defensive Fallback Form)

The viewer can inject app --color-* variables so the report themes with light/dark mode. Always author colors in the fallback form so the document also renders correctly standalone, in a downloaded file, and in print:

css
color: var(--color-text-primary, var(--fb-text-primary));
background: var(--color-bg-card, var(--fb-bg-card));
border: 1px solid var(--color-border-muted, var(--fb-border-muted));

The fallback is what shows when no app vars are injected (downloaded file, PDF, plain open). Point it at a private --fb-* variable from the block below so a standalone document follows the reader's OS light/dark preference instead of guessing one theme. Never write a bare var(--color-x) without a fallback, and never hardcode a color with no variable — both break one of the surfaces.

Define the fallback palette once at the top of your <style>. The private --fb-* names can never collide with — or override — the injected app tokens:

css
:root {
  --fb-bg-page: #fbfaf8; --fb-bg-card: #ffffff; --fb-bg-elevated: #ffffff;
  --fb-bg-subtle: #f4f2ee; --fb-bg-hover: #efece7;
  --fb-text-primary: #1a1a1a; --fb-text-secondary: #5a5a5a; --fb-text-tertiary: #8a8a8a;
  --fb-border-muted: #e4e1dc; --fb-accent-primary: #1f5fb4;
  --fb-profit: #1a7f4f; --fb-loss: #b42318; --fb-warning: #b7791f;
  --fb-info: #1f5fb4; --fb-success: #1a7f4f;
}
@media (prefers-color-scheme: dark) {
  :root {
    --fb-bg-page: #0f1117; --fb-bg-card: #1a1d27; --fb-bg-elevated: #20242f;
    --fb-bg-subtle: #161a24; --fb-bg-hover: #252a36;
    --fb-text-primary: #e8e8e8; --fb-text-secondary: #9ca3af; --fb-text-tertiary: #6b7280;
    --fb-border-muted: #262a33; --fb-accent-primary: #5b9bff;
    --fb-profit: #3fb37a; --fb-loss: #f0685a; --fb-warning: #d69e2e;
    --fb-info: #5b9bff; --fb-success: #3fb37a;
  }
}

Reuse the same variable names as the inline-widget skill:

VariablePurposeLight --fb-* value
--color-bg-pagePage background#fbfaf8
--color-bg-cardCard/panel background#ffffff
--color-bg-elevatedElevated surface#ffffff
--color-bg-subtleSubtle/muted background#f4f2ee
--color-bg-hoverHover state background#efece7
--color-text-primaryPrimary text#1a1a1a
--color-text-secondarySecondary/muted text#5a5a5a
--color-text-tertiaryHint/label text#8a8a8a
--color-border-mutedDefault border (hairline)#e4e1dc
--color-accent-primaryBrand/accent color#1f5fb4
--color-profitPositive/gain (green)#1a7f4f
--color-lossNegative/loss (red)#b42318
--color-warningWarning (amber)#b7791f
--color-infoInfo (blue)#1f5fb4
--color-successSuccess (green)#1a7f4f

Charts

Load Chart.js or ECharts from CDN. Canvas pixels cannot read CSS variables, so resolve colors via getComputedStyle with a fallback chain for the standalone/print case:

html
<div style="position: relative; height: 320px;">
  <canvas id="revChart"></canvas>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4/dist/chart.umd.min.js"></script>
<script>
  var cs = getComputedStyle(document.documentElement);
  // App token if injected, else the OS-adaptive --fb-* value, else the literal.
  function pick(name, fallback) {
    var v = cs.getPropertyValue(name).trim()
      || cs.getPropertyValue(name.replace('--color-', '--fb-')).trim();
    return v || fallback;
  }
  var accent = pick('--color-accent-primary', '#1f5fb4');
  var border = pick('--color-border-muted', '#e4e1dc');

  new Chart(document.getElementById('revChart'), {
    type: 'line',
    data: { labels: DATA.labels, datasets: [{ data: DATA.revenue, borderColor: accent, backgroundColor: accent + '22', tension: 0.3, fill: true }] },
    options: {
      responsive: true,
      maintainAspectRatio: false,
      animation: { duration: 400 },
      scales: { y: { grid: { color: border } }, x: { grid: { display: false } } }
    }
  });
</script>

Rules:

  • Set height on the wrapper <div>, never on the <canvas>.
  • responsive: true, maintainAspectRatio: false always.
  • Resolve canvas colors with getComputedStyle + fallback chain (the pick() helper above) — never bare var() in canvas color strings.
  • Use UMD CDN builds (set the library global).
  • For categorical series, follow the restrained palette in .agents/skills/ui-design/SKILL.md — no rainbow defaults.

Interactivity (When It Helps)

The served document runs JavaScript, so a report can be interactive — and should be when interactivity genuinely helps the reader explore the data, not as decoration. All of it runs client-side over the embedded DATA snapshot; there is no server and no live refresh (that's interactive-dashboard).

Reach for interactivity when it earns its place:

  • Sortable / filterable tables — let the reader sort a holdings table by weight or P&L, or filter to a sector. Pays off most on tables past ~15 rows.
  • Tabbed or accordion sections — segment a long report (Summary / Financials / Valuation / Risks) so the reader isn't scrolling past everything.
  • Interactive charts — Chart.js/ECharts hover tooltips, series toggles (click a legend entry to hide a line), range zoom on a long price history.
  • Collapsible detail / "show more" — keep the default view tight; let the curious expand methodology, footnotes, or a raw-numbers table.
  • In-page search / highlight — for a long screen or a wide comparison.

Rules:

  • Wire events with addEventListener, not inline onclick= / on*= attributes. It is the robust pattern across every surface and keeps logic out of the markup.
  • Client-side only. Operate on the embedded DATA; never fetch() a data API (the CDN allowlist blocks it). If the data must be live or is too big to embed, that's a dashboard, not a report.
  • Default state must be meaningful. The report has to read correctly before any click — a reader (or a PDF export) that never interacts must still see the substance. Never hide the headline finding behind a tab.
  • Degrade for print. Interactive controls (tab bars, filter inputs, sort buttons, "show more" toggles) are chrome — give them .no-print, and make collapsed content render expanded when printing so the PDF is complete. The @media print block below already hides button / .no-print.
  • Keep it self-contained and lean. Vanilla JS over the embedded data; no framework, no build step. A little event delegation goes a long way.

Match the effort to the data: a one-number recap needs no interactivity; a 40-holding portfolio or a multi-section deep-dive benefits a lot.

Print / PDF

PDF export = the browser's print-to-PDF. Include an @media print block — without it, PDFs come out degraded. It should:

css
@media print {
  /* hide interactive chrome — buttons, toolbars, nav, anything not part of the document */
  .no-print, button, nav, .toolbar { display: none !important; }

  /* keep logical blocks from splitting across pages */
  section, figure, table, .card, .kpi { break-inside: avoid; page-break-inside: avoid; }
  h1, h2, h3 { break-after: avoid; }

  /* sane page setup */
  @page { margin: 18mm 16mm; }
  body { background: #fff; color: #000; }

  /* never let entrance animations leave content invisible in the PDF */
  *, *::before, *::after { animation: none !important; transition: none !important; opacity: 1 !important; }

  /* collapse side-by-side layouts — paper is ~816px wide; squeezed columns
     overlap charts and crush prose */
  .row, .grid, .columns { display: block !important; }
  .row > *, .grid > *, .columns > * { width: 100% !important; max-width: 100% !important; }
}

If any element starts at opacity: 0 for an entrance animation, the opacity: 1 !important rule above is what stops the PDF from exporting blank — keep it. Test the print path before declaring done.

Multi-column layouts print badly. Print width is ~816 CSS px — a flex/grid row pairing a chart card with a text column does not fit and will overlap or crush. Either keep the document single-column throughout (safest for a report), or include print rules like the collapse block above for every side-by-side container you create. Chart wrappers keep their fixed height either way.

Landscape documents must declare it. If the content is genuinely wide (a comparison matrix, a wide timeline, a dashboard-style sheet), declare the orientation in the print block — PDF export honors it and lays the page out at landscape width (~1056 CSS px), so charts and columns size for the real paper:

css
@page { size: letter landscape; margin: 14mm 16mm; }

Named sizes (a4, legal, ...) with optional landscape work too. Without a declaration, export is portrait Letter — don't design landscape-wide content and skip the declaration.

Print typography. Screen sizing usually reads too large on paper. Inside @media print, set print-affecting sizes in pt and tighten slightly:

css
@media print {
  body { font-size: 10.5pt; line-height: 1.45; }
  h1 { font-size: 17pt; }  h2 { font-size: 13pt; }  h3 { font-size: 11pt; }
  .card, section { padding: 10pt 12pt; }
}

Aim for 10–11pt body text — the register of a printed research note. Keep table cell padding compact (4pt 8pt) so wide tables fit. Page margins come from @page { margin: ... }, not body padding.

Authoring Workflow

  1. Fetch and validate data first (check for empty/None); sample or aggregate to a sensible size.
  2. Read .agents/skills/ui-design/SKILL.md and commit to a typographic pairing + color direction.
  3. Build the full document — inline CSS/JS, embed DATA, draw charts from it; add the @media print block.
  4. Write to work/<task_name>/report.html (UTF-8). Image-heavy → reference the charts already in work/<task_name>/charts/*.png relatively as charts/*.png.
  5. Open it and print-preview, then cite the report to the user as a clickable link.

Use the Quality Checklist below to verify before delivering.

Quality Checklist

  • Full <!DOCTYPE html> document; CSS and JS inline; only allowlisted CDNs referenced
  • Data embedded via <script>const DATA = {json.dumps(..., ensure_ascii=False)}</script>; large datasets sampled/aggregated
  • Multi-file (if used): all asset paths relative, all assets under the report's task directory
  • Every color in var(--color-role, #literalFallback) form — no bare var(), no unvariabled hardcodes
  • Charts: wrapper-div heights, maintainAspectRatio: false, getComputedStyle + literal fallback for canvas colors
  • @media print block present: hides chrome, break-inside: avoid, sane @page margins, animations/opacity neutralized
  • Interactivity (if any): events via addEventListener (no inline on*=), runs on embedded DATA (no live fetch), default state is meaningful, controls .no-print and collapsed content expands when printing
  • User's stated preferences (this chat / long-term memory / saved prefs) honored wherever they differ from this skill's defaults
  • Design follows .agents/skills/ui-design/SKILL.md (typography, single accent, profit/loss color discipline, no AI slop)
  • Written to work/<task_name>/; numbers correctly formatted; opened and print-previewed; cited to the user as a link

Frequently asked questions

What does the Html Report AI skill do?

Self-contained styled HTML reports written to the task directory: PDF-exportable research documents with inline data, charts, and theme-aware CSS

Why use Html Report on TypingMind?

Because you install it once and use it with any model. Html Report is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Html Report in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/ginlix-ai/LangAlpha/tree/main/plugins/langalpha_deliverables/skills/html-report. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Html Report?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Html Report?

As many as you like. As long as a model supports skills, you can use Html Report with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Html Report AI skill free?

Yes. It is published on GitHub by ginlix-ai under the Apache-2.0 license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇