Overview
Alpine.js is the project’s interactive island framework. Zero-JS by default; Alpine hydrates only interactive components.
Directive Syntax in Astro
Always use full-form directives (x-bind:, x-on:, x-transition:) inside Astro template expressions (ternaries, && conditionals, map() calls). Prettier’s Astro parser cannot parse shorthands (:, @) in these contexts. Full-form directives are safe everywhere.
State Ownership Pattern
The parent element owns x-data. Child primitives (like Drawer) accept a binding prop specifying the Alpine variable name to read/write. This keeps state management explicit and composable.
<div x-data="{ drawerOpen: false }">
<button x-on:click="drawerOpen = true">Open</button>
<Drawer binding="drawerOpen" ... />
</div>
Single-Expand Accordion Pattern
Parent defines x-data="{ active: null }". Each item uses a computed getter/setter:
get expanded() { return this.active === this.id },
set expanded(value) { this.active = value ? this.id : null }
Toggle via @click="expanded = !expanded". Only one item open at a time.
Inline Methods Pattern
For simple self-contained interactions (e.g., CopyButton), define methods directly in x-data:
x-data="{ copied: false, copy() { navigator.clipboard.writeText(this.text).then(() => { this.copied = true; setTimeout(() => { this.copied = false; }, 2000); }); } }"
Visibility and Transitions
x-showfor CSS display toggle (element stays in DOM).x-cloakprevents flash of unstyled content.- Alpine transition classes (named) for enter/leave animations using project duration/easing tokens.
Focus Management
x-trap.noscrollfor focus trapping with scroll lock (used in Drawer, SearchModal).x-on:keydown.escapefor ESC-to-close patterns.
Progressive Enhancement
Components degrade to readable HTML without JS. Forms show <noscript> fallback messages. Non-interactive content is always visible.
Script Tag Rules
- Do not wrap
<script>in Astro template expressions — breaks Prettier. - Use unconditional
<script>with runtime DOM guard. - Do not add
data-*attributes to<script>tags — forcesis:inlinemode, disabling TypeScript.