Contributing a Demo
Every demo on this site lives in a single directory under
src/content/demos/. Drop in a folder with two files and it
appears automatically — no routing or config changes required.
1 Fork & clone
Fork the repository on GitHub, then clone your fork locally:
git clone https://github.com/YOUR-USERNAME/html-in-canvas-dot-dev.git
cd html-in-canvas-dot-dev
npm install 2 Create your demo directory
The fastest way is to run the scaffold script. It copies the starter template and pre-fills your slug:
./scripts/new-demo.sh my-cool-demo
This creates src/content/demos/my-cool-demo/ with a
ready-to-edit meta.json and demo.html.
Prefer to do it by hand? Copy the template directory:
cp -r src/content/demos/_template src/content/demos/my-cool-demo 3 Edit meta.json
Open meta.json and fill in your demo's metadata. Every
field is documented below.
| Field | Required | Description |
|---|---|---|
title | Yes | Human-readable name shown in the gallery and detail page. |
description | Yes | One-sentence summary shown on the demo card and in meta tags. |
tags |
Array of lowercase tags for filtering.
Example: ["animation", "text"] | |
author | Your name or handle. | |
dateCreated | ISO date (YYYY-MM-DD) when the demo was first written. | |
dateUpdated | ISO date of the last significant update. | |
context |
Canvas API context: "2d" (default),
"webgl", or "webgpu".
| |
difficulty | "beginner" (default),
"intermediate", or
"advanced".
| |
features |
Spec features exercised:
"layoutsubtree",
"drawElementImage",
"onpaint",
"requestPaint", etc.
| |
browserSupport |
Object with chrome, firefox,
safari booleans. Set to true once
verified in that browser.
|
Delete the _field_docs block from the template before
committing — it's only there for reference.
4 Build your demo.html
Each demo is a single self-contained HTML file. It's served two ways:
as a standalone document at /demos/{slug}/demo.html,
and mounted inside a shadow root on the wrapped
/demos/{slug}/ page. Your demo needs to work in
both contexts — the template handles this for you.
layoutsubtree
Add the layoutsubtree attribute to your
<canvas> element. This tells the browser to lay
out the canvas's child elements so they can be painted.
<canvas id="canvas" layoutsubtree>
<div id="content">...</div>
</canvas> onpaint callback
Set canvas.onpaint to a function that clears and
redraws via ctx.drawElementImage(). The browser
calls it whenever children need re-rendering.
canvas.onpaint = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawElementImage(content, 0, 0);
}; ResizeObserver + requestPaint
Keep the canvas resolution in sync with its CSS size, then call
requestPaint() so the browser generates fresh
cached paint records that drawElementImage depends on.
new ResizeObserver(([entry]) => {
const { width, height } = entry.contentRect;
canvas.width = Math.round(width * devicePixelRatio);
canvas.height = Math.round(height * devicePixelRatio);
ctx.scale(devicePixelRatio, devicePixelRatio);
canvas.requestPaint?.();
}).observe(canvas); body, :host selector list
Use a body, :host selector list for any rule that
should style the document root. body matches in
standalone, :host matches inside the shadow mount.
Both have the same specificity, so one rule covers both.
body,
:host {
margin: 0;
min-height: min(78vh, 820px);
background: #0a0a0f;
} window.__demoRoot
Resolve element lookups against this root, not document.
The wrapper page sets it to the shadow root before running your
script; in standalone mode it's undefined and falls
back to document.
const root = window.__demoRoot ?? document;
const canvas = root.getElementById("canvas");
You can add extra .css, .js, or other files
alongside demo.html — they'll show up in the Source
Viewer automatically. The wrapper page also handles the demo stage
size for you: aim for min-height: min(78vh, 820px) on
body, :host so small demos fill the area while larger
ones can grow naturally.
5 Test locally
Start the dev server and visit your demo:
npm run dev
# Open http://localhost:4321/demos/my-cool-demo/ The dev server watches for file changes and reloads automatically. Verify your demo appears in the gallery and that the metadata renders correctly on the detail page.
Note: The HTML-in-Canvas API requires
Chrome Canary with the
canvas-draw-element flag enabled. Other browsers will
show the fallback overlay.
6 Submit a PR
Commit your demo directory, push to your fork, and open a pull request
against main:
git add src/content/demos/my-cool-demo
git commit -m "Add my-cool-demo demo"
git push origin my-branch
# Open a PR on GitHub In your PR description, briefly explain what the demo shows and which spec features it exercises.
Tips
-
Use kebab-case for your directory slug
(e.g.
gradient-text, notGradientText). - Keep demos self-contained — no external CDN scripts or images from third-party hosts.
-
The demo stage has a dark background (
#0a0a0a) by default; match it in yourdemo.htmlfor a seamless look. -
Text files (
.html,.css,.js,.ts,.json,.svg,.glsl,.wgsl) are displayed in the Source Viewer automatically. Binary files are excluded. -
Use the
hello-worlddemo as a minimal reference and the_templatedirectory as your starting point.