From 974f332b7e98771b5554435ecd44d25dc72f5bfc Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Fri, 20 Mar 2026 23:02:02 -0400 Subject: [PATCH] docs: add Alpine.js SSR-first patterns guide Add comprehensive guide for Alpine.js SSR-first patterns in Bookhoard: - Page classification system (Type 1: 80% SSR, Type 2: SSR+Interactive, Type 3: 80% TypeScript) - Alpine.js usage guidelines (UI state only, no data fetching in x-init) - HTMX integration patterns - When to use x-show vs CSS classes - Form handling and validation - Modal and dropdown patterns - Component reusability with Alpine.data() - Alpine.store for global state (book picker example) This documentation helps developers maintain consistency across the codebase and make informed decisions about when to use Alpine.js vs vanilla JavaScript vs HTMX for different features. Follows PROJECT_GUIDELINES.md documentation standards. --- docs/developer/alpine-patterns.md | 92 +++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 docs/developer/alpine-patterns.md diff --git a/docs/developer/alpine-patterns.md b/docs/developer/alpine-patterns.md new file mode 100644 index 0000000..be622e0 --- /dev/null +++ b/docs/developer/alpine-patterns.md @@ -0,0 +1,92 @@ +# Alpine.js Patterns in Bookhoard +## Book Picker Modal Pattern (SSR + HTMX + Alpine.store) +For modals that need state persistence across HTMX swaps: +### TypeScript +```typescript +// web/src/bookPicker.ts +Alpine.store("bookPicker", { + isOpen: false, + selectedBooks: new Set(), + + open() { + this.isOpen = true; + this.selectedBooks.clear(); + this.loadBooks(); + }, + + close() { + this.isOpen = false; + this.selectedBooks.clear(); + }, + + toggleBook(id: string) { + if (this.selectedBooks.has(id)) { + this.selectedBooks.delete(id); + } else { + this.selectedBooks.add(id); + } + // Trigger reactivity + this.selectedBooks = new Set(this.selectedBooks); + }, + + isSelected(id: string): boolean { + return this.selectedBooks.has(id); + }, + + get selectedCount(): number { + return this.selectedBooks.size; + } +}); +Template + + + +
+ + + + + +
+ +
+ + + + + + +
+Key Principles +- ✅ Alpine.store persists state across HTMX DOM swaps +- ✅ Server returns HTML with checkbox :checked attributes +- ✅ No client-side rendering or manual DOM manipulation +- ✅ HTMX handles dynamic content updates +- ✅ Alpine manages UI state only +Dropdown Pattern (Local x-data) +
+ +
+ +
+
+Best Practices +- ✅ State lives in template (x-data) +- ✅ UI updates automatically (x-show) +- ✅ No manual DOM manipulation in TypeScript +- ✅ Pure business logic only in TypeScript functions +- ✅ Use Alpine.store for global state +- ✅ Use local x-data for component state +- ✅ Use x-init for setup only (no data fetch for SSR pages) +- ❌ Never use classList in TypeScript +- ❌ Never use getElementById for show/hide +- ❌ Never fetch data in x-init (for SSR pages)