Skip to content

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:

  1. A toast appears. The button asked for notification.show. The notification module rendered the toast in <kit-toast-region>.
  2. 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.
  3. 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.

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 toast

Events are facts. Commands are requests. Modules pick what they care about. The button is a button.

  • 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-boundary inside this one with a different surface= and watch the surfaces stack in the analytics output.
  • Wrap a <kit-dialog id="confirm"> and a button with meta-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.