Feel It in 30 Seconds
No npm. No bundler. No framework. One HTML file. Save it as quill.html and open it in a browser.
<!doctype html><html lang="en"><head> <meta charset="utf-8" /> <title>Kitsune in 30 seconds</title> <script type="module"> import 'https://esm.sh/@atheory-ai/kitsune-app' import 'https://esm.sh/@atheory-ai/kitsune-ui' import { defineKitModule } from 'https://esm.sh/@atheory-ai/kitsune-core' import { notificationModule } from 'https://esm.sh/@atheory-ai/kitsune-ui'
// Two modules. The button below knows about neither of them.
const analyticsStub = defineKitModule({ name: 'analytics-stub', events: { '*': (event) => { console.log('[analytics]', event.type, event.context, event.payload) }, }, })
customElements.whenDefined('kit-shell').then(() => { const shell = document.querySelector('kit-shell') shell.modules = [notificationModule(), analyticsStub] }) </script></head><body> <kit-shell name="quill"> <kit-boundary surface="note-editor" feature="notes" entity-type="note" entity-id="42"> <h1>One button, three concerns it doesn't know about.</h1> <kit-button meta-event="note.saved" meta-command="notification.show" meta-prop-message="Note saved" meta-prop-tone="success" > Save </kit-button> </kit-boundary>
<kit-toast-region></kit-toast-region> </kit-shell></body></html>Click the button. Three things happen at once:
- A toast appears. The button asked for
notification.show. The notification module rendered the toast in<kit-toast-region>. - The console logs an analytics event with the surface (
note-editor), the feature (notes), and the entity (note: 42) attached. The button never named those — the boundary did. - The button knows nothing about either of them. Try replacing the analytics module with a real PostHog call. Or remove it. The button doesn’t move.
That’s the whole loop. The rest of the docs are about scaling this up — to forms, dialogs, lists, command palettes, audit logs, real persistence, and React apps that already exist.
What just happened
Section titled “What just happened”click on <kit-button> ↓<kit-boundary> reads its meta-* attributes and adds context (surface/feature/entity) ↓runtime fires note.saved (event) and dispatches notification.show (command) ↓analytics-stub module observed the event ↓notification module handled the command and rendered the toastEvents are facts. Commands are requests. Modules pick what they care about. The button is a button.
Try this next
Section titled “Try this next”- Add a second event listener:
events: { 'note.saved': (e) => console.log('audit:', e) }— that’s the audit module from chapter 6 of the tutorial, in two lines. - Add a
kit-boundaryinside this one with a differentsurface=and watch the surfaces stack in the analytics output. - Wrap a
<kit-dialog id="confirm">and a button withmeta-command="dialog.open" meta-prop-target="confirm". The dialog handler is built in.
Or go through the tutorial and build a real notes app from this seed.