Alpine.js Samples

Alpine.js examples including directives, components, reactive data, and modern Alpine patterns

Key Facts

Category
Web Frameworks
Items
4
Format Families
sample

Sample Overview

Alpine.js examples including directives, components, reactive data, and modern Alpine patterns This sample set belongs to Web Frameworks and can be used to test related workflows inside Elysia Tools.

💻 Alpine.js Hello World html

🟢 simple

Basic Alpine.js setup and Hello World examples

<!-- Alpine.js Hello World Examples -->

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Alpine.js Hello World</title>
    <!-- Alpine.js CDN -->
    <script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        .demo { margin: 20px 0; padding: 15px; border: 1px solid #ddd; border-radius: 5px; }
        .demo h3 { margin-top: 0; color: #333; }
        button { margin: 5px; padding: 8px 16px; cursor: pointer; }
        input, select { margin: 5px; padding: 5px; }
        .hidden { display: none; }
    </style>
</head>
<body>
    <h1>Alpine.js Hello World Examples</h1>

    <!-- 1. Basic reactive text -->
    <div class="demo">
        <h3>1. Basic Reactive Text</h3>
        <div x-data="{ message: 'Hello, Alpine.js!' }">
            <p x-text="message"></p>
            <input
                type="text"
                x-model="message"
                placeholder="Type something..."
            >
        </div>
    </div>

    <!-- 2. Simple counter -->
    <div class="demo">
        <h3>2. Simple Counter</h3>
        <div x-data="{ count: 0 }">
            <button x-on:click="count--">Decrement</button>
            <span x-text="count"></span>
            <button x-on:click="count++">Increment</button>
            <br><br>
            <button x-on:click="count = 0">Reset</button>
        </div>
    </div>

    <!-- 3. Toggle visibility -->
    <div class="demo">
        <h3>3. Toggle Visibility</h3>
        <div x-data="{ open: false }">
            <button x-on:click="open = !open">
                <span x-show="!open">Show Content</span>
                <span x-show="open">Hide Content</span>
            </button>

            <div x-show="open" x-transition>
                <p style="margin-top: 10px; padding: 10px; background: #f0f0f0; border-radius: 3px;">
                    This content can be toggled on and off!
                </p>
            </div>
        </div>
    </div>

    <!-- 4. User greeting -->
    <div class="demo">
        <h3>4. User Greeting</h3>
        <div x-data="{
            name: 'Guest',
            greeting: 'Welcome'
        }">
            <input
                type="text"
                x-model="name"
                placeholder="Enter your name"
                x-on:input="greeting = 'Hello, ' + name + '!'"
            >
            <p x-text="greeting" x-show="name !== 'Guest'"></p>
        </div>
    </div>

    <!-- 5. Multiple data properties -->
    <div class="demo">
        <h3>5. Multiple Data Properties</h3>
        <div x-data="{
            firstName: 'John',
            lastName: 'Doe',
            age: 25,
            city: 'New York',
            get fullName() {
                return this.firstName + ' ' + this.lastName;
            },
            get info() {
                return this.fullName + ' (' + this.age + ', ' + this.city + ')';
            }
        }">
            <input type="text" x-model="firstName" placeholder="First Name">
            <input type="text" x-model="lastName" placeholder="Last Name">
            <input type="number" x-model="age" placeholder="Age">
            <input type="text" x-model="city" placeholder="City">

            <div style="margin-top: 10px;">
                <strong>Full Name:</strong> <span x-text="fullName"></span><br>
                <strong>Info:</strong> <span x-text="info"></span>
            </div>
        </div>
    </div>

    <!-- 6. Array manipulation -->
    <div class="demo">
        <h3>6. Array Manipulation</h3>
        <div x-data="{
            items: ['Apple', 'Banana', 'Orange'],
            newItem: '',
            addItem() {
                if (this.newItem.trim()) {
                    this.items.push(this.newItem.trim());
                    this.newItem = '';
                }
            },
            removeItem(index) {
                this.items.splice(index, 1);
            }
        }">
            <div style="margin-bottom: 10px;">
                <input
                    type="text"
                    x-model="newItem"
                    placeholder="Add new item"
                    x-on:keyup.enter="addItem()"
                >
                <button x-on:click="addItem()">Add</button>
            </div>

            <ul>
                <template x-for="(item, index) in items">
                    <li>
                        <span x-text="item"></span>
                        <button
                            x-on:click="removeItem(index)"
                            style="margin-left: 10px;"
                        >
                            Remove
                        </button>
                    </li>
                </template>
            </ul>

            <p x-show="items.length === 0">No items in the list</p>
            <p x-show="items.length > 0">
                Total items: <span x-text="items.length"></span>
            </p>
        </div>
    </div>

    <!-- 7. Todo list -->
    <div class="demo">
        <h3>7. Todo List</h3>
        <div x-data="{
            todos: [
                { text: 'Learn Alpine.js', completed: false },
                { text: 'Build something cool', completed: false }
            ],
            newTodo: '',
            addTodo() {
                if (this.newTodo.trim()) {
                    this.todos.push({
                        text: this.newTodo.trim(),
                        completed: false
                    });
                    this.newTodo = '';
                }
            },
            toggleTodo(index) {
                this.todos[index].completed = !this.todos[index].completed;
            },
            clearCompleted() {
                this.todos = this.todos.filter(todo => !todo.completed);
            },
            get completedCount() {
                return this.todos.filter(todo => todo.completed).length;
            },
            get activeCount() {
                return this.todos.filter(todo => !todo.completed).length;
            }
        }">
            <form x-on:submit.prevent="addTodo()" style="margin-bottom: 15px;">
                <input
                    type="text"
                    x-model="newTodo"
                    placeholder="What needs to be done?"
                    style="width: 300px;"
                >
                <button type="submit">Add</button>
            </form>

            <ul style="list-style: none; padding: 0;">
                <template x-for="(todo, index) in todos">
                    <li style="margin: 5px 0; padding: 8px; background: #f9f9f9; border-radius: 3px;">
                        <input
                            type="checkbox"
                            x-model="todo.completed"
                            style="margin-right: 8px;"
                        >
                        <span
                            x-text="todo.text"
                            :style="todo.completed ? 'text-decoration: line-through; opacity: 0.6;' : ''"
                        ></span>
                    </li>
                </template>
            </ul>

            <div style="margin-top: 15px;">
                <span x-text="activeCount"></span> active,
                <span x-text="completedCount"></span> completed
                <button
                    x-on:click="clearCompleted()"
                    x-show="completedCount > 0"
                    style="margin-left: 10px;"
                >
                    Clear completed
                </button>
            </div>
        </div>
    </div>

    <!-- 8. Form with validation -->
    <div class="demo">
        <h3>8. Form with Validation</h3>
        <div x-data="{
            name: '',
            email: '',
            message: '',
            errors: {},
            validate() {
                this.errors = {};

                if (!this.name.trim()) {
                    this.errors.name = 'Name is required';
                }

                if (!this.email.trim()) {
                    this.errors.email = 'Email is required';
                } else if (!this.email.includes('@')) {
                    this.errors.email = 'Email is invalid';
                }

                if (!this.message.trim()) {
                    this.errors.message = 'Message is required';
                }

                return Object.keys(this.errors).length === 0;
            },
            submit() {
                if (this.validate()) {
                    alert('Form submitted successfully!\n' +
                          'Name: ' + this.name + '\n' +
                          'Email: ' + this.email + '\n' +
                          'Message: ' + this.message);
                }
            }
        }">
            <form x-on:submit.prevent="submit()">
                <div style="margin-bottom: 10px;">
                    <label>Name:</label><br>
                    <input
                        type="text"
                        x-model="name"
                        style="width: 250px;"
                    >
                    <span x-show="errors.name" style="color: red; display: block;">
                        <span x-text="errors.name"></span>
                    </span>
                </div>

                <div style="margin-bottom: 10px;">
                    <label>Email:</label><br>
                    <input
                        type="email"
                        x-model="email"
                        style="width: 250px;"
                    >
                    <span x-show="errors.email" style="color: red; display: block;">
                        <span x-text="errors.email"></span>
                    </span>
                </div>

                <div style="margin-bottom: 10px;">
                    <label>Message:</label><br>
                    <textarea
                        x-model="message"
                        rows="4"
                        style="width: 250px;"
                    ></textarea>
                    <span x-show="errors.message" style="color: red; display: block;">
                        <span x-text="errors.message"></span>
                    </span>
                </div>

                <button type="submit">Submit</button>
            </form>
        </div>
    </div>

    <!-- 9. Color picker -->
    <div class="demo">
        <h3>9. Color Picker</h3>
        <div x-data="{
            color: '#3B82F6',
            presetColors: ['#EF4444', '#F59E0B', '#10B981', '#3B82F6', '#8B5CF6', '#EC4899'],
            setColor(newColor) {
                this.color = newColor;
            }
        }">
            <div style="display: flex; align-items: center; gap: 15px;">
                <label>Choose Color:</label>
                <input
                    type="color"
                    x-model="color"
                    style="width: 50px; height: 40px;"
                >
                <div
                    style="width: 100px; height: 40px; background-color: white; border: 2px solid #ddd; border-radius: 5px;"
                    :style="'background-color: ' + color"
                ></div>
                <span x-text="color"></span>
            </div>

            <div style="margin-top: 15px;">
                <p>Quick Colors:</p>
                <template x-for="presetColor in presetColors">
                    <button
                        x-on:click="setColor(presetColor)"
                        :style="'background-color: ' + presetColor + '; color: white; margin: 2px; padding: 8px; border: none; border-radius: 3px; cursor: pointer;'"
                        x-text="presetColor"
                    ></button>
                </template>
            </div>
        </div>
    </div>

    <!-- 10. Shopping cart -->
    <div class="demo">
        <h3>10. Shopping Cart</h3>
        <div x-data="{
            products: [
                { id: 1, name: 'Laptop', price: 999 },
                { id: 2, name: 'Mouse', price: 25 },
                { id: 3, name: 'Keyboard', price: 75 }
            ],
            cart: [],
            addToCart(product) {
                const existingItem = this.cart.find(item => item.id === product.id);
                if (existingItem) {
                    existingItem.quantity++;
                } else {
                    this.cart.push({ ...product, quantity: 1 });
                }
            },
            removeFromCart(productId) {
                this.cart = this.cart.filter(item => item.id !== productId);
            },
            updateQuantity(productId, quantity) {
                const item = this.cart.find(item => item.id === productId);
                if (item) {
                    if (quantity <= 0) {
                        this.removeFromCart(productId);
                    } else {
                        item.quantity = quantity;
                    }
                }
            },
            get total() {
                return this.cart.reduce((sum, item) => sum + (item.price * item.quantity), 0);
            },
            get itemCount() {
                return this.cart.reduce((sum, item) => sum + item.quantity, 0);
            }
        }">
            <div style="display: flex; gap: 30px;">
                <!-- Products -->
                <div style="flex: 1;">
                    <h4>Products</h4>
                    <template x-for="product in products">
                        <div style="border: 1px solid #ddd; padding: 10px; margin-bottom: 10px; border-radius: 5px;">
                            <h5 x-text="product.name"></h5>
                            <p>$<span x-text="product.price"></span></p>
                            <button x-on:click="addToCart(product)">Add to Cart</button>
                        </div>
                    </template>
                </div>

                <!-- Cart -->
                <div style="flex: 1;">
                    <h4>Cart (<span x-text="itemCount"></span> items)</h4>
                    <div x-show="cart.length === 0">
                        <p>Your cart is empty</p>
                    </div>

                    <template x-for="item in cart">
                        <div style="border: 1px solid #ddd; padding: 10px; margin-bottom: 10px; border-radius: 5px;">
                            <h5 x-text="item.name"></h5>
                            <p>$<span x-text="item.price"></span> x
                               <input
                                   type="number"
                                   x-model.number="item.quantity"
                                   min="1"
                                   style="width: 60px;"
                               > = $<span x-text="item.price * item.quantity"></span>
                            </p>
                            <button x-on:click="removeFromCart(item.id)">Remove</button>
                        </div>
                    </template>

                    <div x-show="cart.length > 0" style="margin-top: 15px; padding-top: 15px; border-top: 2px solid #333;">
                        <strong>Total: $<span x-text="total"></span></strong>
                    </div>
                </div>
            </div>
        </div>
    </div>

