<script src="https://cdn.tailwindcss.com"></script> <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script> <script type="module" src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.esm.js"></script> <script nomodule src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.js"></script> <body class="bg-gray-50 min-h-screen flex items-center justify-center p-4"> <div class="w-full max-w-md"> <div class="bg-white rounded-lg shadow-sm border border-gray-200 p-8"> <div class="text-center mb-8"> <h2 class="text-2xl font-semibold text-gray-900">Create Account</h2> <p class="text-gray-600 mt-2 text-sm">Sign up to get started</p> </div> <form x-data="registrationForm()" @submit.prevent="handleSubmit" class="space-y-5"> <!-- Full Name --> <div> <label class="block text-sm font-medium text-gray-700 mb-1">Full Name</label> <div class="relative"> <span class="absolute inset-y-0 left-0 flex items-center pl-3 text-gray-400"> <ion-icon name="person-outline"></ion-icon> </span> <input type="text" x-model="formData.name" @blur="validateField('name')" class="w-full pl-10 pr-3 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition" :class="errors.name ? 'border-red-500' : 'border-gray-300'" placeholder="John Doe"> </div> <p x-show="errors.name" x-text="errors.name" class="text-red-500 text-xs mt-1"></p> </div> <!-- Email --> <div> <label class="block text-sm font-medium text-gray-700 mb-1">Email Address</label> <div class="relative"> <span class="absolute inset-y-0 left-0 flex items-center pl-3 text-gray-400"> <ion-icon name="mail-outline"></ion-icon> </span> <input type="email" x-model="formData.email" @blur="validateField('email')" class="w-full pl-10 pr-3 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition" :class="errors.email ? 'border-red-500' : 'border-gray-300'" placeholder="john@example.com"> </div> <p x-show="errors.email" x-text="errors.email" class="text-red-500 text-xs mt-1"></p> </div> <!-- Password --> <div> <label class="block text-sm font-medium text-gray-700 mb-1">Password</label> <div class="relative"> <span class="absolute inset-y-0 left-0 flex items-center pl-3 text-gray-400"> <ion-icon name="lock-closed-outline"></ion-icon> </span> <input :type="showPassword ? 'text' : 'password'" x-model="formData.password" @blur="validateField('password')" class="w-full pl-10 pr-10 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition" :class="errors.password ? 'border-red-500' : 'border-gray-300'" placeholder="••••••••"> <button type="button" @click="showPassword = !showPassword" class="absolute inset-y-0 right-0 flex items-center pr-3 text-gray-400 hover:text-gray-600"> <ion-icon :name="showPassword ? 'eye-off-outline' : 'eye-outline'"></ion-icon> </button> </div> <p x-show="errors.password" x-text="errors.password" class="text-red-500 text-xs mt-1"></p> </div> <!-- Confirm Password --> <div> <label class="block text-sm font-medium text-gray-700 mb-1">Confirm Password</label> <div class="relative"> <span class="absolute inset-y-0 left-0 flex items-center pl-3 text-gray-400"> <ion-icon name="lock-closed-outline"></ion-icon> </span> <input :type="showConfirmPassword ? 'text' : 'password'" x-model="formData.confirmPassword" @blur="validateField('confirmPassword')" class="w-full pl-10 pr-10 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition" :class="errors.confirmPassword ? 'border-red-500' : 'border-gray-300'" placeholder="••••••••"> <button type="button" @click="showConfirmPassword = !showConfirmPassword" class="absolute inset-y-0 right-0 flex items-center pr-3 text-gray-400 hover:text-gray-600"> <ion-icon :name="showConfirmPassword ? 'eye-off-outline' : 'eye-outline'"></ion-icon> </button> </div> <p x-show="errors.confirmPassword" x-text="errors.confirmPassword" class="text-red-500 text-xs mt-1"></p> </div> <!-- Terms Checkbox --> <div> <label class="flex items-start"> <input type="checkbox" x-model="formData.terms" @change="validateField('terms')" class="mt-1 rounded border-gray-300 text-blue-600 focus:ring-2 focus:ring-blue-500"> <span class="ml-2 text-sm text-gray-600"> I agree to the <a href="#" class="text-blue-600 hover:underline">Terms of Service</a> and <a href="#" class="text-blue-600 hover:underline">Privacy Policy</a> </span> </label> <p x-show="errors.terms" x-text="errors.terms" class="text-red-500 text-xs mt-1"></p> </div> <!-- Submit Button --> <button type="submit" :disabled="isSubmitting" class="w-full bg-blue-600 text-white py-2.5 rounded-lg font-medium hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition disabled:opacity-50 disabled:cursor-not-allowed"> <span x-show="!isSubmitting">Create Account</span> <span x-show="isSubmitting">Creating Account...</span> </button> <!-- Success Message --> <div x-show="successMessage" class="bg-green-50 border border-green-200 text-green-700 px-4 py-3 rounded-lg text-sm"> <span x-text="successMessage"></span> </div> <!-- Login Link --> <p class="text-center text-sm text-gray-600 mt-4"> Already have an account? <a href="#" class="text-blue-600 hover:underline font-medium">Sign in</a> </p> </form> </div> </div>
function registrationForm() { return { formData: { name: '', email: '', password: '', confirmPassword: '', terms: false }, errors: {}, showPassword: false, showConfirmPassword: false, isSubmitting: false, successMessage: '', validateField(field) { this.errors[field] = ''; switch(field) { case 'name': if (!this.formData.name.trim()) { this.errors.name = 'Name is required'; } else if (this.formData.name.trim().length < 2) { this.errors.name = 'Name must be at least 2 characters'; } break; case 'email': const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!this.formData.email.trim()) { this.errors.email = 'Email is required'; } else if (!emailRegex.test(this.formData.email)) { this.errors.email = 'Please enter a valid email'; } break; case 'password': if (!this.formData.password) { this.errors.password = 'Password is required'; } else if (this.formData.password.length < 8) { this.errors.password = 'Password must be at least 8 characters'; } if (this.formData.confirmPassword) { this.validateField('confirmPassword'); } break; case 'confirmPassword': if (!this.formData.confirmPassword) { this.errors.confirmPassword = 'Please confirm your password'; } else if (this.formData.password !== this.formData.confirmPassword) { this.errors.confirmPassword = 'Passwords do not match'; } break; case 'terms': if (!this.formData.terms) { this.errors.terms = 'You must accept the terms'; } break; } }, validateAll() { this.validateField('name'); this.validateField('email'); this.validateField('password'); this.validateField('confirmPassword'); this.validateField('terms'); return Object.keys(this.errors).every(key => !this.errors[key]); }, handleSubmit() { this.successMessage = ''; if (this.validateAll()) { this.isSubmitting = true; // Simulate API call setTimeout(() => { this.isSubmitting = false; this.successMessage = 'Account created successfully!'; // Reset form after 2 seconds setTimeout(() => { this.formData = { name: '', email: '', password: '', confirmPassword: '', terms: false }; this.successMessage = ''; }, 2000); }, 1500); } } } }
Login to leave a comment
No comments yet. Be the first!
No comments yet. Be the first!