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, not GradientText).
  • 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 your demo.html for 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-world demo as a minimal reference and the _template directory as your starting point.