Get started
A state machine, designed for UIs. Every interface starts as a description: open/closed, hovered, dragging, on this event, go there. That description is pure intent, and it rarely changes. What changes is the code you write to express it, tangled into whatever framework you happen to be using. Dunky turns the description into the thing that actually runs: an agnostic machine that holds the behavior and nothing else, portable everywhere JavaScript does, and built for heavy load.
AGNOSTIC SUBSTRATE π« βοΈ >> π§ >> π >> β¨ ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β dunky β β machine β β binding β β behavior β β β β β β β β β β the engine β β states + β β neutral wire β β live feature β β that runs β β events + β β agnostic β β on DOM / β β machines β β logic β β events/attrs β β TUI / RN β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ powers decides connects appearsHow did it all end up here?
Section titled βHow did it all end up here?βIβve spent the last decade building design systems. New company, new tech stack, new framework, but always the same story: rebuild the button, the tooltip, the dialog, the combobox. From scratch. Again.
The behaviors never really changed. Focus management, keyboard navigation, ARIA roles, state transitions, itβs all been documented by the W3C for years. The ARIA Authoring Practices Guide literally has the recipe. Yet every new project meant reinventing the same wheel, just with whatever fancy hammer was trending that quarter.
The final boss? Trying to port a full component library to a collaborative whiteboard running on a canvas engine.
No DOM, yet, a tooltip is still a tooltip. A menu still needs arrow-key navigation. A dialog still needs to trap focus. The primitives donβt care about your rendering layer. The behaviors are the same. They always are.
Thatβs when I finally got fed up and started π« Dunky.
The wiring is the problem
Section titled βThe wiring is the problemβThe cost isnβt the core, itβs everything around it: bending behavior you already understand to fit each platform and frameworkβs quirks. The DOM fires keydown; React Native has no key events. React wants state in a hook; the canvas wants a frame loop. The state graph is identical, but the wiring is rewritten from scratch every time. The wiring never compounds: you re-solve the same integration against a slightly different host, forever.
The old dream is write once, render everywhere, and it breaks for an honest reason: the render genuinely should differ. A tooltip canβt hover on a touch screen. aria-* is a DOM contract; React Native speaks accessibilityRole. Force one render across all of them and you get a component thatβs slightly wrong everywhere.
Whatβs actually the same across every target was never the render, itβs the behavior. Thatβs the part worth writing once.
Why now? Wellβ¦ AI π€
Section titled βWhy now? Wellβ¦ AI π€βThe βwrite it once, run it everywhereβ dream isnβt new. What is new is that AI-assisted development makes behavior-driven primitives dramatically more powerful. When your components are pure state machines with typed inputs and outputs, an AI can reason about them precisely, generate implementations, write tests, migrate between versions, without hallucinating framework-specific magic.
Properly specified behavior is a gift to AI tooling. And properly driven AI is a gift back.
The Dunky layers
Section titled βThe Dunky layersβDunky splits the problem into three independent layers:
| Layer | Package | Job |
|---|---|---|
| Behavior | @dunky.dev/state-machine | What a component does |
| Styles | @dunky.dev/style-engine | How it looks |
| Render | @dunky.dev/react-state-machine (and friends) | Where it renders |
Youβre here for layer one.
The state machine defines behavior once, keyboard interactions, focus, ARIA, transitions, and that logic runs identically in every render substrate. π