How to share a folder of HTML files

Plenty of tools do not produce an HTML file. They produce a directory:

coverage/
├── index.html
├── assets/
│   ├── style.css
│   └── app.js
├── img/
│   └── chart.png
└── src/
    └── parser.py.html

Jest and pytest coverage, Allure, Sphinx, MkDocs, Storybook, JaCoCo, Lighthouse, profiling output — nearly all of them. Every one of those files matters, and every relative link between them assumes they stay neighbours.

Send index.html on its own and the reader gets grey text on white.

Why one file is not enough

The browser resolves ./assets/style.css relative to wherever index.html currently is. Move the file alone into an email attachment, a chat upload, or a cloud-storage preview, and those neighbours no longer exist. The page loads — it just loads without its styling, its charts and its sub-pages. The longer diagnosis is in why a shared HTML report has no CSS.

Sub-pages make it worse: a coverage report's per-file drilldown is dozens of linked pages. There is no "just send the important one."

Option 1 — collapse it into a single file

If the generator can inline its own resources, take that. One file travels anywhere:

Tool Flag
nbconvert --to html --embed-images
Quarto -M embed-resources:true
R Markdown self_contained: true (the default)
pandoc --embed-resources --standalone
Plotly write_html(..., include_plotlyjs="inline")

Not every tool offers it. Coverage reports and docs sites are genuinely multi-page and cannot be flattened.

Option 2 — share the bundle

Zip the directory contentsindex.html at the archive root — and publish the zip. Comma serves bundles from one origin, so relative paths, sub-pages and assets behave exactly as they did locally.

cd coverage && zip -qr ../coverage.zip . && cd ..

curl -X POST https://commareports.com/api/v1/reports \
  -H "Authorization: Bearer $COMMA_API_TOKEN" \
  -F "title=Coverage — main @ $(git rev-parse --short HEAD)" \
  -F "visibility=team" \
  -F "bundle=@coverage.zip"

Or drag the zip into /new if you would rather not use a terminal.

Getting the zip right. cd into the directory before zipping. Zipping from the parent (zip -r coverage.zip coverage/) nests everything one level deeper, so there is no index.html at the root and the entry point 404s. If your archive has a wrapper folder, that is the bug.

What the bundle gets you

  • Every link works. Drilldowns, per-file pages, search indexes and fonts resolve against one origin.
  • Access per report. Private, team, your email domain, any signed-in user, or an unlisted link — decided per bundle, not per bucket. See sharing and access control.
  • Comments on the rendered page. A reviewer selects the uncovered branch or the failing test row and leaves a thread anchored to it. See commenting on HTML.
  • Revisions at one URL. PATCH the same id from the next CI run; the link in your README never changes and any two runs are diffable. See publishing from CI.
  • Sandboxed execution. Bundles render with allow-scripts and no allow-same-origin, so interactive output runs without reaching anyone's session.

What not to do

  • Emailing the zip. The recipient must download, unzip and find index.html on a machine that may refuse to open it — and mail gateways strip archives containing .js more often than you would expect.
  • Cloud storage previews. Drive, Dropbox, Box and SharePoint all store the folder and none of them render it. See Dropbox HTML preview and Box HTML preview.
  • Screenshotting the summary. It loses the drilldown, which is the only part anyone needed. See stop screenshotting reports.

Related