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 contents — index.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.
PATCHthe 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-scriptsand noallow-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.htmlon a machine that may refuse to open it — and mail gateways strip archives containing.jsmore 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.