</body>
</html>

💻 Alpine.js Directives html

🟡 intermediate

Complete guide to Alpine.js directives and their usage

<!-- Alpine.js Directives Guide -->

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Alpine.js Directives</title>
    <script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        .demo { margin: 20px 0; padding: 15px; border: 1px solid #ddd; border-radius: 5px; }
        .demo h3 { margin-top: 0; color: #333; }
        button { margin: 5px; padding: 8px 16px; cursor: pointer; }
        input, select, textarea { margin: 5px; padding: 5px; }
        .highlight { background: yellow; }
        .error { color: red; }
        .success { color: green; }
        .fade-enter { opacity: 0; }
        .fade-enter-active { transition: opacity 0.5s; }
        .slide-enter { transform: translateX(-100%); }
        .slide-enter-active { transition: transform 0.3s; }
        .box { padding: 20px; margin: 10px 0; background: #f0f0f0; border-radius: 5px; }
    </style>
</head>
<body>
    <h1>Alpine.js Directives Guide</h1>

    <!-- 1. x-data - Component Initialization -->
    <div class="demo">
        <h3>1. x-data - Component Initialization</h3>
        <div x-data="{ message: 'Hello World!' }">
            <p x-text="message"></p>
        </div>

        <div x-data="{
            count: 0,
            increment() { this.count++ },
            decrement() { this.count-- }
        }">
            <button x-on:click="decrement()">-</button>
            <span x-text="count"></span>
            <button x-on:click="increment()">+</button>
        </div>
    </div>

    <!-- 2. x-init - Initialization Hook -->
    <div class="demo">
        <h3>2. x-init - Initialization Hook</h3>
        <div x-data="{
            time: new Date().toLocaleTimeString(),
            startClock() {
                setInterval(() => {
                    this.time = new Date().toLocaleTimeString();
                }, 1000);
            }
        }"
             x-init="startClock()">
            <p>Current time: <span x-text="time"></span></p>
        </div>

        <div x-data="{ data: null }"
             x-init="fetch('https://jsonplaceholder.typicode.com/posts/1')
                     .then(response => response.json())
                     .then(json => data = json)">
            <div x-show="data">
                <p><strong>Title:</strong> <span x-text="data.title"></span></p>
                <p><strong>Body:</strong> <span x-text="data.body"></span></p>
            </div>
            <div x-show="!data">
                <p>Loading...</p>
            </div>
        </div>
    </div>

    <!-- 3. x-show / x-hide - Conditional Rendering -->
    <div class="demo">
        <h3>3. x-show / x-hide - Conditional Rendering</h3>
        <div x-data="{
            isVisible: true,
            hasError: false,
            isLoading: false
        }">
            <button x-on:click="isVisible = !isVisible">Toggle Visibility</button>
            <button x-on:click="hasError = !hasError">Toggle Error</button>
            <button x-on:click="isLoading = !isLoading">Toggle Loading</button>

            <div class="box" x-show="isVisible">
                This content is conditionally visible
            </div>

            <div class="box error" x-show="hasError">
                Error message goes here
            </div>

            <div class="box" x-show="isLoading">
                Loading... Please wait
            </div>
        </div>
    </div>

    <!-- 4. x-if - True/False Block Rendering -->
    <div class="demo">
        <h3>4. x-if - True/False Block Rendering</h3>
        <div x-data="{ user: null }">
            <button x-on:click="user = { name: 'John', role: 'admin' }">
                Login as Admin
            </button>
            <button x-on:click="user = { name: 'Guest', role: 'guest' }">
                Login as Guest
            </button>
            <button x-on:click="user = null">
                Logout
            </button>

            <template x-if="user">
                <div class="box">
                    <p>Welcome, <span x-text="user.name"></span>!</p>
                    <template x-if="user.role === 'admin'">
                        <p>You have admin privileges.</p>
                    </template>
                    <template x-if="user.role === 'guest'">
                        <p>You have guest access.</p>
                    </template>
                </div>
            </template>

            <template x-if="!user">
                <div class="box">
                    <p>Please log in to continue.</p>
                </div>
            </template>
        </div>
    </div>

    <!-- 5. x-for - Looping -->
    <div class="demo">
        <h3>5. x-for - Looping</h3>
        <div x-data="{
            users: [
                { id: 1, name: 'Alice', age: 25, role: 'admin' },
                { id: 2, name: 'Bob', age: 30, role: 'user' },
                { id: 3, name: 'Charlie', age: 35, role: 'user' }
            ],
            addUser() {
                this.users.push({
                    id: this.users.length + 1,
                    name: 'User ' + (this.users.length + 1),
                    age: 20,
                    role: 'user'
                });
            }
        }">
            <button x-on:click="addUser()">Add User</button>

            <h4>Users List:</h4>
            <template x-for="user in users">
                <div class="box">
                    <p><strong>Name:</strong> <span x-text="user.name"></span></p>
                    <p><strong>Age:</strong> <span x-text="user.age"></span></p>
                    <p><strong>Role:</strong> <span x-text="user.role"></span></p>
                </div>
            </template>

            <h4>With Index:</h4>
            <template x-for="(user, index) in users">
                <div class="box">
                    <p><span x-text="index + 1"></span>. <span x-text="user.name"></span></p>
                </div>
            </template>

            <h4>Admins Only:</h4>
            <template x-for="user in users.filter(u => u.role === 'admin')">
                <div class="box">
                    <p>Admin: <span x-text="user.name"></span></p>
                </div>
            </template>
        </div>
    </div>

    <!-- 6. x-text - Text Content -->
    <div class="demo">
        <h3>6. x-text - Text Content</h3>
        <div x-data="{
            message: 'Hello Alpine.js',
            htmlContent: '<strong>Bold Text</strong>',
            computeGreeting() {
                return 'Welcome to ' + this.message;
            }
        }">
            <p>Direct text: <span x-text="message"></span></p>
            <p>Computed text: <span x-text="computeGreeting()"></span></p>
            <p>Escaped HTML: <span x-text="htmlContent"></span></p>
            <p>String manipulation: <span x-text="message.toUpperCase()"></span></p>
            <p>Template literal: <span x-text="`Length: ${message.length}`"></span></p>
        </div>
    </div>

    <!-- 7. x-html - HTML Content -->
    <div class="demo">
        <h3>7. x-html - HTML Content (Use with caution)</h3>
        <div x-data="{
            safeHtml: '<p>This is <strong>safe</strong> HTML</p>',
            userInput: '',
            renderedInput: ''
        }">
            <div>
                <label>Enter HTML (will be rendered):</label>
                <input
                    type="text"
                    x-model="userInput"
                    placeholder="Enter some HTML"
                >
                <button x-on:click="renderedInput = userInput">Render HTML</button>
            </div>

            <h4>Safe HTML:</h4>
            <div x-html="safeHtml" class="box"></div>

            <h4>User Input (warning - this can be dangerous!):</h4>
            <div x-show="renderedInput" x-html="renderedInput" class="box"></div>

            <p style="color: orange; font-weight: bold;">
                ⚠️ Warning: x-html can expose you to XSS attacks. Only use with trusted content!
            </p>
        </div>
    </div>

    <!-- 8. x-model - Two-way Data Binding -->
    <div class="demo">
        <h3>8. x-model - Two-way Data Binding</h3>
        <div x-data="{
            text: '',
            number: 0,
            checkbox: false,
            radio: 'option1',
            select: 'apple',
            textarea: '',
            multiSelect: [],
            terms: false
        }">
            <h4>Text Input:</h4>
            <input type="text" x-model="text" placeholder="Type something...">
            <p>You typed: <span x-text="text"></span></p>

            <h4>Number Input:</h4>
            <input type="number" x-model.number="number">
            <p>Number doubled: <span x-text="number * 2"></span></p>

            <h4>Checkbox:</h4>
            <label>
                <input type="checkbox" x-model="checkbox">
                I agree to terms (<span x-text="checkbox ? 'Yes' : 'No'"></span>)
            </label>

            <h4>Radio Buttons:</h4>
            <label><input type="radio" x-model="radio" value="option1"> Option 1</label>
            <label><input type="radio" x-model="radio" value="option2"> Option 2</label>
            <p>Selected: <span x-text="radio"></span></p>

            <h4>Select Dropdown:</h4>
            <select x-model="select">
                <option value="apple">Apple</option>
                <option value="banana">Banana</option>
                <option value="orange">Orange</option>
            </select>
            <p>Selected fruit: <span x-text="select"></span></p>

            <h4>Textarea:</h4>
            <textarea x-model="textarea" rows="3" style="width: 100%;"></textarea>
            <p>Character count: <span x-text="textarea.length"></span></p>

            <h4>Multi-select:</h4>
            <select x-model="multiSelect" multiple>
                <option value="red">Red</option>
                <option value="blue">Blue</option>
                <option value="green">Green</option>
            </select>
            <p>Selected colors: <span x-text="multiSelect.join(', ')"></span></p>
        </div>
    </div>

    <!-- 9. x-on - Event Handling -->
    <div class="demo">
        <h3>9. x-on - Event Handling</h3>
        <div x-data="{
            message: 'Click me!',
            mousePosition: { x: 0, y: 0 },
            keyPress: '',
            formInput: '',
            handleClick() {
                this.message = 'Clicked!';
                setTimeout(() => {
                    this.message = 'Click me!';
                }, 1000);
            },
            handleMouseMove(event) {
                this.mousePosition.x = event.offsetX;
                this.mousePosition.y = event.offsetY;
            },
            handleKeydown(event) {
                this.keyPress = event.key;
            },
            handleSubmit(event) {
                event.preventDefault();
                alert('Form submitted: ' + this.formInput);
                this.formInput = '';
            }
        }">
            <!-- Click events -->
            <button x-on:click="handleClick()" x-text="message"></button>
            <button x-on:click="message = 'Directly changed!'">Direct Change</button>
            <button x-on:click.once="message = 'This only works once!'">Once Only</button>

            <!-- Mouse events -->
            <div
                class="box"
                x-on:mousemove="handleMouseMove($event)"
                style="height: 100px; position: relative;"
            >
                Mouse position: (<span x-text="mousePosition.x"></span>, <span x-text="mousePosition.y"></span>)
            </div>

            <!-- Keyboard events -->
            <input
                type="text"
                x-on:keydown="handleKeydown($event)"
                placeholder="Type to see key pressed"
            >
            <p>Last key: <span x-text="keyPress"></span></p>

            <!-- Form events -->
            <form x-on:submit="handleSubmit($event)">
                <input
                    type="text"
                    x-model="formInput"
                    placeholder="Enter text to submit"
                    required
                >
                <button type="submit">Submit</button>
            </form>

            <!-- Modifier examples -->
            <div style="margin-top: 15px;">
                <p>With modifiers:</p>
                <input
                    type="text"
                    placeholder="Enter and press Enter"
                    x-on:keydown.enter="alert('Enter pressed!')"
                >
                <button
                    x-on:click.shift="alert('Shift+Click!')"
                    x-on:click.ctrl="alert('Ctrl+Click!')"
                    x-on:click.alt="alert('Alt+Click!')"
                >
                    Click with modifier keys
                </button>
            </div>
        </div>
    </div>

    <!-- 10. x-bind - Attribute Binding -->
    <div class="demo">
        <h3>10. x-bind - Attribute Binding</h3>
        <div x-data="{
            color: '#3B82F6',
            width: 100,
            isDisabled: false,
            href: 'https://example.com',
            alt: 'Sample image',
            sizes: ['small', 'medium', 'large'],
            selectedSize: 'medium'
        }">
            <!-- Style binding -->
            <div
                class="box"
                :style="`background-color: ${color}; width: ${width}px; height: 50px;`"
            ></div>

            <!-- Class binding -->
            <div
                class="box"
                :class="{
                    'highlight': color === '#3B82F6',
                    'error': color === '#EF4444',
                    'success': color === '#10B981'
                }"
            >
                Dynamic styling based on color
            </div>

            <!-- Attribute binding -->
            <a :href="href" :target="'_blank'">External Link</a><br>
            <img :src="'https://picsum.photos/100/100'" :alt="alt" style="margin-top: 10px;"><br>

            <!-- Multiple attributes -->
            <button
                :disabled="isDisabled"
                :style="`background-color: ${isDisabled ? '#ccc' : '#3B82F6'};`"
            >
                <span x-text="isDisabled ? 'Disabled' : 'Enabled'"></span>
            </button>
            <button x-on:click="isDisabled = !isDisabled">
                Toggle Disabled
            </button>

            <!-- Select binding -->
            <select x-model="selectedSize">
                <template x-for="size in sizes">
                    <option :value="size" x-text="size"></option>
                </template>
            </select>
            <span>Selected: <span x-text="selectedSize"></span></span>

            <!-- Dynamic attributes -->
            <div
                x-data="{ dynamicAttrs: { id: 'dynamic-id', 'data-value': '123' } }"
                x-bind="dynamicAttrs"
            >
                This div has dynamic attributes
            </div>
        </div>
    </div>

    <!-- 11. x-transition - Transitions -->
    <div class="demo">
        <h3>11. x-transition - Transitions</h3>
        <div x-data="{
            show: false,
            showSlide: false,
            showFade: false,
            items: ['Item 1', 'Item 2', 'Item 3'],
            currentItemIndex: 0,
            nextItem() {
                this.currentItemIndex = (this.currentItemIndex + 1) % this.items.length;
            }
        }">
            <!-- Basic transition -->
            <button x-on:click="show = !show">Toggle Basic Transition</button>
            <div
                x-show="show"
                x-transition
                class="box"
            >
                This content fades in and out
            </div>

            <!-- Fade transition -->
            <button x-on:click="showFade = !showFade">Toggle Fade Transition</button>
            <div
                x-show="showFade"
                x-transition:enter="transition ease-out duration-300"
                x-transition:enter-start="opacity-0"
                x-transition:enter-end="opacity-100"
                x-transition:leave="transition ease-in duration-300"
                x-transition:leave-start="opacity-100"
                x-transition:leave-end="opacity-0"
                class="box"
            >
                This content has a custom fade transition
            </div>

            <!-- Slide transition -->
            <button x-on:click="showSlide = !showSlide">Toggle Slide Transition</button>
            <div
                x-show="showSlide"
                x-transition:enter="transition ease-out duration-500"
                x-transition:enter-start="opacity-0 transform -translate-x-full"
                x-transition:enter-end="opacity-100 transform translate-x-0"
                x-transition:leave="transition ease-in duration-500"
                x-transition:leave-start="opacity-100 transform translate-x-0"
                x-transition:leave-end="opacity-0 transform -translate-x-full"
                class="box"
            >
                This content slides in and out
            </div>

            <!-- Carousel with transitions -->
            <div style="position: relative; height: 100px;">
                <button x-on:click="nextItem()">Next Item</button>
                <template x-for="(item, index) in items">
                    <div
                        x-show="currentItemIndex === index"
                        x-transition:enter="transition ease-out duration-300"
                        x-transition:enter-start="opacity-0"
                        x-transition:enter-end="opacity-100"
                        x-transition:leave="transition ease-in duration-300"
                        x-transition:leave-start="opacity-100"
                        x-transition:leave-end="opacity-0"
                        style="position: absolute; width: 100%;"
                    >
                        <div class="box">
                            <h4 x-text="item"></h4>
                            <p>Item <span x-text="index + 1"></span> of <span x-text="items.length"></span></p>
                        </div>
                    </div>
                </template>
            </div>
        </div>
    </div>

    <!-- 12. x-ref - Element References -->
    <div class="demo">
        <h3>12. x-ref - Element References</h3>
        <div x-data="{
            message: '',
            inputRef: null,
            init() {
                // Auto-focus input after 2 seconds
                setTimeout(() => {
                    if (this.$refs.inputRef) {
                        this.$refs.inputRef.focus();
                    }
                }, 2000);
            },
            focusInput() {
                this.$refs.inputRef.focus();
            },
            scrollToTop() {
                this.$refs.containerRef.scrollTo({ top: 0, behavior: 'smooth' });
            },
            getInputValue() {
                this.message = 'Input value: ' + this.$refs.inputRef.value;
            },
            changeBackgroundColor() {
                this.$refs.boxRef.style.backgroundColor =
                    '#' + Math.floor(Math.random()*16777215).toString(16);
            }
        }"
             x-init="init()">
            <!-- Reference to input -->
            <div>
                <input
                    type="text"
                    x-ref="inputRef"
                    placeholder="This input will auto-focus after 2 seconds"
                >
                <button x-on:click="focusInput()">Focus Input</button>
                <button x-on:click="getInputValue()">Get Value</button>
            </div>

            <!-- Reference to div -->
            <div
                x-ref="boxRef"
                class="box"
                style="margin-top: 10px;"
            >
                Click the button to change my background color
            </div>
            <button x-on:click="changeBackgroundColor()">Change Background</button>

            <!-- Reference to container -->
            <div
                x-ref="containerRef"
                style="height: 100px; overflow-y: auto; border: 1px solid #ccc; margin-top: 10px;"
            >
                <p style="height: 200px;">Scrollable content</p>
                <p>More content...</p>
            </div>
            <button x-on:click="scrollToTop()">Scroll to Top</button>

            <!-- Show message -->
            <p x-show="message" x-text="message" style="margin-top: 10px; color: green;"></p>
        </div>
    </div>

    <!-- 13. x-cloak - Prevent Flash -->
    <div class="demo">
        <h3>13. x-cloak - Prevent Flash</h3>
        <style>
            [x-cloak] { display: none !important; }
        </style>

        <div x-data="{ message: 'This content was hidden initially' }">
            <div x-cloak>
                <h4>This content is hidden until Alpine.js loads:</h4>
                <p x-text="message"></p>
            </div>

            <div>
                <h4>This content is visible immediately:</h4>
                <p>This will show even before Alpine.js loads</p>
            </div>
        </div>

        <p style="color: blue;">
            The content above with x-cloak is hidden initially to prevent the "flash" of unstyled content before Alpine.js initializes.
        </p>
    </div>

