STET / JAVASCRIPT

UI annotations in JavaScript.

Stet is a free, MIT-licensed UI annotation library. Add hand-sketched marks to live JavaScript interfaces while your existing controls keep their layout, events, and semantics.

Install and make your first mark

The package is @funsaized/stet. These ESM examples use the project’s generated framework templates. Pin your installed version and inspect its capabilities before using newer options.

The core exports circle, underline, highlight, arrow, mark, and sticky. Pass resolved Elements after DOM mount. Each call returns a handle; your application decides when to attach and remove it. No framework or runtime dependency is required.

The canonical example uses TypeScript. Compile it with your app’s toolchain, or omit the Element type annotation when using plain JavaScript.

JavaScript / first mark
// Adapt these sample elements to existing source; retain their semantics and handlers.
import { circle } from "@funsaized/stet";
import "@funsaized/stet/style.css";

// Call after DOM mount with resolved, unique Elements. Call cleanup before removal.
export function annotate(target: Element) {
  const handle = circle(target, {"seed":42,"description":"Review this action before continuing."});
  return { refresh: () => handle.refresh(), destroy: () => handle.destroy() };
}

Browse the canonical JavaScript templates

Keep marks in step with the interface

These lifecycle templates follow the repository checkout. Placement nudges such as labelOffsetY are unreleased; omit them with published 0.1.0 and check your installed capabilities before adapting the example.

Call refresh after application-driven movement that does not resize the target. Call destroy before removing the element. When changing options or arrow destinations, destroy old handles and attach new ones; keep the same seed for repeatable geometry.

JavaScript / lifecycle
// Adapt to existing controls; annotation state never controls their presence.
import { arrow, circle, sticky, type StetHandle } from "@funsaized/stet";
function attachMarks(target: Element, destination: Element | null | undefined, enabled: boolean) {
  const handles: StetHandle[] = [];
  const destroy = () => { for (const handle of handles.splice(0).reverse()) handle.destroy(); };
  try {
    if (enabled) {
      handles.push(circle(target, { seed: 42, description: "Review this action before continuing." }));
      handles.push(sticky(target, { seed: 43, text: "Read the consequences before continuing." }));
      if (destination) handles.push(arrow(target, destination, { seed: 44, label: "Consequences are explained here.", labelOffsetY: -12 }));
    }
    return destroy;
  } catch (error) { destroy(); throw error; }
}
import "@funsaized/stet/style.css";

// Call update after the application mounts/replaces/removes its destination.
// Call destroy before removing the source control. Reattachment preserves seeds.
export function annotate(target: Element) {
  let cleanup = () => {};
  return {
    update(enabled: boolean, destination: Element | null = null) {
      cleanup(); cleanup = () => {};
      cleanup = attachMarks(target, destination, enabled);
    },
    destroy() { cleanup(); cleanup = () => {}; },
  };
}

Ink, layout, and accessible meaning

Import @funsaized/stet/style.css globally. Set --stet-stroke, --stet-fill, and --stet-paper to fit your design. SVG overlays do not occupy layout space or intercept pointer input.

Keep native labels, errors, keyboard focus, and event handlers on your controls. Give meaningful marks a description; color alone cannot explain a finding. Sticky notes and arrow labels supply readable text. Optional ink motion respects reduced-motion preferences.

Stet uses browser DOM APIs. Attach handles after mount, not during server rendering. Resize and scroll tracking cannot cover every transform or top-layer dialog; read placement limits and the full API reference.

Where this fits

Add marks to an existing handbook or product demo with ordinary DOM elements and your own event listeners.

Reproduce and annotate a form focus bug · Document an interactive activity inbox · Try all six primitives in the playground

Work with your coding agent

The agent inspects your installed package, validates an annotation plan, and adapts a JavaScript template. Plans are authoring artifacts, not a runtime selector engine. Browser verification must still check placement and behavior.

Install project Agent Skills · See the complete visual handoff workflow

Machine-readable capabilities · Annotation plan schema · Full API reference