# Alpine.js patterns in Bookhoard Bookhoard's Alpine runs the **`@alpinejs/csp` build** — the CSP build, so the app's `script-src` can stay a hash allow-list with no `'unsafe-eval'`. That single fact dictates most of this page: inline JavaScript in HTML is not evaluated, so everything expressive lives in registered TypeScript components. ## Rule one: components are registered, not inlined Every `x-data` references a component registered with `Alpine.data` in `web/src` — the attribute value is the component name (optionally with arguments), never an object literal: ```typescript // web/src/admin.ts Alpine.data("admin", () => ({ /* state + methods */ })); ``` ```html ``` Arguments work, but the **whole attribute value must be the expression** — templ does not interpolate `{ expr }` inside quoted attribute values, so `x-data="sidebarShell('{ col.ID }')"` would ship a literal `{ col.ID }` to the browser: ```html
``` Free-text arguments that could contain quotes go through `templates.JSONArg`, never string concatenation. ## Stores: state that survives htmx swaps Alpine's reactivity is DOM-local; when htmx swaps a node, its `x-data` state dies with it. State that must survive swaps lives in a store — `web/src/bookPicker.ts` is the canonical example: ```typescript Alpine.store("bookPicker", { isOpen: false, selectedBooks: new Set(), open() { /* … */ }, toggleBook(id: string) { /* … */ }, }); ``` ```html ``` The server returns HTML (checked checkboxes included); the store tracks selection across swaps; nobody rebuilds markup from JSON. ## Local state: plain `x-data` on the smallest element Ephemeral UI (a dropdown, a panel toggle) needs no store and no round-trip: ```html
…
``` ## The banned list — all enforced by the CSP build - **No inline handlers**, in any form: `onclick=`, `onerror=`, templ-brace variants — hash-mode CSP refuses them unconditionally. Rows generated in TypeScript carry `data-*` attributes and bind listeners after swap (see `custom-section-builder.ts`). - **No `hx-on*` attributes and no `js:`-prefixed `hx-vals`** — both evaluate strings, which the CSP build cannot do. Plain JSON `hx-vals` only (`templates/utils.go` has the helper). - **No inline `