--- name: html-authoring description: "**[REQUIRED]** for ALL creation, updating, or editing of `.html` files, regardless of complexity. Snowflake renders report HTML in a strict, sandboxed environment: no inline event handlers, no `eval`, no runtime network calls, no remote images or CDN scripts — only a fixed set of vendored libraries served from `/libs/`. Author every report to these rules so it renders correctly and is safe to share. Must use whenever generating, creating, updating, or modifying an `.html` file (e.g. 'update the HTML report at …')." --- # HTML Authoring When report sharing is enabled, Snowflake renders your report HTML in a **locked-down sandbox**: a strict Content-Security-Policy applies and the page has **no network access**. Author to the rules below so the report renders correctly and is safe to share — plain, self-contained HTML with inline JavaScript and all data embedded is ideal. --- ## Where to save the file **Never leave the file in transient storage** — a temporary or scratch location (e.g. `/tmp`) is wiped when the session ends, leaving nothing to publish or refresh later. The file must land somewhere durable. **Creating a new report:** save it to the **current workspace** by default. This skill can also run outside a workspace (e.g. from the Cowork page), and the user may name a destination of their own — use it when it's durable, otherwise ask. If no durable destination is clear, ask before writing. **Updating an existing report:** write it back to the same location. --- ## Marker meta tag Include this in `` so downstream tooling knows the file is agent-authored: ```html ``` --- ## Report metadata — for later refresh Embed one provenance block in `` so a future run (yours or another agent's) can **refresh or iterate** on the report — its equivalent of a header doc-comment in code. Record where the data came from and how each part was produced. It's a ` ``` - **Key each `sections[]` entry to the section's anchor id** (`