</body>
</html>

💻 Alpine.js Magic Properties html

🟡 intermediate

Working with Alpine.js magic properties and built-in helpers

<!-- Alpine.js Magic Properties -->

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Alpine.js Magic Properties</title>
    <script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        .demo { margin: 20px 0; padding: 15px; border: 1px solid #ddd; border-radius: 5px; }
        .demo h3 { margin-top: 0; color: #333; }
        .box { padding: 15px; margin: 10px 0; background: #f8f9fa; border-radius: 5px; border: 1px solid #dee2e6; }
        .highlight { background: #fff3cd; border-color: #ffeaa7; }
        .error { background: #f8d7da; border-color: #f5c6cb; color: #721c24; }
        .success { background: #d4edda; border-color: #c3e6cb; color: #155724; }
        button { margin: 5px; padding: 8px 16px; cursor: pointer; }
        input, select { margin: 5px; padding: 5px; }
        .scroll-container { height: 200px; overflow-y: auto; border: 1px solid #ddd; padding: 10px; }
        .modal {
            position: fixed; top: 0; left: 0; width: 100%; height: 100%;
            background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center;
            z-index: 1000;
        }
        .modal-content { background: white; padding: 20px; border-radius: 8px; max-width: 500px; }
    </style>
</head>
<body>
    <h1>Alpine.js Magic Properties</h1>

    <!-- 1. $el - Current Element -->
    <div class="demo">
        <h3>1. $el - Current Element</h3>
        <div x-data="{ message: 'Click me!' }">
            <button
                x-on:click="$el.textContent = 'Clicked!'"
                style="padding: 10px 20px; font-size: 16px;"
                x-text="message"
            ></button>

            <div
                class="box"
                x-on:mouseover="$el.style.backgroundColor = '#e3f2fd'"
                x-on:mouseout="$el.style.backgroundColor = '#f8f9fa'"
            >
                Hover over this box to change its background
            </div>

            <input
                type="text"
                x-on:focus="$el.style.borderColor = '#2196f3'"
                x-on:blur="$el.style.borderColor = '#ddd'"
                placeholder="Focus me to see border change"
            >
        </div>
    </div>

    <!-- 2. $el - Element Manipulation -->
    <div class="demo">
        <h3>2. $el - Advanced Element Manipulation</h3>
        <div x-data="{
            counter: 0,
            increment() {
                this.counter++;
                // Add highlight effect
                $el.classList.add('highlight');
                setTimeout(() => {
                    $el.classList.remove('highlight');
                }, 500);
            },
            changeColor() {
                const colors = ['#ff6b6b', '#4ecdc4', '#45b7d1', '#f9ca24', '#6c5ce7'];
                const randomColor = colors[Math.floor(Math.random() * colors.length)];
                $el.style.backgroundColor = randomColor;
                $el.style.color = 'white';
            }
        }">
            <div
                class="box"
                x-on:click="increment()"
                style="cursor: pointer;"
            >
                <p>Click count: <span x-text="counter"></span></p>
                <small>Click this box to increment and see highlight effect</small>
            </div>

            <button x-on:click="changeColor()">Change My Color</button>
        </div>
    </div>

    <!-- 3. $refs - Element References -->
    <div class="demo">
        <h3>3. $refs - Element References</h3>
        <div x-data="{
            message: '',
            focusInput() {
                this.$refs.myInput.focus();
                this.$refs.myInput.select();
            },
            getInputValue() {
                this.message = this.$refs.myInput.value;
            },
            scrollToTop() {
                this.$refs.scrollContainer.scrollTop = 0;
            },
            scrollToBottom() {
                this.$refs.scrollContainer.scrollTop = this.$refs.scrollContainer.scrollHeight;
            },
            toggleClass() {
                this.$refs.targetBox.classList.toggle('highlight');
            },
            getElementInfo() {
                const element = this.$refs.infoElement;
                this.message = `Tag: ${element.tagName}, ID: ${element.id}, Classes: ${element.className}`;
            }
        }">
            <input
                type="text"
                x-ref="myInput"
                placeholder="This is the referenced input"
                value="Hello World!"
            >
            <button x-on:click="focusInput()">Focus & Select</button>
            <button x-on:click="getInputValue()">Get Value</button>

            <div
                x-ref="scrollContainer"
                class="scroll-container"
            >
                <p>This is scrollable content...</p>
                <p>Scroll down and use the buttons to navigate</p>
                <div style="height: 100px; background: #f0f0f0; margin: 10px 0;">More content 1</div>
                <div style="height: 100px; background: #e0e0e0; margin: 10px 0;">More content 2</div>
                <div style="height: 100px; background: #d0d0d0; margin: 10px 0;">More content 3</div>
                <div style="height: 100px; background: #c0c0c0; margin: 10px 0;">End of content</div>
            </div>
            <button x-on:click="scrollToTop()">Scroll to Top</button>
            <button x-on:click="scrollToBottom()">Scroll to Bottom</button>

            <div
                x-ref="targetBox"
                class="box"
            >
                <button x-on:click="toggleClass()">Toggle My Class</button>
            </div>

            <div
                x-ref="infoElement"
                id="info-div"
                class="box highlight"
            >
                Element with ID and multiple classes
            </div>
            <button x-on:click="getElementInfo()">Get Element Info</button>

            <p x-show="message" x-text="message" class="success"></p>
        </div>
    </div>

    <!-- 4. $store - Global Store -->
    <div class="demo">
        <h3>4. $store - Global Store</h3>
        <div x-data="{
            theme: 'light',
            user: { name: 'John', role: 'admin' },
            notifications: [],
            addNotification(message) {
                this.notifications.push({
                    id: Date.now(),
                    message,
                    timestamp: new Date().toLocaleTimeString()
                });
                // Keep only last 5 notifications
                if (this.notifications.length > 5) {
                    this.notifications.shift();
                }
            },
            clearNotifications() {
                this.notifications = [];
            }
        }"
             x-init="$store.theme = theme; $store.user = user; $store.notifications = notifications;">

            <!-- Theme switcher -->
            <div class="box">
                <h4>Theme: <span x-text="$store.theme"></span></h4>
                <button x-on:click="$store.theme = 'light'">Light Theme</button>
                <button x-on:click="$store.theme = 'dark'">Dark Theme</button>
                <p>Current theme stored in $store.theme: <span x-text="$store.theme"></span></p>
            </div>

            <!-- User info -->
            <div class="box">
                <h4>User Info (from $store):</h4>
                <p>Name: <span x-text="$store.user.name"></span></p>
                <p>Role: <span x-text="$store.user.role"></span></p>
                <button x-on:click="$store.user.name = 'Jane'">Change Name to Jane</button>
            </div>

            <!-- Notifications -->
            <div class="box">
                <h4>Notifications:</h4>
                <button x-on:click="addNotification('New notification!')">
                    Add Notification
                </button>
                <button x-on:click="clearNotifications()">
                    Clear All
                </button>

                <div style="margin-top: 10px;">
                    <template x-for="notification in $store.notifications">
                        <div class="highlight" style="margin-bottom: 5px; padding: 8px;">
                            <small x-text="notification.timestamp"></small>:
                            <span x-text="notification.message"></span>
                        </div>
                    </template>
                </div>

                <p x-show="$store.notifications.length === 0">No notifications</p>
            </div>
        </div>
    </div>

    <!-- 5. $event - Event Object -->
    <div class="demo">
        <h3>5. $event - Event Object</h3>
        <div x-data="{
            mousePosition: { x: 0, y: 0 },
            keyInfo: '',
            clickInfo: '',
            submitInfo: '',
            handleMouseMove(e) {
                this.mousePosition.x = e.offsetX;
                this.mousePosition.y = e.offsetY;
            },
            handleKeydown(e) {
                this.keyInfo = `Key: ${e.key}, Code: ${e.code}, Ctrl: ${e.ctrlKey}, Shift: ${e.shiftKey}`;
            },
            handleClick(e) {
                this.clickInfo = `Clicked at (${e.clientX}, ${e.clientY}) on element: ${e.target.tagName}`;
            },
            handleSubmit(e) {
                e.preventDefault();
                this.submitInfo = `Form submitted with ${new FormData(e.target).get('username')}`;
            }
        }">
            <!-- Mouse events -->
            <div
                class="box"
                style="height: 100px; cursor: crosshair; position: relative;"
                x-on:mousemove="handleMouseMove($event)"
                x-on:click="handleClick($event)"
            >
                <p>Move mouse here: (<span x-text="mousePosition.x"></span>, <span x-text="mousePosition.y"></span>)</p>
                <p x-show="clickInfo" x-text="clickInfo"></p>
            </div>

            <!-- Keyboard events -->
            <input
                type="text"
                x-on:keydown="handleKeydown($event)"
                placeholder="Type to see event info"
                style="width: 300px;"
            >
            <p x-show="keyInfo" x-text="keyInfo"></p>

            <!-- Form events -->
            <form x-on:submit="handleSubmit($event)">
                <input type="text" name="username" placeholder="Username" required>
                <button type="submit">Submit</button>
            </form>
            <p x-show="submitInfo" x-text="submitInfo"></p>

            <!-- Event prevention and propagation -->
            <div
                class="box"
                x-on:click="alert('Outer div clicked!')"
                style="position: relative; padding: 20px;"
            >
                <p>Outer div (clickable)</p>
                <button
                    x-on:click.stop="alert('Button clicked without propagating')"
                >
                    Stop Propagation
                </button>
                <button
                    x-on:click.prevent="alert('Default prevented')"
                >
                    Prevent Default
                </button>
                <button
                    x-on:click.self="alert('Only when clicking the div itself')"
                >
                    Self Only
                </button>
            </div>
        </div>
    </div>

    <!-- 6. $dispatch - Custom Events -->
    <div class="demo">
        <h3>6. $dispatch - Custom Events</h3>
        <div x-data="{
            customMessage: '',
            eventData: null,
            lastEvent: '',
            handleMessage(event) {
                this.customMessage = event.detail.message;
                this.eventData = event.detail;
                this.lastEvent = 'Received: ' + event.type;
            },
            handleNotification(event) {
                alert('Notification: ' + event.detail.message);
            }
        }"
             x-on:custom-message="handleMessage($event)"
             x-on:show-notification="handleNotification($event)">

            <!-- Event dispatcher -->
            <div class="box">
                <h4>Event Dispatchers:</h4>
                <button x-on:click="$dispatch('custom-message', { message: 'Hello from dispatcher!', timestamp: Date.now() })">
                    Dispatch Custom Message
                </button>
                <button x-on:click="$dispatch('show-notification', { message: 'This is a notification!' })">
                    Show Notification
                </button>
                <button x-on:click="$dispatch('user-action', { action: 'click', element: 'button' })">
                    Dispatch User Action
                </button>
            </div>

            <!-- Event listener -->
            <div class="box">
                <h4>Event Receiver:</h4>
                <p>Custom Message: <span x-text="customMessage"></span></p>
                <p>Last Event: <span x-text="lastEvent"></span></p>
                <div x-show="eventData">
                    <h5>Event Details:</h5>
                    <template x-for="(value, key) in eventData">
                        <p><small><strong x-text="key"></strong>: <span x-text="value"></span></small></p>
                    </template>
                </div>
            </div>

            <!-- Cross-component communication -->
            <div class="box">
                <h4>Cross-Component Communication:</h4>
                <button x-on:click="$dispatch('theme-change', { theme: 'dark' })">
                    Change Theme to Dark
                </button>
                <button x-on:click="$dispatch('theme-change', { theme: 'light' })">
                    Change Theme to Light
                </button>
            </div>

            <div
                x-data="{ currentTheme: 'light' }"
                x-on:theme-change="currentTheme = $event.detail.theme"
                class="box"
            >
                <p>Current Theme: <span x-text="currentTheme"></span></p>
                <div :style="'background: ' + (currentTheme === 'dark' ? '#333' : '#fff') + '; color: ' + (currentTheme === 'dark' ? '#fff' : '#333') + '; padding: 10px;'">
                    Themed content based on dispatched events
                </div>
            </div>
        </div>
    </div>

    <!-- 7. $watch - Reactive Watching -->
    <div class="demo">
        <h3>7. $watch - Reactive Watching</h3>
        <div x-data="{
            counter: 0,
            message: '',
            searchQuery: '',
            results: [],
            user: { name: '', email: '' },
            watchCount: 0,
            init() {
                // Watch for counter changes
                this.$watch('counter', (value) => {
                    this.message = `Counter changed to: ${value}`;
                    this.watchCount++;
                });

                // Watch for search query
                this.$watch('searchQuery', (value) => {
                    if (value.length > 2) {
                        // Simulate search
                        setTimeout(() => {
                            this.results = [`Result for "${value}"`, `Another result for "${value}"`];
                        }, 500);
                    } else {
                        this.results = [];
                    }
                });

                // Deep watch user object
                this.$watch('user', (value) => {
                    console.log('User changed:', value);
                }, { deep: true });
            }
        }">
            <div class="box">
                <h4>Counter Watcher:</h4>
                <button x-on:click="counter++">Increment</button>
                <button x-on:click="counter--">Decrement</button>
                <button x-on:click="counter = 0">Reset</button>
                <p>Counter: <span x-text="counter"></span></p>
                <p>Watch triggered: <span x-text="watchCount"></span> times</p>
                <p x-show="message" x-text="message"></p>
            </div>

            <div class="box">
                <h4>Search Watcher:</h4>
                <input
                    type="text"
                    x-model="searchQuery"
                    placeholder="Search for something (type 3+ characters)"
                    style="width: 300px;"
                >
                <div x-show="results.length > 0">
                    <h5>Results:</h5>
                    <template x-for="result in results">
                        <div class="highlight" style="padding: 5px; margin: 2px 0;">
                            <span x-text="result"></span>
                        </div>
                    </template>
                </div>
            </div>

            <div class="box">
                <h4>Deep Watcher:</h4>
                <input
                    type="text"
                    x-model="user.name"
                    placeholder="Name"
                >
                <input
                    type="email"
                    x-model="user.email"
                    placeholder="Email"
                >
                <p>User object: <span x-text="JSON.stringify(user)"></span></p>
                <small>Check console to see deep watcher in action</small>
            </div>
        </div>
    </div>

    <!-- 8. $nextTick - DOM Update Timing -->
    <div class="demo">
        <h3>8. $nextTick - DOM Update Timing</h3>
        <div x-data="{
            items: ['Item 1', 'Item 2'],
            newItem: '',
            message: '',
            addItem() {
                this.items.push(this.newItem);
                this.newItem = '';

                // Wait for DOM to update
                this.$nextTick(() => {
                    // Scroll to the new item
                    const allItems = document.querySelectorAll('.list-item');
                    const lastItem = allItems[allItems.length - 1];
                    if (lastItem) {
                        lastItem.scrollIntoView({ behavior: 'smooth' });
                    }
                    this.message = 'New item added and scrolled into view!';
                    setTimeout(() => {
                        this.message = '';
                    }, 2000);
                });
            },
            highlightLastItem() {
                this.$nextTick(() => {
                    const items = document.querySelectorAll('.list-item');
                    if (items.length > 0) {
                        items[items.length - 1].classList.add('highlight');
                        setTimeout(() => {
                            items[items.length - 1].classList.remove('highlight');
                        }, 1000);
                    }
                });
            }
        }">
            <div class="box">
                <h4>Add Items with Auto-Scroll:</h4>
                <input
                    type="text"
                    x-model="newItem"
                    placeholder="Enter item name"
                    x-on:keyup.enter="addItem()"
                >
                <button x-on:click="addItem()">Add Item</button>
                <button x-on:click="highlightLastItem()">Highlight Last Item</button>

                <div class="scroll-container" style="max-height: 200px; overflow-y: auto; margin-top: 10px;">
                    <template x-for="(item, index) in items">
                        <div
                            class="list-item"
                            style="padding: 8px; margin: 2px 0; background: #f8f9fa; border-left: 3px solid #007bff;"
                        >
                            <span x-text="index + 1"></span>. <span x-text="item"></span>
                        </div>
                    </template>
                </div>

                <p x-show="message" x-text="message" class="success"></p>
            </div>
        </div>
    </div>

