<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <link rel="canonical" href="https://profilecard.online/">

    <title>Personal Profile Card Generator</title>

    <script src="https://cdn.tailwindcss.com"></script>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>

    <style>

        .apple-green { background-color: #52C41A; }

        .color-preset { 

            width: 40px; 

            height: 40px; 

            border-radius: 8px; 

            cursor: pointer; 

            transition: transform 0.2s;

        }

        .color-preset:hover { transform: scale(1.1); }

        .color-preset.selected { 

            border: 3px solid #000; 

            transform: scale(1.15);

        }

        #preview-card { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; }

    </style>

</head>

<body class="bg-gray-50 min-h-screen py-8 px-4">

    <div class="max-w-6xl mx-auto">

        <header class="text-center mb-8">

            <h1 class="text-4xl font-bold text-gray-900 mb-2">Personal Profile Card Generator</h1>

            <p class="text-gray-600">Create your professional profile card in seconds</p>

        </header>


        <div class="grid md:grid-cols-2 gap-8">

            <!-- Form Section -->

            <div class="bg-white rounded-2xl shadow-lg p-6">

                <h2 class="text-2xl font-semibold mb-6 text-gray-800">Your Information</h2>

                

                <form id="profile-form" class="space-y-5">

                    <!-- Name -->

                    <div>

                        <label class="block text-sm font-medium text-gray-700 mb-2">Full Name *</label>

                        <input type="text" id="name" required class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent" placeholder="John Doe">

                    </div>


                    <!-- Avatar Upload -->

                    <div>

                        <label class="block text-sm font-medium text-gray-700 mb-2">Avatar Image *</label>

                        <input type="file" id="avatar" accept="image/*" required class="w-full px-4 py-2 border border-gray-300 rounded-lg file:mr-4 file:py-2 file:px-4 file:rounded-lg file:border-0 file:bg-green-50 file:text-green-700 hover:file:bg-green-100">

                    </div>


                    <!-- Introduction -->

                    <div>

                        <label class="block text-sm font-medium text-gray-700 mb-2">One-line Introduction *</label>

                        <input type="text" id="intro" required maxlength="100" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent" placeholder="Full-stack developer passionate about building amazing web experiences">

                    </div>


                    <!-- Color Picker -->

                    <div>

                        <label class="block text-sm font-medium text-gray-700 mb-2">Card Color Theme</label>

                        <div class="flex items-center gap-3 mb-3">

                            <div class="color-preset selected" data-color="#52C41A" style="background-color: #52C41A;" title="Apple Green"></div>

                            <div class="color-preset" data-color="#1890FF" style="background-color: #1890FF;" title="Blue"></div>

                            <div class="color-preset" data-color="#722ED1" style="background-color: #722ED1;" title="Purple"></div>

                            <div class="color-preset" data-color="#FA541C" style="background-color: #FA541C;" title="Orange"></div>

                            <div class="color-preset" data-color="#EB2F96" style="background-color: #EB2F96;" title="Pink"></div>

                        </div>

                        <div class="flex gap-2">

                            <input type="color" id="color-picker" value="#52C41A" class="h-10 w-16 border border-gray-300 rounded-lg cursor-pointer">

                            <input type="text" id="color-hex" value="#52C41A" pattern="^#[0-9A-Fa-f]{6}$" class="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent" placeholder="#52C41A">

                        </div>

                    </div>


                    <!-- Skills -->

                    <div>

                        <label class="block text-sm font-medium text-gray-700 mb-2">Skills (Max 5)</label>

                        <div id="skills-container" class="space-y-2 mb-2"></div>

                        <button type="button" id="add-skill-btn" class="w-full py-2 px-4 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition">+ Add Skill</button>

                    </div>


                    <!-- Contact Details -->

                    <div class="border-t pt-4">

                        <h3 class="font-semibold text-gray-800 mb-3">Contact Details</h3>

                        

                        <div class="space-y-3">

                            <input type="email" id="email" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent" placeholder="Email">

                            

                            <input type="tel" id="phone" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent" placeholder="Phone">

                            

                            <input type="text" id="wechat" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent" placeholder="WeChat ID">

                            

                            <input type="url" id="linkedin" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent" placeholder="LinkedIn URL (https://...)">

                            

                            <input type="url" id="twitter" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent" placeholder="Twitter URL (https://...)">

                        </div>

                    </div>


                    <!-- Card Size -->

                    <div>

                        <label class="block text-sm font-medium text-gray-700 mb-2">Card Size</label>

                        <select id="card-size" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent">

                            <option value="1080x1080">1080 × 1080 px (Square)</option>

                            <option value="1920x1080">1920 × 1080 px (Wide)</option>

                        </select>

                    </div>


                    <!-- Submit Button -->

                    <button type="submit" class="w-full py-3 px-6 bg-green-600 text-white rounded-lg font-semibold hover:bg-green-700 transition text-lg">

                        Generate Card

                    </button>

                </form>


                <!-- Download Section -->

                <div id="download-section" class="hidden mt-6 p-4 bg-green-50 rounded-lg border border-green-200">

                    <p class="text-green-800 font-medium mb-3">✓ Your card is ready!</p>

                    <button id="download-btn" class="w-full py-3 px-6 bg-green-600 text-white rounded-lg font-semibold hover:bg-green-700 transition">

                        Download Card

                    </button>

                </div>

            </div>


            <!-- Preview Section -->

            <div class="bg-white rounded-2xl shadow-lg p-6">

                <h2 class="text-2xl font-semibold mb-6 text-gray-800">Live Preview</h2>

                <div class="flex justify-center">

                    <div id="preview-card" class="bg-white rounded-2xl shadow-2xl overflow-hidden" style="width: 400px;">

                        <div id="card-header" class="apple-green p-8 text-white text-center relative">

                            <div class="w-32 h-32 mx-auto mb-4 rounded-full overflow-hidden bg-white shadow-lg">

                                <img id="preview-avatar" src="" alt="Avatar" class="w-full h-full object-cover hidden">

                                <div id="avatar-placeholder" class="w-full h-full flex items-center justify-center text-gray-400 text-5xl">👤</div>

                            </div>

                            <h3 id="preview-name" class="text-3xl font-bold mb-2">Your Name</h3>

                            <p id="preview-intro" class="text-lg opacity-95">Your introduction appears here</p>

                        </div>

                        

                        <div class="p-8">

                            <div id="preview-skills" class="mb-6 hidden">

                                <h4 class="text-sm font-semibold text-gray-600 mb-3 uppercase tracking-wide">Skills</h4>

                                <div id="skills-list" class="flex flex-wrap gap-2"></div>

                            </div>


                            <div id="preview-contact" class="space-y-3 text-sm hidden">

                                <h4 class="text-sm font-semibold text-gray-600 mb-3 uppercase tracking-wide">Contact</h4>

                                <div id="contact-email" class="hidden flex items-center gap-2 text-gray-700">

                                    <span>📧</span>

                                    <span></span>

                                </div>

                                <div id="contact-phone" class="hidden flex items-center gap-2 text-gray-700">

                                    <span>📱</span>

                                    <span></span>

                                </div>

                                <div id="contact-wechat" class="hidden flex items-center gap-2 text-gray-700">

                                    <span>💬</span>

                                    <span></span>

                                </div>

                                <div id="contact-linkedin" class="hidden flex items-center gap-2 text-gray-700">

                                    <span>🔗</span>

                                    <span class="truncate"></span>

                                </div>

                                <div id="contact-twitter" class="hidden flex items-center gap-2 text-gray-700">

                                    <span>🐦</span>

                                    <span class="truncate"></span>

                                </div>

                            </div>

                        </div>

                    </div>

                </div>

            </div>

        </div>


        <footer class="text-center mt-12 text-gray-600 text-sm">

            <p>Powered by <a href="https://Card-j0s.pages.dev" target="_blank" class="text-green-600 hover:underline">Card-j0s</a></p>

        </footer>

    </div>


    <script>

        let skillCount = 0;

        let currentColor = '#52C41A';

        let avatarDataUrl = null;


        // Initialize

        document.addEventListener('DOMContentLoaded', function() {

            addSkill(); // Start with one skill field

            setupEventListeners();

        });


        function setupEventListeners() {

            // Form inputs

            document.getElementById('name').addEventListener('input', updatePreview);

            document.getElementById('intro').addEventListener('input', updatePreview);

            document.getElementById('avatar').addEventListener('change', handleAvatarUpload);

            document.getElementById('email').addEventListener('input', updatePreview);

            document.getElementById('phone').addEventListener('input', updatePreview);

            document.getElementById('wechat').addEventListener('input', updatePreview);

            document.getElementById('linkedin').addEventListener('input', updatePreview);

            document.getElementById('twitter').addEventListener('input', updatePreview);


            // Color controls

            document.getElementById('color-picker').addEventListener('input', function(e) {

                updateColor(e.target.value);

            });

            

            document.getElementById('color-hex').addEventListener('input', function(e) {

                if (/^#[0-9A-Fa-f]{6}$/.test(e.target.value)) {

                    updateColor(e.target.value);

                }

            });


            document.querySelectorAll('.color-preset').forEach(preset => {

                preset.addEventListener('click', function() {

                    document.querySelectorAll('.color-preset').forEach(p => p.classList.remove('selected'));

                    this.classList.add('selected');

                    updateColor(this.dataset.color);

                });

            });


            // Add skill button

            document.getElementById('add-skill-btn').addEventListener('click', addSkill);


            // Form submit

            document.getElementById('profile-form').addEventListener('submit', handleSubmit);


            // Download button

            document.getElementById('download-btn').addEventListener('click', downloadCard);

        }


        function addSkill() {

            if (skillCount >= 5) return;

            

            skillCount++;

            const container = document.getElementById('skills-container');

            const skillDiv = document.createElement('div');

            skillDiv.className = 'flex gap-2';

            skillDiv.innerHTML = `

                <input type="text" class="skill-input flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent" placeholder="e.g. JavaScript, Design, etc.">

                <button type="button" class="remove-skill px-3 py-2 bg-red-100 text-red-600 rounded-lg hover:bg-red-200 transition">✕</button>

            `;

            

            const input = skillDiv.querySelector('.skill-input');

            const removeBtn = skillDiv.querySelector('.remove-skill');

            

            input.addEventListener('input', updatePreview);

            removeBtn.addEventListener('click', function() {

                skillDiv.remove();

                skillCount--;

                updateAddSkillButton();

                updatePreview();

            });

            

            container.appendChild(skillDiv);

            updateAddSkillButton();

        }


        function updateAddSkillButton() {

            const btn = document.getElementById('add-skill-btn');

            if (skillCount >= 5) {

                btn.disabled = true;

                btn.classList.add('opacity-50', 'cursor-not-allowed');

            } else {

                btn.disabled = false;

                btn.classList.remove('opacity-50', 'cursor-not-allowed');

            }

        }


        function updateColor(color) {

            currentColor = color;

            document.getElementById('color-picker').value = color;

            document.getElementById('color-hex').value = color;

            document.getElementById('card-header').style.backgroundColor = color;

        }


        function handleAvatarUpload(e) {

            const file = e.target.files[0];

            if (file) {

                const reader = new FileReader();

                reader.onload = function(event) {

                    avatarDataUrl = event.target.result;

                    const img = document.getElementById('preview-avatar');

                    const placeholder = document.getElementById('avatar-placeholder');

                    img.src = avatarDataUrl;

                    img.classList.remove('hidden');

                    placeholder.classList.add('hidden');

                };

                reader.readAsDataURL(file);

            }

        }


        function updatePreview() {

            // Name

            const name = document.getElementById('name').value || 'Your Name';

            document.getElementById('preview-name').textContent = name;


            // Intro

            const intro = document.getElementById('intro').value || 'Your introduction appears here';

            document.getElementById('preview-intro').textContent = intro;


            // Skills

            const skillInputs = document.querySelectorAll('.skill-input');

            const skills = Array.from(skillInputs)

                .map(input => input.value.trim())

                .filter(skill => skill !== '');

            

            const skillsSection = document.getElementById('preview-skills');

            const skillsList = document.getElementById('skills-list');

            

            if (skills.length > 0) {

                skillsSection.classList.remove('hidden');

                skillsList.innerHTML = skills.map(skill => 

                    `<span class="px-3 py-1 bg-gray-100 text-gray-700 rounded-full text-sm">${skill}</span>`

                ).join('');

            } else {

                skillsSection.classList.add('hidden');

            }


            // Contact details

            const contactSection = document.getElementById('preview-contact');

            let hasContact = false;


            const email = document.getElementById('email').value;

            const emailDiv = document.getElementById('contact-email');

            if (email) {

                emailDiv.classList.remove('hidden');

                emailDiv.querySelector('span:last-child').textContent = email;

                hasContact = true;

            } else {

                emailDiv.classList.add('hidden');

            }


            const phone = document.getElementById('phone').value;

            const phoneDiv = document.getElementById('contact-phone');

            if (phone) {

                phoneDiv.classList.remove('hidden');

                phoneDiv.querySelector('span:last-child').textContent = phone;

                hasContact = true;

            } else {

                phoneDiv.classList.add('hidden');

            }


            const wechat = document.getElementById('wechat').value;

            const wechatDiv = document.getElementById('contact-wechat');

            if (wechat) {

                wechatDiv.classList.remove('hidden');

                wechatDiv.querySelector('span:last-child').textContent = wechat;

                hasContact = true;

            } else {

                wechatDiv.classList.add('hidden');

            }


            const linkedin = document.getElementById('linkedin').value;

            const linkedinDiv = document.getElementById('contact-linkedin');

            if (linkedin) {

                linkedinDiv.classList.remove('hidden');

                linkedinDiv.querySelector('span:last-child').textContent = linkedin;

                hasContact = true;

            } else {

                linkedinDiv.classList.add('hidden');

            }


            const twitter = document.getElementById('twitter').value;

            const twitterDiv = document.getElementById('contact-twitter');

            if (twitter) {

                twitterDiv.classList.remove('hidden');

                twitterDiv.querySelector('span:last-child').textContent = twitter;

                hasContact = true;

            } else {

                twitterDiv.classList.add('hidden');

            }


            if (hasContact) {

                contactSection.classList.remove('hidden');

            } else {

                contactSection.classList.add('hidden');

            }

        }


        function handleSubmit(e) {

            e.preventDefault();

            document.getElementById('download-section').classList.remove('hidden');

            document.getElementById('download-section').scrollIntoView({ behavior: 'smooth', block: 'nearest' });

        }


        async function downloadCard() {

            const card = document.getElementById('preview-card');

            const size = document.getElementById('card-size').value;

            const [width, height] = size.split('x').map(Number);

            

            // Calculate scale factor

            const currentWidth = card.offsetWidth;

            const scale = width / currentWidth;

            

            try {

                const canvas = await html2canvas(card, {

                    scale: scale,

                    backgroundColor: '#ffffff',

                    logging: false,

                    useCORS: true

                });

                

                const link = document.createElement('a');

                link.download = `profile-card-${Date.now()}.png`;

                link.href = canvas.toDataURL('image/png');

                link.click();

            } catch (error) {

                console.error('Error generating image:', error);

                alert('Failed to generate card. Please try again.');

            }

        }

    </script>

</body>

</html>