Browse the docs

The kit page API

Every call a Site Kit's JavaScript can make: route, place, anchor, onResponse, store, settings, command, sidebar, toast, ui.upgrade and theme.

Inside a kit's features/*.js and lib/*.js, kit is a local variable (never on window). It has everything switchboard has (emit, run, on, vars; see Safari and Chrome) plus the calls below. Site Kits explains how they fit together.

kit.route(pattern, { mount, unmount }) #

Runs mount(ctx) while the address matches pattern and unmount(ctx) when it stops matching. pattern is a path: :name matches one segment (it appears in ctx.params), * matches anything including slashes, and a trailing slash doesn't matter. Query and hash are ignored.

  • Unmount runs before mount, newest first. Going from /item/1 to /item/2 unmounts and mounts again.

  • mount may be async. If the route leaves while it runs, its awaits reject with an AbortError that is ignored.

  • Errors in mount are reported. Three in a row turn that feature file off.

ctx #

ctx.paramsThe :name parts of the address, frozen.
ctx.urlThe current address as a URL.
ctx.routeThe pattern this route was declared with.
ctx.signalAn AbortSignal that aborts at unmount. Pass it as { signal } to addEventListener and to observers, and they go with the route.
ctx.settingsThe kit's settings when the route mounted, frozen. A change mounts the route again.
await ctx.anchor(name)The element for an anchor declared in kit.json. Waits up to 5 seconds, then throws AnchorMissing (reported, and counted in the kit's health).
ctx.place(html, anchor, position?, options?)Puts HTML next to an element and returns the first element placed.

ctx.place #

position is before, after, prepend, append (the default) or replace. anchor is an element (from ctx.anchor) or the name of one you have already awaited. The HTML is drawn in a closed shadow root with the kit's components and tokens, so the site's CSS can't reach it and yours doesn't leak out. <script> is removed. data-switchboard-run, -emit and -bind attributes work.

option
like"sibling" copies height, radius, weight, colours and hover from the neighbour. A selector copies from that element. false uses the kit's base theme. Default: "sibling" when the anchor already holds an element of the same kind.
isolatefalse places the HTML in the page's own DOM, with no shadow root, so the site's CSS applies. Default true.

Placed content fades in over 150 ms and collapses in 120 ms when removed, and does neither under reduced motion. In a flex or grid parent it takes the parent's gap and alignment.

kit.onResponse(pattern, fn, options?) #

Calls fn(body, { url, status, method, at }) for each fetch or XHR response whose address path matches pattern, before the page draws it. The page always gets the original response.

  • pattern must be listed in permissions.responses in kit.json, and the kit needs mainWorld: true (and Allow User Scripts in Chrome). Otherwise it throws "not in this kit's permissions".

  • as: "json" (default; a body that isn't JSON is skipped) or "text".

  • types: ["fetch", "xhr"] (default both) or one of them.

  • Bodies over 2 MB are skipped. An error in fn is reported and doesn't affect the page or other hooks. Returns a function that removes the hook.

kit.store #

Records the kit keeps in Switchboard, shared by every tab in every browser and by the kit's macros. A key is a string up to 200 characters, a value is JSON up to 1 MB, a collection holds up to 10,000 keys. Names beginning __ are the kit's own.

await kit.store.get(collection, key)             // the value, or null
await kit.store.put(collection, key, value)      // true; fails naming the limit
await kit.store.delete(collection, key)          // true
await kit.store.query(collection, { prefix, limit })   // [{ key, value }] in key order
const stop = kit.store.watch(collection, (key, value) => {})   // value is null after a delete

Everything but watch returns a promise and rejects with the reason (for example "the value is over 1 MB"). Your own change may come back to your watch too: compare with what you hold before redrawing.

kit.settings #

kit.settings.get("compact")        // the current value
kit.settings.all()                 // a copy of every value
const stop = kit.settings.on((settings, changed) => {})   // changed: names that differ

Values come from settings in kit.json, with the person's changes laid over the defaults. When one changes, on handlers run first and then every mounted route mounts again, so a route that reads ctx.settings in mount needs nothing else.

kit.command(name, fn, options?) #

Adds a command to the palette (⌥⌘K). options.keys gives it a shortcut, such as "⌥⌘L", while the kit is on the page; a shortcut without ⌘, ⌥ or ⌃ doesn't fire while the person is typing in a field. fn may return a promise; a rejection is reported. Returns a function that removes it. Register inside mount and remove with ctx.signal to make a command exist only on its route.

The kit's macros with Callable from pages are listed in the palette without any code.

kit.sidebar #

kit.sidebar.set(tree)                 // draw (or replace) the sidebar
kit.sidebar.update(id, { text: "…" }) // change one node's props
const stop = kit.sidebar.on(id, (e) => {})   // e: { type, value, … }
kit.sidebar.open()                    // show it (the side panel, or the drawer)

A tree is nodes with an id: stack (children, gap), text (text, style: title, body, caption, mono), image, button (title, role), field (label, kind: text, multiline, secure; placeholder, value), toggle (label, value), list (items, select), table (columns, rows), progress, divider and spacer. Events: a button gives { type: "click" }, a field { type: "input", value } once typing pauses (and { type: "submit", value } on Return in a single-line field), a toggle { type: "toggle", value }, a list or table { type: "select", value }.

A kit with settings gets a ⚙ at the top of its sidebar that opens the kit's settings in Switchboard.

kit.toast(text, options?) #

A short message in the kit's look. style: info (default), success, warning, error. seconds: default 3. action: { label, run } adds a button.

kit.ui.upgrade(root) #

Makes the sb-* elements under root real components. ctx.place does it for what it places; call this for DOM you build yourself. It keeps watching root for markup added later, and returns root.

kit.theme #

Read-only: { tokens, lines, source }. tokens are the --sb-* values now in force, lines are the contrast nudges made to them, and source is profile, sibling, sample or switchboard.

kit.html(name) #

The text of a file in html/: kit.html("badge.html"), for ctx.place.

kit.gm(ctx, info) #

For imported Tampermonkey and Violentmonkey scripts only; the importer writes the call, you will not need it in a kit you write. info is {script: {name, version, namespace, description?}, grants, resources, runAt}. It resolves to an object holding the GM_* functions (and an async GM.* object), once the page reaches runAt:

  • GM_addStyle adds a <style> and removes it when the route unmounts; GM_getValue, GM_setValue, GM_deleteValue and GM_listValues keep their values in the kit's store collection gm (read once up front, so they stay synchronous);

  • GM_registerMenuCommand is kit.command; GM_notification is kit.toast (onclick is ignored); GM_setClipboard writes to the page's clipboard; GM_openInTab opens a new tab; GM_getResourceText and GM_getResourceURL read the script's @resource files; GM_info and unsafeWindow are provided.

  • GM_xmlhttpRequest, GM_download, GM_cookie and the other calls that need more power than a page script should have throw an error that says so.

kit.id and kit.version #

The kit's id and version from kit.json.

Modules #

require("name") returns the exports of lib/name.js; ./name, lib/name.js and name.js mean the same. A module runs once per page. A cycle gets what the module has exported so far.