Skip to content

Build your first custom widget

The custom widget path is the fastest way onto the overlay: a folder with an index.html and a widget.json becomes a live, hot-reloaded hub — the panel chrome, drag, hover-reveal, and auto-collapse are drawn for you. No build step, no TypeScript. It’s locked down by default — it gets a limited set of capabilities declared in its widget.json (and can run a restricted Node.js backend), but it can’t extend TeamHUD’s internals or reach native code. For full trust, build a plugin.

This is also the path the in-app AI chat uses when you ask it to build a widget.

  • The TeamHUD app running.
  • Nothing else — you edit plain HTML/CSS/JS.

Custom widgets live in a watched folder. Your personal one survives app updates:

~/.teamhud/custom-widgets/my-widget/
widget.json
index.html

The overlay picks up the folder within ~1.5 s — no restart. If it doesn’t appear, enable it from the widget library.

widget.json is the only required config beyond your HTML:

{
"name": "My Widget", // hub label
"html": "index.html", // your UI (local file or an https:// URL)
"icon": "icon.svg", // optional hub icon — SVG only
"size": { "width": 300, "height": 220 },
"permissions": { "backend": false, "node": [], "network": false }
}

Two rules make your widget match the HUD by construction:

  • Use --th-* tokens for color, type, spacing, and radius — never hardcode hex, px font sizes, or font-family literals. The host injects a live snapshot of every token and re-pushes them when the user changes density, accent, or theme, so var(--th-…) tracks the HUD automatically.
  • Mark clickable areas class="teamhud-interactive" so the click-through overlay lets pointer events reach them.

A starter index.html:

<!doctype html>
<body style="margin: 0; font-family: var(--th-font-ui); color: var(--th-text-primary);">
<div id="app" class="teamhud-interactive" style="padding: 12px; display: flex; flex-direction: column; gap: 8px;">
<div style="font-size: var(--th-text-sm); color: var(--th-text-muted);">Clicks</div>
<div id="count" style="font-size: var(--th-text-xl);">0</div>
<button id="bump" style="font: inherit; padding: 6px 10px; border-radius: var(--th-radius-md);
border: 1px solid var(--th-border-secondary); background: var(--th-bg-surface); color: var(--th-text-primary);">
Count up
</button>
</div>
<script>
let n = 0;
const out = document.getElementById('count');
document.getElementById('bump').addEventListener('click', () => { out.textContent = String(++n); });
</script>
</body>

Save, and the overlay hot-reloads. Common tokens: --th-text-primary / --th-text-muted (text), --th-text-sm/-md/-lg/-xl (sizes), --th-bg-surface / --th-bg-hover (surfaces), --th-border-secondary (borders), --th-radius-md (corners), --th-space-1…--th-space-6 (spacing), --th-accent (accent). The full catalog, the shared .th-* classes/elements, and the rules are in Theming & styling.

A locked-down overlay widget that themes itself to the HUD automatically. When you need to extend TeamHUD’s internals or run with full trust, cross over to building a plugin.

  • Let the AI do it: ask the in-app AI chat to build a widget — it authors this same folder shape behind a confirmation card.
  • Build your first plugin — the full TypeScript/Lit path.