Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Profile Builder - WYSIWYG Link in Bio</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://unpkg.com/feather-icons"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'); | |
| body { | |
| font-family: 'Inter', sans-serif; | |
| } | |
| .glass-card { | |
| background: rgba(255, 255, 255, 0.08); | |
| backdrop-filter: blur(10px); | |
| -webkit-backdrop-filter: blur(10px); | |
| border-radius: 12px; | |
| border: 1px solid rgba(255, 255, 255, 0.18); | |
| } | |
| .gradient-text { | |
| background: linear-gradient(90deg, #3b82f6, #8b5cf6); | |
| -webkit-background-clip: text; | |
| background-clip: text; | |
| color: transparent; | |
| } | |
| .social-icon-placeholder { | |
| width: 12px; | |
| height: 12px; | |
| border-radius: 50%; | |
| background: #3b82f6; | |
| margin: 0 2px; | |
| display: inline-block; | |
| } | |
| .editable { | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| min-height: 1.2em; | |
| } | |
| .editable:focus { | |
| outline: none; | |
| background: transparent; | |
| border-radius: 4px; | |
| } | |
| .editable:hover { | |
| background: transparent; | |
| border-radius: 4px; | |
| } | |
| .editable:empty:before { | |
| content: attr(data-placeholder); | |
| color: rgba(255, 255, 255, 0.6); | |
| } | |
| .editable:focus { | |
| background: transparent ; | |
| border: none ; | |
| outline: none ; | |
| } | |
| .editable:hover { | |
| background: transparent ; | |
| } | |
| #editableName { | |
| background: transparent ; | |
| border: none ; | |
| outline: none ; | |
| z-index: 20; | |
| position: relative; | |
| } | |
| .social-dropdown { | |
| display: none; | |
| position: absolute; | |
| background: rgba(0, 0, 0, 0.95); | |
| border: 1px solid rgba(255, 255, 255, 0.2); | |
| border-radius: 8px; | |
| padding: 8px; | |
| z-index: 1000; | |
| min-width: 200px; | |
| max-height: 200px; | |
| overflow-y: auto; | |
| box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5); | |
| bottom: 100%; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| margin-bottom: 8px; | |
| } | |
| .social-option { | |
| display: flex; | |
| align-items: center; | |
| padding: 8px 12px; | |
| margin: 2px 0; | |
| border-radius: 6px; | |
| transition: all 0.2s ease; | |
| cursor: pointer; | |
| color: white; | |
| text-decoration: none; | |
| } | |
| .social-option:hover { | |
| background: rgba(59, 130, 246, 0.3); | |
| } | |
| .social-option i { | |
| margin-right: 10px; | |
| width: 20px; | |
| height: 20px; | |
| } | |
| .image-upload-container { | |
| position: relative; | |
| cursor: pointer; | |
| } | |
| .image-upload-overlay { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| background: rgba(0, 0, 0, 0.7); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| opacity: 0; | |
| transition: opacity 0.3s ease; | |
| border-radius: 50%; | |
| } | |
| .image-upload-container:hover .image-upload-overlay { | |
| opacity: 1; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-900 text-white min-h-screen"> | |
| <!-- Builder Controls --> | |
| <div class="fixed top-4 right-4 z-50"> | |
| <button id="saveProfile" class="bg-indigo-600 hover:bg-indigo-700 px-4 py-2 rounded-full text-sm font-medium transition"> | |
| Save Profile | |
| </button> | |
| </div> | |
| <!-- Hero Section --> | |
| <div id="vanta-bg" class="min-h-screen flex items-center justify-center px-4"> | |
| <div class="glass-card p-6 max-w-md w-full mx-auto text-center" style="aspect-ratio: 9/19; max-height: 95vh;"> | |
| <div class="flex flex-col justify-center h-full"> | |
| <!-- Profile Image Upload --> | |
| <div class="image-upload-container w-56 h-56 mx-auto mb-6 rounded-full overflow-hidden border-4 border-indigo-500 border-dashed"> | |
| <img id="profileImage" src="http://static.photos/technology/320x240/42" alt="Profile" class="w-full h-full object-cover"> | |
| <div class="image-upload-overlay"> | |
| <span class="text-white text-sm font-medium">Click to Upload</span> | |
| </div> | |
| <input type="file" id="imageUpload" accept="image/*" class="hidden"> | |
| </div> | |
| <!-- Editable Name --> | |
| <div class="relative mb-3"> | |
| <h1 id="editableName" class="text-3xl font-bold text-white text-center min-h-[1.5em] px-2 py-1" contenteditable="true" data-placeholder="Tap to Add your Name">Tap to Add your Name</h1> | |
| </div> | |
| <!-- Contact Button --> | |
| <div class="relative mb-4 w-3/5 mx-auto"> | |
| <a id="contactButton" href="tel:" class="px-4 py-2 border border-indigo-500 rounded-full hover:bg-indigo-900/30 transition text-xs w-full block editable" contenteditable="true" data-placeholder="Tap to Add Phone Number"></a> | |
| </div> | |
| <!-- Editable Title --> | |
| <p id="editableTitle" class="text-xl text-gray-300 mb-4 editable" contenteditable="true" data-placeholder="Tap to Add your Title"></p> | |
| <!-- Editable Description --> | |
| <p id="editableDescription" class="text-base text-gray-400 mb-8 px-2 editable" contenteditable="true" data-placeholder="Tap to Add a Description about Yourself"></p> | |
| <!-- Social Media Section --> | |
| <div class="mb-8"> | |
| <div class="flex justify-center flex-wrap gap-3 mb-4" id="socialIconsContainer"> | |
| <!-- Social media icons will be added here --> | |
| <div class="flex items-center justify-center space-x-1 mb-2 w-full"> | |
| <span class="social-icon-placeholder"></span> | |
| <span class="social-icon-placeholder"></span> | |
| <span class="social-icon-placeholder"></span> | |
| <span class="text-xs text-gray-400">Click + to add social media</span> | |
| </div> | |
| <!-- Plus button for adding social media --> | |
| <div class="relative"> | |
| <button id="addSocialButton" class="w-12 h-12 rounded-full bg-indigo-600 hover:bg-indigo-700 flex items-center justify-center transition"> | |
| <i data-feather="plus" class="w-5 h-5"></i> | |
| </button> | |
| <div id="socialDropdown" class="social-dropdown"> | |
| <a class="social-option" data-platform="instagram"> | |
| <i data-feather="instagram"></i> Instagram | |
| </a> | |
| <a class="social-option" data-platform="facebook"> | |
| <i data-feather="facebook"></i> Facebook | |
| </a> | |
| <a class="social-option" data-platform="twitter"> | |
| <i data-feather="twitter"></i> Twitter | |
| </a> | |
| <a class="social-option" data-platform="tiktok"> | |
| <i data-feather="video"></i> TikTok | |
| </a> | |
| <a class="social-option" data-platform="youtube"> | |
| <i data-feather="youtube"></i> YouTube | |
| </a> | |
| <a class="social-option" data-platform="linkedin"> | |
| <i data-feather="linkedin"></i> LinkedIn | |
| </a> | |
| <a class="social-option" data-platform="snapchat"> | |
| <i data-feather="camera"></i> Snapchat | |
| </a> | |
| <a class="social-option" data-platform="pinterest"> | |
| <i data-feather="image"></i> Pinterest | |
| </a> | |
| <a class="social-option" data-platform="reddit"> | |
| <i data-feather="message-circle"></i> Reddit | |
| </a> | |
| <a class="social-option" data-platform="github"> | |
| <i data-feather="github"></i> GitHub | |
| </a> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Action Buttons --> | |
| <div class="flex justify-center space-x-3 mt-auto"> | |
| <button class="px-4 py-2 bg-indigo-600 rounded-full hover:bg-indigo-700 transition text-sm">Projects</button> | |
| <button class="px-4 py-2 border border-indigo-500 rounded-full hover:bg-indigo-900/30 transition text-sm">About Me</button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Scripts --> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.globe.min.js"></script> | |
| <script> | |
| VANTA.GLOBE({ | |
| el: "#vanta-bg", | |
| mouseControls: true, | |
| touchControls: true, | |
| gyroControls: false, | |
| minHeight: 200.00, | |
| minWidth: 200.00, | |
| scale: 1.00, | |
| scaleMobile: 1.00, | |
| color: 0x3b82f6, | |
| backgroundColor: 0x111827, | |
| size: 0.8 | |
| }); | |
| feather.replace(); | |
| // Smooth scrolling for anchor links | |
| document.querySelectorAll('a[href^="#"]').forEach(anchor => { | |
| anchor.addEventListener('click', function (e) { | |
| e.preventDefault(); | |
| document.querySelector(this.getAttribute('href')).scrollIntoView({ | |
| behavior: 'smooth' | |
| }); | |
| }); | |
| }); | |
| // Image upload functionality | |
| document.getElementById('imageUpload').addEventListener('change', function(e) { | |
| const file = e.target.files[0]; | |
| if (file) { | |
| const reader = new FileReader(); | |
| reader.onload = function(e) { | |
| document.getElementById('profileImage').src = e.target.result; | |
| } | |
| reader.readAsDataURL(file); | |
| } | |
| }); | |
| // Make image container clickable | |
| document.querySelector('.image-upload-container').addEventListener('click', function() { | |
| document.getElementById('imageUpload').click(); | |
| }); | |
| // Social media dropdown functionality | |
| document.getElementById('addSocialButton').addEventListener('click', function(e) { | |
| e.stopPropagation(); | |
| const dropdown = document.getElementById('socialDropdown'); | |
| dropdown.style.display = dropdown.style.display === 'block' ? 'none' : 'block'; | |
| }); | |
| // Close dropdown when clicking outside | |
| document.addEventListener('click', function(e) { | |
| if (!e.target.closest('#socialDropdown') && !e.target.closest('#addSocialButton')) { | |
| document.getElementById('socialDropdown').style.display = 'none'; | |
| } | |
| }); | |
| // Initialize placeholder functionality for all editable elements except heading | |
| document.querySelectorAll('.editable').forEach(editable => { | |
| // Clear placeholder text when focusing | |
| editable.addEventListener('focus', function() { | |
| if (this.textContent === this.getAttribute('data-placeholder')) { | |
| this.textContent = ''; | |
| } | |
| this.style.background = 'transparent'; | |
| }); | |
| // Restore placeholder if empty when blurring | |
| editable.addEventListener('blur', function() { | |
| if (this.textContent.trim() === '') { | |
| this.textContent = this.getAttribute('data-placeholder'); | |
| } | |
| this.style.background = 'transparent'; | |
| }); | |
| }); | |
| // Special handling for heading element | |
| const editableName = document.getElementById('editableName'); | |
| editableName.addEventListener('focus', function() { | |
| if (this.textContent === this.getAttribute('data-placeholder')) { | |
| this.textContent = ''; | |
| } | |
| this.style.background = 'transparent'; | |
| this.style.border = 'none'; | |
| this.style.outline = 'none'; | |
| }); | |
| editableName.addEventListener('blur', function() { | |
| if (this.textContent.trim() === '') { | |
| this.textContent = this.getAttribute('data-placeholder'); | |
| } | |
| this.style.background = 'transparent'; | |
| this.style.border = 'none'; | |
| this.style.outline = 'none'; | |
| }); | |
| // Force clear any existing background/border styles | |
| editableName.style.background = 'transparent'; | |
| editableName.style.border = 'none'; | |
| editableName.style.outline = 'none'; | |
| function addSocialIcon(platform, handle) { | |
| const container = document.getElementById('socialIconsContainer'); | |
| // Create social icon element | |
| const iconWrapper = document.createElement('div'); | |
| iconWrapper.className = 'relative group'; | |
| const icon = document.createElement('a'); | |
| icon.href = getSocialUrl(platform, handle); | |
| icon.target = '_blank'; | |
| icon.className = 'w-12 h-12 rounded-full bg-gray-800 hover:bg-indigo-600 flex items-center justify-center transition transform hover:scale-110'; | |
| icon.innerHTML = `<i data-feather="${getFeatherIconName(platform)}" class="w-5 h-5"></i>`; | |
| // Add remove button | |
| const removeBtn = document.createElement('button'); | |
| removeBtn.className = 'absolute -top-1 -right-1 w-4 h-4 bg-red-500 rounded-full text-white text-xs flex items-center justify-center opacity-0 group-hover:opacity-100 transition'; | |
| removeBtn.innerHTML = '×'; | |
| removeBtn.onclick = function(e) { | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| container.removeChild(iconWrapper); | |
| }; | |
| iconWrapper.appendChild(icon); | |
| iconWrapper.appendChild(removeBtn); | |
| // Insert before the plus button | |
| const addButton = document.getElementById('addSocialButton').parentNode; | |
| container.insertBefore(iconWrapper, addButton); | |
| feather.replace(); | |
| } | |
| function getFeatherIconName(platform) { | |
| const iconMap = { | |
| instagram: 'instagram', | |
| facebook: 'facebook', | |
| twitter: 'twitter', | |
| tiktok: 'video', | |
| youtube: 'youtube', | |
| linkedin: 'linkedin', | |
| snapchat: 'camera', | |
| pinterest: 'image', | |
| reddit: 'message-circle', | |
| github: 'github' | |
| }; | |
| return iconMap[platform] || 'link'; | |
| } | |
| function getSocialUrl(platform, handle) { | |
| const urls = { | |
| instagram: `https://instagram.com/${handle}`, | |
| facebook: `https://facebook.com/${handle}`, | |
| twitter: `https://twitter.com/${handle}`, | |
| tiktok: `https://tiktok.com/@${handle}`, | |
| youtube: `https://youtube.com/@${handle}`, | |
| linkedin: `https://linkedin.com/in/${handle}`, | |
| snapchat: `https://snapchat.com/add/${handle}`, | |
| pinterest: `https://pinterest.com/${handle}`, | |
| reddit: `https://reddit.com/user/${handle}`, | |
| github: `https://github.com/${handle}` | |
| }; | |
| return urls[platform] || '#'; | |
| } | |
| // Update contact button href when editing | |
| document.getElementById('contactButton').addEventListener('input', function() { | |
| const phoneNumber = this.textContent.replace(/[^\d+]/g, ''); | |
| this.href = `tel:${phoneNumber}`; | |
| }); | |
| // Social media platform selection | |
| document.querySelectorAll('.social-option').forEach(option => { | |
| option.addEventListener('click', function(e) { | |
| e.preventDefault(); | |
| const platform = this.getAttribute('data-platform'); | |
| // Create modal for handle input | |
| const modal = document.createElement('div'); | |
| modal.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50'; | |
| modal.innerHTML = ` | |
| <div class="bg-gray-800 p-6 rounded-lg max-w-sm w-full mx-4"> | |
| <h3 class="text-lg font-medium mb-4">Add your ${platform} handle</h3> | |
| <input type="text" id="handleInput" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 mb-4 text-white" placeholder="Your handle"> | |
| <div class="flex justify-end space-x-2"> | |
| <button id="cancelHandle" class="px-4 py-2 text-gray-300 hover:text-white">Cancel</button> | |
| <button id="saveHandle" class="px-4 py-2 bg-indigo-600 rounded hover:bg-indigo-700">Add</button> | |
| </div> | |
| </div> | |
| `; | |
| document.body.appendChild(modal); | |
| const handleInput = document.getElementById('handleInput'); | |
| const cancelBtn = document.getElementById('cancelHandle'); | |
| const saveBtn = document.getElementById('saveHandle'); | |
| handleInput.focus(); | |
| cancelBtn.addEventListener('click', function() { | |
| document.body.removeChild(modal); | |
| }); | |
| saveBtn.addEventListener('click', function() { | |
| const handle = handleInput.value.trim(); | |
| if (handle) { | |
| addSocialIcon(platform, handle); | |
| // Remove placeholder dots | |
| const placeholderDots = document.querySelectorAll('.social-icon-placeholder'); | |
| if (placeholderDots.length > 0) { | |
| const placeholderContainer = placeholderDots[0].parentNode; | |
| if (placeholderContainer) { | |
| placeholderContainer.remove(); | |
| } | |
| } | |
| } | |
| document.body.removeChild(modal); | |
| document.getElementById('socialDropdown').style.display = 'none'; | |
| }); | |
| // Close modal when clicking outside | |
| modal.addEventListener('click', function(e) { | |
| if (e.target === modal) { | |
| document.body.removeChild(modal); | |
| } | |
| }); | |
| // Handle Enter key | |
| handleInput.addEventListener('keypress', function(e) { | |
| if (e.key === 'Enter') { | |
| saveBtn.click(); | |
| } | |
| }); | |
| }); | |
| }); | |
| function addSocialIcon(platform, handle) { | |
| const container = document.getElementById('socialIconsContainer'); | |
| // Create social icon element | |
| const iconWrapper = document.createElement('div'); | |
| iconWrapper.className = 'relative group'; | |
| const icon = document.createElement('a'); | |
| icon.href = getSocialUrl(platform, handle); | |
| icon.target = '_blank'; | |
| icon.className = 'w-12 h-12 rounded-full bg-gray-800 hover:bg-indigo-600 flex items-center justify-center transition transform hover:scale-110'; | |
| icon.innerHTML = `<i data-feather="${getFeatherIconName(platform)}" class="w-5 h-5"></i>`; | |
| // Add remove button | |
| const removeBtn = document.createElement('button'); | |
| removeBtn.className = 'absolute -top-1 -right-1 w-4 h-4 bg-red-500 rounded-full text-white text-xs flex items-center justify-center opacity-0 group-hover:opacity-100 transition'; | |
| removeBtn.innerHTML = '×'; | |
| removeBtn.onclick = function(e) { | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| container.removeChild(iconWrapper); | |
| }; | |
| iconWrapper.appendChild(icon); | |
| iconWrapper.appendChild(removeBtn); | |
| // Insert before the plus button | |
| const addButton = document.getElementById('addSocialButton').parentNode; | |
| container.insertBefore(iconWrapper, addButton); | |
| feather.replace(); | |
| } | |
| function getFeatherIconName(platform) { | |
| const iconMap = { | |
| instagram: 'instagram', | |
| facebook: 'facebook', | |
| twitter: 'twitter', | |
| tiktok: 'video', | |
| youtube: 'youtube', | |
| linkedin: 'linkedin', | |
| snapchat: 'camera', | |
| pinterest: 'image', | |
| reddit: 'message-circle', | |
| github: 'github' | |
| }; | |
| return iconMap[platform] || 'link'; | |
| } | |
| function getSocialUrl(platform, handle) { | |
| const urls = { | |
| instagram: `https://instagram.com/${handle}`, | |
| facebook: `https://facebook.com/${handle}`, | |
| twitter: `https://twitter.com/${handle}`, | |
| tiktok: `https://tiktok.com/@${handle}`, | |
| youtube: `https://youtube.com/@${handle}`, | |
| linkedin: `https://linkedin.com/in/${handle}`, | |
| snapchat: `https://snapchat.com/add/${handle}`, | |
| pinterest: `https://pinterest.com/${handle}`, | |
| reddit: `https://reddit.com/user/${handle}`, | |
| github: `https://github.com/${handle}` | |
| }; | |
| return urls[platform] || '#'; | |
| } | |
| // Save profile functionality | |
| document.getElementById('saveProfile').addEventListener('click', function() { | |
| const profileData = { | |
| name: document.getElementById('editableName').textContent, | |
| title: document.getElementById('editableTitle').textContent, | |
| description: document.getElementById('editableDescription').textContent, | |
| phone: document.getElementById('contactButton').textContent, | |
| image: document.getElementById('profileImage').src | |
| }; | |
| localStorage.setItem('profileData', JSON.stringify(profileData)); | |
| alert('Profile saved successfully!'); | |
| }); | |
| // Load saved profile | |
| const savedProfile = localStorage.getItem('profileData'); | |
| if (savedProfile) { | |
| const profileData = JSON.parse(savedProfile); | |
| // Set content only if it's not empty or placeholder | |
| if (profileData.name && profileData.name !== 'Tap to Add your Name') { | |
| document.getElementById('editableName').textContent = profileData.name; | |
| } | |
| if (profileData.title && profileData.title !== 'Tap to Add your Title') { | |
| document.getElementById('editableTitle').textContent = profileData.title; | |
| } | |
| if (profileData.description && profileData.description !== 'Tap to Add a Description about Yourself') { | |
| document.getElementById('editableDescription').textContent = profileData.description; | |
| } | |
| if (profileData.phone && profileData.phone !== 'Tap to Add Phone Number') { | |
| document.getElementById('contactButton').textContent = profileData.phone; | |
| document.getElementById('contactButton').href = `tel:${profileData.phone.replace(/[^\d+]/g, '')}`; | |
| } | |
| if (profileData.image && profileData.image !== 'http://static.photos/technology/320x240/42') { | |
| document.getElementById('profileImage').src = profileData.image; | |
| } | |
| } | |
| </script> | |
| </body> | |
| </html> | |