# zulip A modern, no-build web application using Lit web components with Zustand state management. ## Architecture Overview * **Lit-based web components** - Standards-based, framework-agnostic UI * **No-build tooling** - Import maps only, no bundler required * **Zustand state management** - Lightweight, vanilla JS store * **StoreController** - Reactive controller that bridges Zustand and Lit ## Project Structure ``` components/ -> standard lit components, including top-level "app-root" controllers/ -> lit reactive controllers (such as Zustand<->Lit bridge) store/ -> defines specific accessor sets index.html -> entry point with inline importmap and app-root README.md ``` ## Data Flow Diagram ```mermaid sequenceDiagram participant User participant LitComponent participant StoreController participant ZustandStore User->>LitComponent: Click button LitComponent->>ZustandStore: Call action (e.g., addItem) ZustandStore->>ZustandStore: Update state ZustandStore->>StoreController: Notify subscribers StoreController->>LitComponent: requestUpdate() LitComponent->>LitComponent: Re-render LitComponent->>User: Show updated UI ``` ## Example ### 1. Define Store (Zustand Vanilla) ```javascript // store/index.js import { createStore } from 'zustand/vanilla' export const store = createStore((set, get) => ({ count: 0, increment: () => set(s => ({ count: s.count + 1 })), decrement: () => set(s => ({ count: s.count - 1 })), })) ``` ### 2. Create StoreController (Bridge) ```javascript // controllers/store.js export class StoreController { constructor(host, store, selector) { this.host = host this.store = store this.selector = selector host.addController(this) } hostConnected() { this._unsub = this.store.subscribe((state) => { const next = this.selector(state) if (next !== this.value) { this.value = next this.host.requestUpdate() } }) this.value = this.selector(this.store.getState()) } hostDisconnected() { this._unsub?.() } } ``` ### 3. Create Component (Lit + StoreController) ```javascript // components/counter.js import { LitElement, html, css } from 'lit' import { StoreController } from '../controllers/store.js' import { store } from '../store/index.js' class Counter extends LitElement { // Subscribe to store (like useSelector) #count = new StoreController(this, store, s => s.count) static styles = css` button { padding: 1rem; font-size: 1.2rem; margin: 0.5rem; } ` render() { return html`
Count: ${this.#count.value}