Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Alpine.js is a lightweight JavaScript framework for adding interactivity to HTML markup. It provides reactive data, event handling, and DOM manipulation through HTML attributes — like a modern jQuery replacement with declarative syntax.
.claude/skills/terminalskills-alpine/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 80% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 56% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 100% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 260% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 88% | 0% |
Alpine.js adds reactive behavior directly in HTML markup using x- attributes. It's ideal for adding interactivity to server-rendered pages without a build step or SPA framework.
html<!-- index.html — add Alpine via CDN --> <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script> <!-- Or: npm install alpinejs -->
javascript// main.js — npm module setup import Alpine from 'alpinejs'; window.Alpine = Alpine; Alpine.start();
html<!-- templates/basics.html — fundamental Alpine directives --> <div x-data="{ open: false, count: 0 }"> <!-- Toggle visibility --> <button @click="open = !open">Toggle</button> <div x-show="open" x-transition> <p>This content can be toggled</p> </div> <!-- Reactive counter --> <p>Count: <span x-text="count"></span></p> <button @click="count++">Increment</button> <!-- Conditional rendering (removes from DOM) --> <template x-if="count > 5"> <p>Count is greater than 5!</p> </template> </div>
html<!-- templates/binding.html — two-way binding and attribute binding --> <div x-data="{ name: '', color: 'blue' }"> <!-- Two-way binding --> <input x-model="name" placeholder="Your name" /> <p>Hello, <span x-text="name || 'stranger'"></span>!</p> <!-- Attribute binding --> <div :class="{ 'text-red': color === 'red', 'text-blue': color === 'blue' }"> Colored text </div> <select x-model="color"> <option value="blue">Blue</option> <option value="red">Red</option> </select> <!-- Style binding --> <div :style="{ color: color, fontWeight: name ? 'bold' : 'normal' }"> Dynamic styles </div> </div>
html<!-- templates/loops.html — iterating over data --> <div x-data="{ items: ['Apples', 'Bananas', 'Cherries'], newItem: '' }"> <ul> <template x-for="(item, index) in items" :key="index"> <li> <span x-text="item"></span> <button @click="items.splice(index, 1)">×</button> </li> </template> </ul> <form @submit.prevent="items.push(newItem); newItem = ''"> <input x-model="newItem" placeholder="Add item" /> <button type="submit">Add</button> </form> </div>
html<!-- templates/events.html — event modifiers and custom events --> <div x-data="{ count: 0 }"> <!-- Modifiers --> <button @click.prevent="count++">Prevent default</button> <button @click.once="alert('Only once!')">Click once</button> <input @keydown.enter="submitForm()" @keydown.escape="cancel()" /> <!-- Debounce --> <input @input.debounce.300ms="search($event.target.value)" placeholder="Search..." /> <!-- Listen to events from window --> <div @custom-event.window="count++"> Count: <span x-text="count"></span> </div> <!-- Dispatch custom event --> <button @click="$dispatch('custom-event')">Dispatch</button> </div>
html<!-- templates/dropdown.html — dropdown component --> <div x-data="{ open: false }" @click.outside="open = false"> <button @click="open = !open"> Menu <span :class="{ 'rotate-180': open }" x-text="open ? '▲' : '▼'"></span> </button> <div x-show="open" x-transition.origin.top.left @keydown.escape.window="open = false" class="dropdown-menu"> <a href="/profile">Profile</a> <a href="/settings">Settings</a> <button @click="$dispatch('logout')">Logout</button> </div> </div>
html<!-- templates/tabs.html — tabs component --> <div x-data="{ activeTab: 'general' }"> <nav> <button @click="activeTab = 'general'" :class="{ active: activeTab === 'general' }">General</button> <button @click="activeTab = 'security'" :class="{ active: activeTab === 'security' }">Security</button> </nav> <div x-show="activeTab === 'general'">General settings...</div> <div x-show="activeTab === 'security'">Security settings...</div> </div>
html<!-- templates/reusable.html — extracting reusable components --> <script> // Register reusable component document.addEventListener('alpine:init', () => { Alpine.data('todoList', () => ({ items: [], newItem: '', add() { if (this.newItem.trim()) { this.items.push({ text: this.newItem, done: false }); this.newItem = ''; } }, remove(index) { this.items.splice(index, 1); }, get remaining() { return this.items.filter(i => !i.done).length; }, })); }); </script> <!-- Use anywhere --> <div x-data="todoList"> <form @submit.prevent="add"> <input x-model="newItem" placeholder="New todo" /> <button type="submit">Add</button> </form> <p x-text="`${remaining} remaining`"></p> <template x-for="(item, i) in items" :key="i"> <div> <input type="checkbox" x-model="item.done" /> <span x-text="item.text" :class="{ 'line-through': item.done }"></span> <button @click="remove(i)">×</button> </div> </template> </div>
html<!-- templates/alpine-htmx.html — Alpine + htmx together --> <div x-data="{ editing: false }" id="article-42"> <div x-show="!editing"> <h2>Article Title</h2> <button @click="editing = true">Edit</button> <button hx-delete="/articles/42" hx-target="#article-42" hx-swap="outerHTML">Delete</button> </div> <form x-show="editing" hx-put="/articles/42" hx-target="#article-42" hx-swap="outerHTML"> <input name="title" value="Article Title" /> <button type="submit">Save</button> <button type="button" @click="editing = false">Cancel</button> </form> </div>
html<!-- templates/stores.html — Alpine stores for shared state --> <script> document.addEventListener('alpine:init', () => { Alpine.store('notifications', { items: [], add(msg) { this.items.push({ text: msg, id: Date.now() }) }, remove(id) { this.items = this.items.filter(n => n.id !== id) }, }); }); </script> <div x-data @click="$store.notifications.add('Button clicked!')">Click me</div> <div x-data> <template x-for="n in $store.notifications.items" :key="n.id"> <div class="toast" x-text="n.text" @click="$store.notifications.remove(n.id)"></div> </template> </div>
html<!-- templates/magic.html — Alpine magic properties --> <div x-data="{ items: [] }"> <!-- $refs — reference DOM elements --> <input x-ref="input" /> <button @click="$refs.input.focus()">Focus input</button> <!-- $nextTick — run after DOM update --> <button @click="items.push('new'); $nextTick(() => $refs.list.scrollTo(0, 99999))"> Add & scroll </button> <div x-ref="list" style="max-height:200px;overflow:auto"> <template x-for="item in items"><p x-text="item"></p></template> </div> <!-- $watch — react to data changes --> <div x-init="$watch('items', (val) => console.log('items changed:', val))"></div> </div>
x-data on a parent element to define reactive scope — everything inside shares that statex-show for toggling visibility (CSS), x-if for conditional DOM insertionx-model for two-way binding on inputs, selects, checkboxes.prevent, .stop, .debounce, .outside) to reduce boilerplateAlpine.data() to extract reusable components with methods and computed propertiesAlpine.store() for global state shared across components| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 14,490 | 14,851 | +2% | 1 | 1 | 0% | 3,435 | 6,168 | +80% | 0 | 0 | — |
case-02 | fail→pass | 19,745 | 7,681 | -61% | 1 | 1 | 0% | 2,527 | 3,941 | +56% | 0 | 0 | — |
case-03 | fail→pass | 7,306 | 3,420 | -53% | 1 | 1 | 0% | 1,501 | 3,004 | +100% | 0 | 0 | — |
case-04 | fail→pass | 4,102 | 3,613 | -12% | 1 | 1 | 0% | 853 | 3,070 | +260% | 0 | 0 | — |
case-05 | pass→pass | 4,977 | 2,771 | -44% | 1 | 1 | 0% | 905 | 2,857 | +216% | 0 | 0 | — |
case-06 | pass→pass | 4,654 | 3,314 | -29% | 1 | 1 | 0% | 980 | 3,017 | +208% | 0 | 0 | — |
case-07 | fail→pass | 9,357 | 5,032 | -46% | 1 | 1 | 0% | 1,716 | 3,218 | +88% | 0 | 0 | — |
case-08 | fail→pass | 4,226 | 3,154 | -25% | 1 | 1 | 0% | 847 | 2,872 | +239% | 0 | 0 | — |
case-09 | fail→pass | 6,483 | 3,437 | -47% | 1 | 1 | 0% | 1,295 | 2,951 | +128% | 0 | 0 | — |
case-10 | fail→pass | 7,371 | 6,331 | -14% | 1 | 1 | 0% | 1,540 | 3,652 | +137% | 0 | 0 | — |
case-11 | fail→pass | 4,615 | 3,598 | -22% | 1 | 1 | 0% | 909 | 2,937 | +223% | 0 | 0 | — |
case-12 | fail→pass | 6,360 | 2,273 | -64% | 1 | 1 | 0% | 1,152 | 2,689 | +133% | 0 | 0 | — |
case-13 | fail→pass | 11,191 | 5,013 | -55% | 1 | 1 | 0% | 2,680 | 3,328 | +24% | 0 | 0 | — |
case-14 | pass→pass | 7,807 | 7,780 | -0% | 1 | 1 | 0% | 1,740 | 4,249 | +144% | 0 | 0 | — |
case-15 | pass→pass | 10,203 | 4,266 | -58% | 1 | 1 | 0% | 1,889 | 3,091 | +64% | 0 | 0 | — |
case-16 | fail→pass | 7,841 | 8,277 | +6% | 1 | 1 | 0% | 1,520 | 3,744 | +146% | 0 | 0 | — |
case-17 | fail→pass | 7,653 | 2,937 | -62% | 1 | 1 | 0% | 1,631 | 2,871 | +76% | 0 | 0 | — |
case-18 | fail→pass | 9,622 | 4,584 | -52% | 1 | 1 | 0% | 2,156 | 3,283 | +52% | 0 | 0 | — |
case-19 | fail→pass | 7,610 | 3,221 | -58% | 1 | 1 | 0% | 1,282 | 2,794 | +118% | 0 | 0 | — |
case-20 | fail→pass | 10,758 | 5,964 | -45% | 1 | 1 | 0% | 2,181 | 3,440 | +58% | 0 | 0 | — |
case-21 | pass→fail | 10,336 | 9,510 | -8% | 1 | 1 | 0% | 2,042 | 4,221 | +107% | 0 | 0 | — |
case-22 | pass→pass | 5,439 | 4,168 | -23% | 1 | 1 | 0% | 1,053 | 3,076 | +192% | 0 | 0 | — |
case-23 | pass→pass | 4,151 | 5,378 | +30% | 1 | 1 | 0% | 734 | 3,379 | +360% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +65 percentage points is the difference between those two pass rates over the 23 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.