JavaScript API
partialkit/auto registers every component and starts the observer on load — that is what the
drop-in build does. Import partialkit instead when you want to choose.
import { register, start, dialog, dropdownMenu } from "partialkit";
register(dialog);register(dropdownMenu);start();The components are carousel, checkbox, contextMenu, dialog, dropdownMenu, hoverCard,
inputOtp, menubar, popover, slider, tabs, theme, toggle and tooltip. Everything else is CSS only and needs no registration.
Exports
Section titled “Exports”| Export | Purpose |
|---|---|
register(component) | Adds a component and runs its setup once |
start({ root }) | Mounts everything under root and observes it. Defaults to document.body |
mount(root) / unmount(root) | Manual control, if you would rather not observe |
stop() | Disconnects the observer and runs every cleanup |
openDialog(id, opener?) / closeDialog(id, value?) | Programmatic dialog control |
getTheme() / setTheme(theme) / applyTheme() | Theme control |
ensureId, setDefaultAttribute, focusIsLoose, focusableWithin, focusFirst, createTypeahead | ARIA helpers |
findAll, closestWithAttribute, dispatch | DOM helpers |
anchorOf, position, positionAgainst, track | Placing a floating panel against a trigger or a point |
Events
Section titled “Events”| Event | Fired on | Detail |
|---|---|---|
pk:dialog:before-open | the dialog, cancelable | — |
pk:dialog:open | the dialog | — |
pk:dialog:close | the dialog | { returnValue } |
pk:menu:change | the item | { checked, value } |
pk:tabs:change | the trigger | { value } |
pk:toggle:change | the button | { pressed, value } |
pk:carousel:change | the carousel | { index, atStart, atEnd } |
pk:otp:change | the field | { value, complete } |
pk:theme:change | <html> | { theme, dark } |
Writing a component
Section titled “Writing a component”A component is an object with a name, a selector, and a mount that runs once per matching
element. Return a cleanup and it runs when that element leaves the DOM.
import { register, type Component } from "partialkit";
const counter: Component = { name: "counter", selector: "[data-counter]",
mount(element) { const onClick = () => (element.textContent = String(Number(element.textContent) + 1)); element.addEventListener("click", onClick); return () => element.removeEventListener("click", onClick); },};
register(counter);setup() is the other half of the contract: it runs once when the component is registered, and is
where document-level delegation belongs.
const shortcuts: Component = { name: "shortcuts", selector: "[data-shortcut]", mount() {},
setup() { const onKeydown = (event: KeyboardEvent) => { /* ... */ }; document.addEventListener("keydown", onKeydown); return () => document.removeEventListener("keydown", onKeydown); },};Prefer setup for anything that can be delegated. It costs one listener no matter how many
elements arrive, which is what keeps the runtime cheap on pages that replace markup often.