# 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 `