</body>
</html>

💻 Alpine.js Plugins and Utilities html

🔴 complex

Alpine.js plugins, extensions, and utility functions

<!-- Alpine.js Plugins and Utilities -->

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Alpine.js Plugins and Utilities</title>
    <!-- Alpine.js Core -->
    <script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>
    <!-- Alpine.js Plugins -->
    <script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/[email protected]/dist/cdn.min.js"></script>
    <script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/[email protected]/dist/cdn.min.js"></script>
    <script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/[email protected]/dist/cdn.min.js"></script>
    <script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/[email protected]/dist/cdn.min.js"></script>
    <script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/[email protected]/dist/cdn.min.js"></script>

    <style>
        body { font-family: Arial, sans-serif; margin: 20px; line-height: 1.6; }
        .demo { margin: 20px 0; padding: 20px; border: 1px solid #ddd; border-radius: 8px; background: #fafafa; }
        .demo h3 { margin-top: 0; color: #333; border-bottom: 2px solid #007bff; padding-bottom: 5px; }
        .box { padding: 15px; margin: 10px 0; background: white; border-radius: 5px; border: 1px solid #e0e0e0; }
        .highlight { background: #fff3cd; border-color: #ffeaa7; }
        .success { background: #d4edda; border-color: #c3e6cb; color: #155724; }
        .error { background: #f8d7da; border-color: #f5c6cb; color: #721c24; }
        button {
            margin: 5px; padding: 10px 15px; cursor: pointer;
            border: 1px solid #007bff; background: #007bff; color: white;
            border-radius: 4px; transition: all 0.2s;
        }
        button:hover { background: #0056b3; }
        button.secondary { background: #6c757d; border-color: #6c757d; }
        button.danger { background: #dc3545; border-color: #dc3545; }
        input, select, textarea { margin: 5px; padding: 8px; border: 1px solid #ddd; border-radius: 4px; }
        .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; }
        .tab-content { padding: 15px; border: 1px solid #ddd; border-top: none; }
        .scroll-spy-nav {
            position: sticky; top: 20px; background: white; padding: 10px;
            border: 1px solid #ddd; border-radius: 5px; max-width: 200px;
        }
        .modal-overlay {
            position: fixed; top: 0; left: 0; width: 100%; height: 100%;
            background: rgba(0,0,0,0.5); display: flex; align-items: center;
            justify-content: center; z-index: 1000;
        }
        .modal-content {
            background: white; padding: 30px; border-radius: 8px;
            max-width: 500px; width: 90%; max-height: 80vh; overflow-y: auto;
        }
    </style>
</head>
<body>
    <h1>Alpine.js Plugins and Utilities</h1>

    <!-- 1. Mask Plugin -->
    <div class="demo">
        <h3>1. Mask Plugin</h3>
        <div x-data="{
            phone: '',
            date: '',
            creditCard: '',
            currency: '',
            time: '',
            postalCode: ''
        }">
            <div class="grid">
                <div class="box">
                    <h4>Phone Number</h4>
                    <input
                        type="text"
                        x-model="phone"
                        x-mask="(999) 999-9999"
                        placeholder="(123) 456-7890"
                    >
                    <p>Formatted: <strong x-text="phone"></strong></p>
                </div>

                <div class="box">
                    <h4>Date</h4>
                    <input
                        type="text"
                        x-model="date"
                        x-mask="99/99/9999"
                        placeholder="12/25/2023"
                    >
                    <p>Formatted: <strong x-text="date"></strong></p>
                </div>

                <div class="box">
                    <h4>Credit Card</h4>
                    <input
                        type="text"
                        x-model="creditCard"
                        x-mask="9999 9999 9999 9999"
                        placeholder="1234 5678 9012 3456"
                    >
                    <p>Formatted: <strong x-text="creditCard"></strong></p>
                </div>

                <div class="box">
                    <h4>Currency</h4>
                    <input
                        type="text"
                        x-model="currency"
                        x-mask="$9,999.99"
                        placeholder="$1,234.56"
                    >
                    <p>Formatted: <strong x-text="currency"></strong></p>
                </div>

                <div class="box">
                    <h4>Time</h4>
                    <input
                        type="text"
                        x-model="time"
                        x-mask="99:99"
                        placeholder="14:30"
                    >
                    <p>Formatted: <strong x-text="time"></strong></p>
                </div>

                <div class="box">
                    <h4>Postal Code</h4>
                    <input
                        type="text"
                        x-model="postalCode"
                        x-mask="99999"
                        placeholder="12345"
                    >
                    <p>Formatted: <strong x-text="postalCode"></strong></p>
                </div>
            </div>
        </div>
    </div>

    <!-- 2. Anchor Plugin (Smooth Scroll) -->
    <div class="demo">
        <h3>2. Anchor Plugin (Smooth Scroll)</h3>
        <div x-data="{ activeSection: '' }">
            <nav class="scroll-spy-nav">
                <h4>Navigation:</h4>
                <a href="#section1" x-anchor>Section 1</a><br>
                <a href="#section2" x-anchor>Section 2</a><br>
                <a href="#section3" x-anchor>Section 3</a><br>
                <a href="#section4" x-anchor>Section 4</a><br>
                <p>Active: <strong x-text="activeSection"></strong></p>
            </nav>

            <div style="margin-left: 220px;">
                <section id="section1" class="box" style="min-height: 200px; margin-bottom: 20px;">
                    <h3>Section 1</h3>
                    <p>This is the first section. Click the navigation links to see smooth scrolling in action.</p>
                </section>

                <section id="section2" class="box" style="min-height: 200px; margin-bottom: 20px;">
                    <h3>Section 2</h3>
                    <p>This is the second section. The anchor plugin provides smooth scrolling between sections.</p>
                </section>

                <section id="section3" class="box" style="min-height: 200px; margin-bottom: 20px;">
                    <h3>Section 3</h3>
                    <p>This is the third section. Scroll spy functionality highlights the active section.</p>
                </section>

                <section id="section4" class="box" style="min-height: 200px; margin-bottom: 20px;">
                    <h3>Section 4</h3>
                    <p>This is the fourth section. The scroll behavior is smooth and automatic.</p>
                </section>
            </div>
        </div>
    </div>

    <!-- 3. Focus Plugin -->
    <div class="demo">
        <h3>3. Focus Plugin (Trap Focus)</h3>
        <div x-data="{
            showModal: false,
            formData: {
                name: '',
                email: '',
                message: ''
            }
        }">
            <button x-on:click="showModal = true">Open Modal with Focus Trap</button>

            <div x-show="showModal" x-trap="showModal" class="modal-overlay">
                <div class="modal-content">
                    <h3>Contact Form</h3>
                    <p>Focus is trapped within this modal. Press Tab to navigate.</p>

                    <form>
                        <div style="margin-bottom: 15px;">
                            <label>Name:</label><br>
                            <input
                                type="text"
                                x-model="formData.name"
                                placeholder="Your name"
                                style="width: 100%;"
                            >
                        </div>

                        <div style="margin-bottom: 15px;">
                            <label>Email:</label><br>
                            <input
                                type="email"
                                x-model="formData.email"
                                placeholder="[email protected]"
                                style="width: 100%;"
                            >
                        </div>

                        <div style="margin-bottom: 15px;">
                            <label>Message:</label><br>
                            <textarea
                                x-model="formData.message"
                                placeholder="Your message"
                                rows="4"
                                style="width: 100%;"
                            ></textarea>
                        </div>

                        <div style="margin-bottom: 15px;">
                            <button
                                type="button"
                                x-on:click="alert('Form submitted: ' + JSON.stringify(formData))"
                            >
                                Submit
                            </button>
                            <button
                                type="button"
                                class="secondary"
                                x-on:click="showModal = false"
                            >
                                Cancel
                            </button>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>

    <!-- 4. Intersect Plugin -->
    <div class="demo">
        <h3>4. Intersect Plugin (Scroll Animations)</h3>
        <div>
            <div class="box"
                 x-intersect:enter.opacity-100.transition.duration-500ms
                 x-intersect:leave.opacity-0.transition.duration-500ms>
                <h3>Fade In/Out</h3>
                <p>This box fades in when it enters the viewport and fades out when it leaves.</p>
            </div>

            <div class="box"
                 x-intersect:enter.translate-y-0.transition.duration-700ms
                 x-intersect:leave.-translate-y-8.transition.duration-700ms>
                <h3>Slide Animation</h3>
                <p>This box slides up when it enters the viewport and slides down when it leaves.</p>
            </div>

            <div class="box"
                 x-data="{ hasIntersected: false }"
                 x-intersect.once="hasIntersected = true">
                <h3>Once Only</h3>
                <p>This animation only triggers once when first visible:
                   <span x-text="hasIntersected ? '✓ Triggered' : '○ Not triggered'"></span></p>
            </div>

            <div class="box"
                 x-data="{
                     visibleCount: 0,
                     countIntersect(event) {
                         if (event.detail.isIntersecting) {
                             this.visibleCount++;
                         }
                     }
                 }"
                 x-intersect.margin.-100px="countIntersect($event)">
                <h3>With Margin</h3>
                <p>Triggers when 100px from viewport edge. Visible count:
                   <strong x-text="visibleCount"></strong></p>
            </div>

            <div class="box"
                 x-data="{ threshold: 0.5 }"
                 x-intersect.threshold.50="console.log('50% visible!')">
                <h3>Custom Threshold</h3>
                <p>Triggers when 50% of this element is visible (check console).</p>
                <label>Threshold:
                    <input type="number" x-model.number="threshold" min="0" max="1" step="0.1">
                    <span x-text="threshold"></span>
                </label>
            </div>

            <!-- Add some spacing to allow scrolling -->
            <div style="height: 300px;"></div>
        </div>
    </div>

    <!-- 5. Persist Plugin -->
    <div class="demo">
        <h3>5. Persist Plugin (Local Storage)</h3>
        <div x-data="{
            preferences: {
                theme: $persist('light').as('theme'),
                language: $persist('en').as('language'),
                notifications: $persist(true).as('notifications')
            },
            user: {
                name: $persist('').as('userName'),
                visits: $persist(0).as('visitCount')
            },
            cart: $persist([]).as('shoppingCart'),
            todoList: $persist(['Learn Alpine.js', 'Build cool projects']).as('todos'),
            init() {
                this.user.visits++;
                console.log('Welcome back! Visit count:', this.user.visits);
            },
            addItem() {
                this.cart.push({
                    id: Date.now(),
                    name: 'Item ' + (this.cart.length + 1),
                    price: Math.floor(Math.random() * 100) + 1
                });
            },
            clearCart() {
                this.cart = [];
            },
            addTodo(event) {
                if (event.target.value.trim()) {
                    this.todoList.push(event.target.value.trim());
                    event.target.value = '';
                }
            },
            removeTodo(index) {
                this.todoList.splice(index, 1);
            }
        }">
            <div class="grid">
                <div class="box">
                    <h4>User Preferences</h4>
                    <div style="margin-bottom: 10px;">
                        <label>Theme:</label>
                        <select x-model="preferences.theme">
                            <option value="light">Light</option>
                            <option value="dark">Dark</option>
                            <option value="auto">Auto</option>
                        </select>
                    </div>
                    <div style="margin-bottom: 10px;">
                        <label>Language:</label>
                        <select x-model="preferences.language">
                            <option value="en">English</option>
                            <option value="es">Spanish</option>
                            <option value="fr">French</option>
                        </select>
                    </div>
                    <div style="margin-bottom: 10px;">
                        <label>
                            <input type="checkbox" x-model="preferences.notifications">
                            Enable notifications
                        </label>
                    </div>
                    <p><small>All preferences are saved to localStorage!</small></p>
                </div>

                <div class="box">
                    <h4>User Info</h4>
                    <div style="margin-bottom: 10px;">
                        <label>Name:</label>
                        <input
                            type="text"
                            x-model="user.name"
                            placeholder="Your name"
                        >
                    </div>
                    <p>Visit count: <strong x-text="user.visits"></strong></p>
                    <button class="danger" x-on:click="user.visits = 0">Reset Visits</button>
                </div>

                <div class="box">
                    <h4>Shopping Cart</h4>
                    <button x-on:click="addItem()">Add Random Item</button>
                    <button class="secondary" x-on:click="clearCart()">Clear Cart</button>
                    <div style="margin-top: 10px;">
                        <template x-for="item in cart">
                            <div style="padding: 5px; background: #f8f9fa; margin: 2px 0; border-radius: 3px;">
                                <span x-text="item.name"></span> - $<span x-text="item.price"></span>
                            </div>
                        </template>
                        <p x-show="cart.length === 0">Cart is empty</p>
                        <p x-show="cart.length > 0">
                            Total: $<strong x-text="cart.reduce((sum, item) => sum + item.price, 0)"></strong>
                        </p>
                    </div>
                </div>

                <div class="box">
                    <h4>Todo List</h4>
                    <div style="margin-bottom: 10px;">
                        <input
                            type="text"
                            x-on:keyup.enter="addTodo($event)"
                            placeholder="Add todo and press Enter"
                        >
                    </div>
                    <ul style="list-style: none; padding: 0;">
                        <template x-for="(todo, index) in todoList">
                            <li style="margin: 5px 0; padding: 8px; background: #e9ecef; border-radius: 3px;">
                                <span x-text="todo"></span>
                                <button
                                    class="danger"
                                    style="float: right; padding: 2px 8px;"
                                    x-on:click="removeTodo(index)"
                                >
                                    ×
                                </button>
                            </li>
                        </template>
                    </ul>
                    <p x-show="todoList.length === 0">No todos yet</p>
                </div>
            </div>
        </div>
    </div>

    <!-- 6. Custom Magic Properties -->
    <div class="demo">
        <h3>6. Custom Magic Properties</h3>
        <div>
            <p>Custom magic properties allow you to extend Alpine.js functionality:</p>

            <div x-data="{
                message: 'Hello Custom Magic!',
                init() {
                    // Add custom magic property
                    Alpine.magic('now', () => new Date().toLocaleTimeString());

                    // Custom magic with parameter
                    Alpine.magic('format', () => (value, type) => {
                        switch(type) {
                            case 'currency': return '$' + parseFloat(value).toFixed(2);
                            case 'percent': return value + '%';
                            case 'uppercase': return value.toUpperCase();
                            default: return value;
                        }
                    });

                    // Custom magic for API calls
                    Alpine.magic('api', () => ({
                        get: async (endpoint) => {
                            const response = await fetch(endpoint);
                            return response.json();
                        },
                        post: async (endpoint, data) => {
                            const response = await fetch(endpoint, {
                                method: 'POST',
                                headers: { 'Content-Type': 'application/json' },
                                body: JSON.stringify(data)
                            });
                            return response.json();
                        }
                    }));
                },
                price: 123.45,
                percentage: 75,
                text: 'hello world',
                data: null,
                async loadData() {
                    try {
                        this.data = await this.$api.get('https://jsonplaceholder.typicode.com/posts/1');
                    } catch (error) {
                        console.error('API call failed:', error);
                    }
                }
            }">
                <div class="box">
                    <h4>Custom Magic Properties in Action</h4>
                    <p>Current time: <strong x-text="$now"></strong></p>
                    <p>Price formatted: <strong x-text="$format(price, 'currency')"></strong></p>
                    <p>Percentage: <strong x-text="$format(percentage, 'percent')"></strong></p>
                    <p>Uppercase: <strong x-text="$format(text, 'uppercase')"></strong></p>

                    <button x-on:click="loadData()">Load API Data</button>
                    <div x-show="data" class="success" style="margin-top: 10px; padding: 10px;">
                        <h5>API Response:</h5>
                        <p><strong>Title:</strong> <span x-text="data.title"></span></p>
                        <p><strong>Body:</strong> <span x-text="data.body"></span></p>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <!-- 7. Alpine Utilities -->
    <div class="demo">
        <h3>7. Alpine Utilities</h3>
        <div x-data="{
            componentData: { count: 0, message: 'Hello' },
            methods: {
                increment() { this.componentData.count++; },
                reset() {
                    this.componentData.count = 0;
                    this.componentData.message = 'Reset';
                }
            },
            init() {
                // Alpine utilities
                console.log('Alpine version:', Alpine.version);
                console.log('Current component data:', this.$data);

                // Store some data
                Alpine.store('app', {
                    version: '1.0.0',
                    user: { name: 'Demo User' }
                });
            },
            inspectData() {
                alert('Component data: ' + JSON.stringify(this.$data, null, 2));
            },
            inspectStore() {
                alert('App store: ' + JSON.stringify(Alpine.store('app'), null, 2));
            }
        }">
            <div class="box">
                <h4>Component Utilities</h4>
                <p>Count: <strong x-text="componentData.count"></strong></p>
                <p>Message: <strong x-text="componentData.message"></strong></p>
                <button x-on:click="methods.increment()">Increment</button>
                <button x-on:click="methods.reset()">Reset</button>
                <button class="secondary" x-on:click="inspectData()">Inspect Data</button>
                <button class="secondary" x-on:click="inspectStore()">Inspect Store</button>
            </div>

            <div class="box">
                <h4>Alpine Store</h4>
                <p>App Version: <strong x-text="$store.app.version"></strong></p>
                <p>User: <strong x-text="$store.app.user.name"></strong></p>
                <button x-on:click="$store.app.user.name = 'Updated User'">Update User</button>
            </div>
        </div>
    </div>

</body>
</html>