page
Build and publish a self-contained HTML page to Superset, then answer the comments readers pin to it. Use when the user asks to make or publish a page, turn a report, dashboard, chart, doc, or analysis into a shareable link, update or re-version a page already published, or work through comments left on one, including "make me a page for this", "publish this as a page", "share it as a link", "add a version", "address the comments on that page".
git clone --depth 1 https://github.com/superset-sh/superset /tmp/page && cp -r /tmp/page/plugins/superset/skills/page ~/.claude/skills/pageSKILL.md
# Superset Pages
A page is an **`.html` document** published to a URL people in the org can
open. Publish a single file and it must be self-contained; publish a directory
and its `index.html` is the document, with the images, stylesheets, and media
it references by relative path published alongside it. Every publish mints a version, so a page has history. Readers can
pin a comment to any element on it, and those comments come back to an agent to
fix. That is what makes a page a working surface rather than an export.
Pages are served from their own origin under a strict content policy. Most of
the work in this skill is respecting that policy; a page that ignores it looks
fine locally and breaks silently once published.
## When a page is the right surface
Publish a page when the work has a **reader** and wants a **link**: a report
someone will skim, a dashboard for a standup, a comparison table, a diagram, a
walkthrough of what you changed.
Don't publish when the artifact belongs in the repo (source, docs, config: put
those in files and commit them), or when it genuinely needs a server, a
database, or a login. A page has none of those.
If you're unsure, ask. Publishing is cheap and reversible, but a page the user
didn't want is noise in their org's list.
## The content policy, which is what actually bites
Every page gets its own origin, `https://<pageId>.frame.supersetusercontent.com`, and
is framed with `sandbox="allow-scripts allow-same-origin allow-forms
allow-popups"`. So the page is a real origin of its own, and a locked-down
one. The policy is `default-src 'none'` with a short allowlist, and it is
enforced identically in the desktop pane and the web viewer:
- **No network from script.** `fetch`, `XHR`, `EventSource` and WebSockets are
all blocked, and so is `fetch("data:...")`: a page cannot read its own
inlined data URIs back out. Write pages that need no network at all: bake
the data into the document as a literal, or decode base64 in JavaScript
(`atob`, then `Uint8Array.from`).
- **No compiling code at runtime.** `script-src` carries no `'unsafe-eval'`,
so `eval()` and `new Function()` both raise an `EvalError`. This rules out
inlining any library that builds functions at runtime, which includes
several chart and templating libraries and a number of date and expression
helpers. Check for it before you reach for a dependency: the page renders
nothing and gives no visible reason why.
- **No scripts or stylesheets from a remote host.** `<script
src="https://…">` and `<link rel="stylesheet" href="https://…">` are
blocked, Google Fonts `<link>` tags included. A directory publish's own
files load fine (relative `src`/`href`), and a remote font *file* is
allowed, so an inline `@font-face { src: url(https://…) }` works.
- **Images, video and audio may be remote** (`https:`, `data:` or `blob:`),
but prefer `data:` URIs for anything the page cannot do without: a reader
with the network off sees nothing, and a remote image makes every reader's
browser call that host directly, which hands a third party the IP address
of everyone who opens the page.
- **Storage works** and is scoped to the page: `localStorage`,
`sessionStorage`, `indexedDB` and cookies persist across reloads and across
versions of the same page. Use it for a chosen tab or filter, never for
anything the page cannot rebuild from its own content.
- **No parent access.** The viewer is a different origin, so
`window.parent.document` and `window.top.location` throw. Superset injects
one script into the page for comment anchoring; nothing else listens to
`postMessage`, so don't build a handshake on it.
- **No form submission.** `form-action 'none'`: a `<form>` may exist for its
controls, but submitting it goes nowhere. Handle inputs in script.
Scripts and popups *do* work. Inline JS runs normally, so charts, filters,
sorting, tabs, and interactive controls are all fine, as long as everything
they need is already in the file.
## The other hard limits
1. **`.html` only.** Any other extension is rejected at the CLI.
2. **One file, or one directory.** `superset pages publish ./report/`
publishes a directory: `index.html` is the page, and every other file
ships at its relative path, so `<video src="demo.mp4">`,
`<link href="site.css">` and `<script src="app.js">` all work. Asset
paths may not start with `versions/`, `files/`, `_superset/` or `~`, or
be named `thumbnail.jpg`. Assets go up to 1 GiB each; on republish,
unchanged assets are not re-uploaded. Prefer H.264 MP4 or WebM for
video: iPhone `.mov` recordings may not play in every browser. Remote
CDN links and external stylesheets are still blocked; for a single-file
page, inline all CSS and JS and embed images as `data:` URIs.
3. **3 MB maximum for the HTML document itself**, and base64 `data:` URIs
count toward it at ~1.37× their
raw size. A few small SVGs or PNGs are fine; a photo gallery is not.
4. **Full-bleed frame with a white default background.** Set your own `body`
background explicitly rather than inheriting.
Check before publishing: no `<script src>` or `<link rel="stylesheet">` pointing
at a remote host, no `fetch` of any kind including of a `data:` URI, no `eval`
or `new Function` anywhere in the file or in anything you inlined, page fits in
3 MB, opens correctly from `file://` with the network disabled. Remote images
are the one permitted exception: they go blank offline, which is the price of
not inlining them.
## Design
The page should look deliberate. Avoid the house style of generic AI output:
purple-to-blue gradients, everything centered, uniform pill-rounded corners on
every element, Inter (or system-sans) for every line, and emoji as section
icons. Those read as "generated" at a glance.
Instead: pick a real palette and hold to it, set a typographic scale with actual
contrast between heading and body, and let the layout follow the content: a
data-dense table wantsPersonalized audit that teaches the advanced Superset features the user isn't using yet (automations, parallel agents, tasks, multi-host, terminal remote control, custom commands, MCP) and sets them up live. Use when the user wants to get more out of Superset, asks "what else can Superset do", "how do I 10x my workflow", "what am I missing", or wants to learn a specific Superset feature.
Turn a recurring chore into a Superset automation. Drafts the agent prompt, confirms schedule and target, creates it with the CLI, and reviews the first run together. Use when the user wants a scheduled or recurring agent, a daily or weekly job, or says things like "every morning do X", "automate this", "set up a cron agent", "run this on a schedule".
Open, navigate, screenshot, read, click, and type in web pages from an agent. Use when the user asks to open a URL, preview or verify a running web app, check a page's console, fill a form, click through a web flow, or automate anything in a browser, including "open localhost:3000", "screenshot the page", "what does the console say", "click the submit button". Drives the in-app browser pane of a Superset workspace by default and Browser Use for browsers the panes cannot reach.
Verify UI behavior end-to-end by driving the running desktop app over the Chrome DevTools Protocol. Use when asked to verify, reproduce, or confirm a UI change, bug, or regression in the real app rather than in tests.
Operate the user's real desktop apps and windows on macOS, Windows, or Linux. Use when the user asks to open or drive a native app, click or type in a desktop UI, inspect or arrange a window, use their signed-in system browser, or verify an end-to-end GUI flow, including "open Settings and turn on dark mode", "click Save in that window", "what's on screen". Uses an accessibility-first driver (Cua Driver by default, Peekaboo on macOS). Not for Superset's in-app browser pane, headless scraping, or anything an API or CLI can do directly.
Set up a Superset open-source contribution, from forking and cloning superset-sh/superset through local dev setup and the repo's rules to a merge-ready PR. Use when the user wants to contribute to Superset, fix a Superset bug themselves, add a feature to Superset, or prepare a PR against superset-sh/superset.
Create a database migration with Drizzle on a fresh Neon branch. Use when changing the packages/db schema or generating migrations.
Walk the user through design or implementation decisions one at a time, or review completed code one change at a time. Use when the user says "walk me through each decision", "let's decide together", "help me work through these choices", "walk me through what you did", or "QA step by step". Present concise context, mutually exclusive options, log each answer, and finish with a summary.