-
Notifications
You must be signed in to change notification settings - Fork 0
Home
Dependency-free Web Components for building chat interfaces.
@machfivetechchicago/machvive-chat-syncopation-ai is a collection of
standards-based custom elements you compose into the chat surface your product
actually needs — with an emphasis on user engagement, low cognitive load, modern web
behaviour, and the user's agency over their own conversation data.
No framework. No build step. No runtime dependencies. No model.
Start here
- Getting Started — install, the smallest working surface, and composing the rest
- Component Reference — every tag, attribute, event and method
Building with it
- Services and the Bus — how components find each other and what they say
- Writing a Transport — connecting a model, including one running in the browser
-
Theming — the
--mcs-*tokens, dark mode, and four rules with defects behind them - Data Agency — persistence, export, and deletion that actually deletes
Understanding it
- Design Decisions — behaviours that look like bugs and are not
- Testing a Chat Surface — jsdom's blind spots and the two traps that cost us a day
Elsewhere
- npm package
- Live playground
- machvive-webmcp-ai — the sibling package, for agents rather than people
Chat is now the default interface for anything an AI touches, and almost all of it is rebuilt from scratch each time. The same bubble list. The same Enter-to-send textarea. The same autoscroll bug where the transcript yanks you to the bottom while you are reading something further up. The same disabled Send button that leaves you watching output you cannot stop.
None of that is model work. It is interface work, and it has correct answers that nobody should have to rediscover.
Chat Syncopation is that layer built once. Nothing in it calls a model — the model is a transport you supply, in about fifteen lines. Everything else, the parts that are the same in every chat product and wrong in most of them, is done.
User engagement. An empty prompt box is the highest-friction moment in any chat interface: the user has to invent both the task and the phrasing. The nudge component offers concrete openings — and fills the composer rather than sending, so the user stays the author of their own message.
Prompt cognitive load. A conversation is a reading surface before it is an input surface. Turns stream into a single node instead of re-rendering the list. Autoscroll follows the bottom only while the reader is already there. The send button becomes Stop rather than going disabled.
Modern web experiences. Shadow DOM, custom properties that inherit through the
shadow boundary, prefers-color-scheme, prefers-reduced-motion, aria-live on
the transcript, the Speech APIs where they exist and an honest explanation where
they do not.
User data domain agency. The conversation lives in the user's browser. Persistence is off until a page turns it on, and the history component gives the person whose words these are three operations: see what is stored, export it as JSON they keep, and delete it for real.
We are deliberately challenging the assumption that a chat surface needs a hosted
model behind it. The daemon's transports are a uniform interface, and the one that
matters strategically is local — a model running inside the user agent, via
WebLLM or similar.
A model in the browser has no per-token cost. That changes the arithmetic entirely for cases usually ruled out as uneconomic:
| Situation | Why a hosted model is a problem | What changes |
|---|---|---|
| Long-tail support | Cost per conversation exceeds the value of the deflection | Cost per conversation is zero |
| Kiosk or in-store terminal | Unmetered usage by passers-by | Unmetered usage costs nothing |
| Classroom or training | Per-seat API spend nobody budgeted | One download, any number of sessions |
| Internal tooling | Hard to justify a line item | No line item |
| Field application | No connectivity at all | Works offline by construction |
| Privacy-sensitive intake | Conversation leaves the device | Conversation never leaves the device |
Because no component knows where its text came from, offline-first is a configuration rather than a rewrite. The same markup that talks to a hosted model talks to one in the browser by changing one attribute.
Nine elements. One is headless and holds the state; the rest are surfaces you arrange.
<machvive-chat-syncopation-services> the bus, conversation, daemon, cache
└── <machvive-chat-syncopation> layout only; children are slotted
├── <machvive-chat-syncopation-nudge>
├── <machvive-chat-syncopation-canvas>
└── <machvive-chat-syncopation-prompt>
Components discover the services element by walking up the DOM, so nesting is the wiring — there is no registry and no global, which is exactly what makes two chat surfaces on one page genuinely independent.
Continue to Getting Started.
Apache-2.0 · MachFiveTech Chicago