Alpine.js Lightweight JS
Custom skill created by Maggie Lerman.
Use this skill to add small, resilient interactivity to otherwise static sites. Prefer HTML-first progressive enhancement, accessible native behavior, and tiny state scopes over large client-side frameworks.
Start Here
- Confirm the interaction actually needs JavaScript. Prefer native HTML, CSS, server-rendered variants, and simple links/forms when they solve the problem.
- Inspect how JavaScript is loaded: CDN script, bundled npm import, deferred module, site-wide entrypoint, page-specific bundle, CSP build, or existing helper.
- If the repo's Alpine version, CDN pin, plugin compatibility, or CSP package matters, pair with
jamstack-version-auditor before recommending an update.
- Keep Alpine component state local with
x-data unless multiple distant components need shared state.
- Use
Alpine.data() for repeated or complex components. Use Alpine.store() for genuinely shared state.
- Preserve core content and navigation without JavaScript whenever practical.
Alpine Patterns
- Use
x-data to establish component scope. Alpine directives like x-bind, x-on, and x-transition need an Alpine component context.
- Use
x-show for toggles that remain in the DOM; pair it with x-transition for simple transitions.
- Use
x-if only when an element should be created/destroyed; do not expect x-transition to work with x-if.
- Use
x-bind or : for attributes and classes derived from state.
- Use
x-on or @ for events, with keyboard and outside-click modifiers when appropriate.
- Use
x-cloak for content that should stay hidden until Alpine initializes; make sure the cloak CSS exists.
- Keep inline expressions short. Extract complex logic into methods, getters, or
Alpine.data().
- For strict Content Security Policy sites, use
@alpinejs/csp and avoid unsupported global expressions.
Accessibility Rules
- Match state to ARIA:
aria-expanded, aria-controls, aria-selected, aria-current, hidden, inert, or dialog semantics as appropriate.
- Keep keyboard support complete for menus, dialogs, tabs, disclosures, and combobox-like controls.
- Manage focus deliberately for dialogs, popovers, and menu-like interactions.
- Do not hide important content in a JS-only path. Server-render default content when possible.
- Respect reduced motion for custom transitions if the repo has motion utilities or CSS support.
Tailwind And Nunjucks
Pair with tailwindcss-expert for styling and eleventy-jamstack-expert in 11ty repos.
- Put state styling in Tailwind variants when possible:
aria-expanded:*, data-*, group-*, peer-*, or class maps from Alpine state.
- Keep Tailwind class names statically discoverable. Avoid constructing partial utility names in Alpine expressions.
- In Nunjucks macros/includes, pass simple behavior options and emit complete Alpine attributes and complete class strings.
Example disclosure:
<section x-data="{ open: false }" class="border border-slate-200">
<button
type="button"
class="flex w-full items-center justify-between px-4 py-3 text-left font-medium"
:aria-expanded="open.toString()"
@click="open = !open"
>
Details
</button>
<div x-show="open" x-transition class="px-4 pb-4">
Content that is useful even when rendered statically.
</div>
</section>
Quality Gate
Before completion:
- Verify the page still has useful content before Alpine starts.
- Test mouse, keyboard, focus, and escape/outside-click behavior where relevant.
- Test with the repo's CSP assumptions.
- Run the repo's build and browser smoke test.
- Check for console errors and hydration-like assumptions that do not belong in a static site.
References
Read references/alpine-patterns.md for component patterns and references/official-docs.md for primary docs.
1---2name: alpinejs-lightweight-js3description: Custom skill created by Maggie Lerman. Alpine.js and lightweight progressive JavaScript workflow for static/JAMstack sites. Use when working with x-data, x-show, x-bind, x-on, x-transition, Alpine.data/store, @alpinejs plugins, CSP-safe Alpine, dropdowns, accordions, tabs, dialogs, filters, or minimal client JS.4---56# Alpine.js Lightweight JS78Custom skill created by Maggie Lerman.910Use this skill to add small, resilient interactivity to otherwise static sites. Prefer HTML-first progressive enhancement, accessible native behavior, and tiny state scopes over large client-side frameworks.1112## Start Here13141. Confirm the interaction actually needs JavaScript. Prefer native HTML, CSS, server-rendered variants, and simple links/forms when they solve the problem.152. Inspect how JavaScript is loaded: CDN script, bundled npm import, deferred module, site-wide entrypoint, page-specific bundle, CSP build, or existing helper.163. If the repo's Alpine version, CDN pin, plugin compatibility, or CSP package matters, pair with `jamstack-version-auditor` before recommending an update.174. Keep Alpine component state local with `x-data` unless multiple distant components need shared state.185. Use `Alpine.data()` for repeated or complex components. Use `Alpine.store()` for genuinely shared state.196. Preserve core content and navigation without JavaScript whenever practical.2021## Alpine Patterns2223- Use `x-data` to establish component scope. Alpine directives like `x-bind`, `x-on`, and `x-transition` need an Alpine component context.24- Use `x-show` for toggles that remain in the DOM; pair it with `x-transition` for simple transitions.25- Use `x-if` only when an element should be created/destroyed; do not expect `x-transition` to work with `x-if`.26- Use `x-bind` or `:` for attributes and classes derived from state.27- Use `x-on` or `@` for events, with keyboard and outside-click modifiers when appropriate.28- Use `x-cloak` for content that should stay hidden until Alpine initializes; make sure the cloak CSS exists.29- Keep inline expressions short. Extract complex logic into methods, getters, or `Alpine.data()`.30- For strict Content Security Policy sites, use `@alpinejs/csp` and avoid unsupported global expressions.3132## Accessibility Rules3334- Match state to ARIA: `aria-expanded`, `aria-controls`, `aria-selected`, `aria-current`, `hidden`, `inert`, or dialog semantics as appropriate.35- Keep keyboard support complete for menus, dialogs, tabs, disclosures, and combobox-like controls.36- Manage focus deliberately for dialogs, popovers, and menu-like interactions.37- Do not hide important content in a JS-only path. Server-render default content when possible.38- Respect reduced motion for custom transitions if the repo has motion utilities or CSS support.3940## Tailwind And Nunjucks4142Pair with `tailwindcss-expert` for styling and `eleventy-jamstack-expert` in 11ty repos.4344- Put state styling in Tailwind variants when possible: `aria-expanded:*`, `data-*`, `group-*`, `peer-*`, or class maps from Alpine state.45- Keep Tailwind class names statically discoverable. Avoid constructing partial utility names in Alpine expressions.46- In Nunjucks macros/includes, pass simple behavior options and emit complete Alpine attributes and complete class strings.4748Example disclosure:4950```html51<section x-data="{ open: false }" class="border border-slate-200">52 <button53 type="button"54 class="flex w-full items-center justify-between px-4 py-3 text-left font-medium"55 :aria-expanded="open.toString()"56 @click="open = !open"57 >58 Details59 </button>60 <div x-show="open" x-transition class="px-4 pb-4">61 Content that is useful even when rendered statically.62 </div>63</section>64```6566## Quality Gate6768Before completion:6970- Verify the page still has useful content before Alpine starts.71- Test mouse, keyboard, focus, and escape/outside-click behavior where relevant.72- Test with the repo's CSP assumptions.73- Run the repo's build and browser smoke test.74- Check for console errors and hydration-like assumptions that do not belong in a static site.7576## References7778Read `references/alpine-patterns.md` for component patterns and `references/official-docs.md` for primary docs.