` or `
`). The id — not the block's position — is the link, so "refresh section X" is a lookup on `sections[].id`, and the pipeline can extract the whole block without losing which entry maps to which section. - Scope each source: put it in a section's `dataSources` if it feeds only that section, or in the top-level `dataSources` if it's shared across sections — use either or both. (The template below is section-level only because each source is section-specific.) - Keep it machine-readable: list every query / table / file each part draws on, plus any parameters or assumptions a future update needs. - Update it whenever you change what the report shows. - Tokens in `{…}` (e.g. `{WAREHOUSE}`, `{DATABASE}.{SCHEMA}.{TABLE}`) are placeholders — replace them with the report's real warehouse and fully-qualified sources. --- ## What you can and can't use **Use freely:** - ✅ Inline ` ``` | Library | Reference path | Use / limits | |---|---|---| | chart.js | `/libs/chart.js@4.4.4/chart.umd.js` | Charts (renders to ``) | | plotly (cartesian) | `/libs/plotly.js-cartesian-dist-min@2.35.2/plotly-cartesian.min.js` | Cartesian charts only | | d3 | `/libs/d3@7.9.0/d3.min.js` | Low-level data viz | | vega | `/libs/vega@6.2.0/vega.min.js` | Base for vega-lite / vega-embed | | vega-lite | `/libs/vega-lite@6.4.3/vega-lite.min.js` | Declarative charts | | vega-embed | `/libs/vega-embed@7.1.0/vega-embed.min.js` | Embeds a spec — **must** pass `{ ast: true }` | | mermaid | `/libs/mermaid@10.9.6/mermaid.min.js` | Diagrams (flowchart, sequence, class, state, ER, gantt, pie, gitGraph, journey, …) — **must init with `securityLevel: 'strict'`** | | katex | `/libs/katex@0.16.21/katex.min.js` + `/libs/katex@0.16.21/katex.min.css` | Math typesetting | | highlight.js | `/libs/highlight.js@11.10.0/highlight.min.js` + `/libs/highlight.js@11.10.0/github.min.css` | Code highlighting | | marked | `/libs/marked@14.1.3/marked.min.js` | Markdown → HTML | | three | `/libs/three@0.169.0/three.module.min.js` | 3D (WebGL); ES module | Notes: - **Mermaid:** initialize with `securityLevel: 'strict'` (required — it sanitizes diagram labels; never use `'loose'`). Images in labels must be `data:` URIs or inline `` — remote image URLs are blocked and won't load. - **Vega:** always pass `{ ast: true }` to `vegaEmbed` (required — without it the chart won't render). Give the spec an explicit numeric `width` (e.g. `width: 700`) — `width: 'container'` renders a zero-width, blank chart in the sandbox. - **KaTeX:** include both the JS and the CSS. - **three.js** is an ES module — load it with `` tag (in the template) is what makes this possible — always keep it. - **Fluid container:** cap the width but let it shrink — `max-width: 880px; width: 100%; box-sizing: border-box;` — and use fluid padding like `padding: clamp(16px, 4vw, 24px);` so narrow screens aren't cramped and wide ones aren't sparse. - **Wide tables:** wrap every table in a horizontally scrollable container so the table scrolls on its own instead of stretching the whole page: ```html
``` - **Images & SVG:** add `img, svg { max-width: 100%; height: auto; }` so they never exceed the viewport width. - **Charts (chart.js):** wrap the `` in a **fixed-height box** and let the chart fill it — put the height in CSS, never let the chart's own aspect ratio decide it. A ratio that looks right on desktop (e.g. a `1040×360` canvas, or `aspectRatio`/`height:auto` derived from it) collapses to a short unreadable sliver at phone width. See the pattern below (`.chart-box` + `responsive: true, maintainAspectRatio: false`). For Vega-Lite, `width: 'container'` renders blank in the sandbox, so keep a numeric `width` and wrap the chart in `
` so it scrolls when the screen is narrower than the chart. - **Charts (Plotly):** Plotly renders to a `
`, so the `.chart-box`/canvas pattern doesn't apply. Wrap it in a `width: 100%` container, set `layout: { autosize: true }` (no fixed `width`), and pass `{ responsive: true }` as the config to `Plotly.newPlot(...)` so it re-fits when the viewport changes. - **Avoid fixed pixel widths** on layout elements — prefer `%`, `max-width`, and `min()`/`clamp()`. For multi-column layouts use a grid/flex that collapses on narrow screens, e.g. `display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));` or `display: flex; flex-wrap: wrap;`. - **Test narrow:** picture the report at ~360px wide — if anything is cut off or triggers horizontal scrolling of the whole page, constrain or wrap it. --- ## External links Write normal links with real URLs: ```html Docs ``` - Links to untrusted hosts are automatically made inert (the text stays, but they aren't clickable). - `mailto:` and `tel:` links are kept as-is — use them for contact links. - Don't use `javascript:` or `data:` hrefs (they're stripped), and don't rely on JavaScript (`window.open`, `location = …`) to navigate. --- ## Charts & interactivity — the pattern Put all logic in an inline ` ``` > This is the sizing that survives a narrow screen. **Don't** size a chart by its canvas `width`/`height` attributes or by `aspectRatio`/`height: auto` — a wide desktop ratio (e.g. `1040×360`) collapses to a ~100px-tall sliver at phone width. Pinning the height on `.chart-box` and filling it (`maintainAspectRatio: false`) gives a full-width, controlled-height chart on every device; the `.chart-box > canvas` rule keeps it filling the box even if the runtime strips chart.js's inline sizing. For a taller/shorter chart, change the one `height` value (use `clamp()`, e.g. `height: clamp(240px, 45vw, 360px)`, if you want it to grow a little on wider screens). **Vega-Lite** (always pass `{ ast: true }`): ```html
``` **Mermaid** (always set `securityLevel: 'strict'`): ```html
flowchart TD
  A[Start] --> B{OK?}
  B -->|yes| C[Done]
``` --- ## File structure template ```html Q4 Revenue Analysis

Q4 Revenue Analysis

Summary of Q4 results by region.

Revenue by region

Detail

RegionRevenueYoY
North America$1.2M+9%
EMEA$0.5M+6%
APAC$0.4M+28%

Source: Snowflake docs.

```