Sistema de Gestión de Carnés con QR en PHP: Solución Completa para Control de Accesos

Introducción

Controlar el acceso a instalaciones, eventos y oficinas es esencial para la seguridad de cualquier organización. Con la llegada de soluciones digitales, es posible automatizar la creación de carnés, validar la identidad de visitantes y empleados, y mantener un registro detallado de entradas y salidas. Nuestro Sistema de Gestión de Carnés con QR en PHP ha sido desarrollado para ofrecer una solución completa, escalable y asequible, con opciones que van desde un generador gratuito hasta versiones empresariales completas.

En ConfiguroWeb, hemos diseñado múltiples versiones que se adaptan a diferentes necesidades y presupuestos, utilizando tecnologías modernas como PHP, MySQL, SQLite y soporte para códigos QR. Este artículo detallará cada versión, ofrecerá un tutorial con código para la versión gratuita y explicará cómo puedes adquirir y utilizar las versiones premium para maximizar el control de accesos.


🆓 Versión Gratuita – Generador Estático de Carnés

La versión gratuita del Sistema de Gestión de Carnés con QR en PHP permite a cualquier usuario generar carnés básicos sin necesidad de instalación ni base de datos.

  • Tecnología: HTML, CSS y JavaScript.
  • Funciones:
    • Generación de carnés personalizados con foto y datos básicos.
    • Descarga directa para imprimir o compartir digitalmente.
  • Demo: Probar Generador Gratuito en GitHub Pages
  • Código abierto: Disponible para personalización en GitHub.

Código de la Versión Gratuita

index.html
<!-- index.html -->
<!DOCTYPE html>
<html lang="es">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Generador de Tarjeta de Visitante</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="./style.css">
    <link rel="shortcut icon" href="./icon.png" type="image/x-icon">
</head>

<body>
    <div class="container">
        <header>
            <h1>Generador de Tarjeta de Visitante</h1>
            <p class="subtitle">Crea tarjetas de identificación profesionales en segundos</p>
        </header>

        <div class="app-container">
            <section class="form-section fade-in">
                <h2>Detalles de la Tarjeta</h2>

                <div class="template-selector">
                    <!-- plantillas sin cambios -->
                </div>

                <div class="form-group">
                    <label for="cardTitle">Título de la Tarjeta</label>
                    <input type="text" id="cardTitle" placeholder="p. ej., PASE DE VISITANTE" value="PASE DE VISITANTE">
                </div>

                <div class="form-group">
                    <label for="headerColor">Color del Encabezado</label>
                    <div class="color-options">
                        <!-- opciones de color sin cambios -->
                    </div>
                </div>

                <div class="form-group">
                    <label for="name">Nombre del Visitante</label>
                    <input type="text" id="name" placeholder="p. ej., Mauricio Sevilla">
                </div>

                <div class="form-group">
                    <label for="company">Empresa/Organización</label>
                    <input type="text" id="company" placeholder="p. ej., ConfiguroWeb">
                </div>

                <div class="form-group">
                    <label for="purpose">Motivo de la Visita</label>
                    <input type="text" id="purpose" placeholder="Reunión, Entrega, etc.">
                </div>

                <div class="form-group">
                    <label for="contact">Persona de Contacto</label>
                    <input type="text" id="contact" placeholder="p. ej., Responsable">
                </div>

                <div class="form-group">
                    <label for="validUntil">Válido Hasta</label>
                    <input type="date" id="validUntil">
                </div>

                <div class="form-group">
                    <label for="photo">Subir Foto</label>
                    <input type="file" id="photo" accept="image/*">
                </div>

                <div class="form-group">
                    <label for="logo">Subir Logotipo (opcional)</label>
                    <input type="file" id="logo" accept="image/*">
                </div>

                <div class="form-group">
                    <label for="additionalInfo">Información Adicional</label>
                    <textarea id="additionalInfo" rows="3" placeholder="Cualquier otro detalle a incluir"></textarea>
                </div>



                <div class="btn-group">
                    <button id="generateBtn" class="btn btn-block">Generar Tarjeta</button>
                    <button id="resetBtn" class="btn btn-outline">Restablecer</button>
                </div>
            </section>

            <section class="preview-section fade-in" style="animation-delay: 0.2s;">
                <h2>Vista Previa de la Tarjeta</h2>
                <div class="card-container" id="card">
                    <div class="card-header">
                        <h2 id="cardTitleDisplay">PASE DE VISITANTE</h2>
                    </div>
                    <div class="card-body">
                        <div class="card-photo">
                            <div class="photo-placeholder">
                                <i class="fas fa-user"></i>
                            </div>
                        </div>
                        <div class="card-details">
                            <p class="name">Mauricio Sevilla Britto</p>
                            <p class="company">ConfiguroWeb</p>
                            <p><strong>Motivo:</strong> <span id="purposeDisplay">Reunión</span></p>
                            <p><strong>Contacto:</strong> <span id="contactDisplay">Responsable</span></p>
                            <p><strong>Válido Hasta:</strong> <span id="validUntilDisplay">31/12/2025</span></p>
                        </div>
                    </div>
                    <div class="card-footer">
                        <span id="additionalInfoDisplay">Solo personal autorizado</span>
                        <img src="" alt="Logotipo" class="card-logo" id="logoDisplay" style="display: none;">
                    </div>
                    <div class="signature" id="signatureDisplay">
                        Firma Autorizada
                    </div>
                </div>

                <div class="action-buttons">
                    <button id="downloadBtn" class="btn">
                        <i class="fas fa-download"></i> Guardar como Imagen
                    </button>
                    <button id="saveTemplateBtn" class="btn btn-outline">
                        <i class="fas fa-save"></i> Guardar Plantilla
                    </button>
                </div>
            </section>
        </div>
    </div>

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

</html>
script.js
// JavaScript will go here
document.addEventListener('DOMContentLoaded', function() {
    // DOM Elements
    const card = document.getElementById('card');
    const cardTitleInput = document.getElementById('cardTitle');
    const cardTitleDisplay = document.getElementById('cardTitleDisplay');
    const nameInput = document.getElementById('name');
    const companyInput = document.getElementById('company');
    const purposeInput = document.getElementById('purpose');
    const purposeDisplay = document.getElementById('purposeDisplay');
    const contactInput = document.getElementById('contact');
    const contactDisplay = document.getElementById('contactDisplay');
    const validUntilInput = document.getElementById('validUntil');
    const validUntilDisplay = document.getElementById('validUntilDisplay');
    const photoInput = document.getElementById('photo');
    const photoPlaceholder = document.querySelector('.photo-placeholder');
    const logoInput = document.getElementById('logo');
    const logoDisplay = document.getElementById('logoDisplay');
    const additionalInfoInput = document.getElementById('additionalInfo');
    const additionalInfoDisplay = document.getElementById('additionalInfoDisplay'); 
    const signatureDisplay = document.getElementById('signatureDisplay');
    const generateBtn = document.getElementById('generateBtn');
    const resetBtn = document.getElementById('resetBtn');
    const downloadBtn = document.getElementById('downloadBtn');
    const saveTemplateBtn = document.getElementById('saveTemplateBtn');
    const colorOptions = document.querySelectorAll('.color-option');
    const templates = document.querySelectorAll('.template');
    const cardHeader = document.querySelector('.card-header');

    // Set default date (today + 1 day)
    const today = new Date();
    const tomorrow = new Date(today);
    tomorrow.setDate(tomorrow.getDate() + 1);
    validUntilInput.valueAsDate = tomorrow;
    updateValidUntilDisplay();

    // Event Listeners
    cardTitleInput.addEventListener('input', updateCardTitle);
    nameInput.addEventListener('input', updateName);
    companyInput.addEventListener('input', updateCompany);
    purposeInput.addEventListener('input', updatePurpose);
    contactInput.addEventListener('input', updateContact);
    validUntilInput.addEventListener('change', updateValidUntilDisplay);
    photoInput.addEventListener('change', handlePhotoUpload);
    logoInput.addEventListener('change', handleLogoUpload);
    additionalInfoInput.addEventListener('input', updateAdditionalInfo);
    generateBtn.addEventListener('click', generateCard);
    resetBtn.addEventListener('click', resetForm);
    downloadBtn.addEventListener('click', downloadCard);
    saveTemplateBtn.addEventListener('click', saveTemplate);

    // Color options
    colorOptions.forEach(option => {
        option.addEventListener('click', function() {
            document.querySelector('.color-option.selected').classList.remove('selected');
            this.classList.add('selected');
            cardHeader.style.backgroundColor = this.dataset.color;
        });
    });

    // Template selection
    templates.forEach(template => {
        template.addEventListener('click', function() {
            document.querySelector('.template.selected').classList.remove('selected');
            this.classList.add('selected');
            applyTemplate(this.dataset.template);
        });
    });

    // Initialize with template 1
    applyTemplate('1');

    // Functions
    function updateCardTitle() {
        cardTitleDisplay.textContent = cardTitleInput.value.toUpperCase();
    }

    function updateName() {
        document.querySelector('.name').textContent = nameInput.value || 'Mauricio Sevilla';
    }

    function updateCompany() {
        document.querySelector('.company').textContent = companyInput.value || 'CofiguroWeb';
    }

    function updatePurpose() {
        purposeDisplay.textContent = purposeInput.value || 'Reunión';
    }

    function updateContact() {
        contactDisplay.textContent = contactInput.value || 'Responsable';
    }

    function updateValidUntilDisplay() {
        if (validUntilInput.value) {
            const date = new Date(validUntilInput.value);
            const formattedDate = date.toLocaleDateString('en-GB');
            validUntilDisplay.textContent = formattedDate;
        }
    }

    function updateAdditionalInfo() {
        additionalInfoDisplay.textContent = additionalInfoInput.value || 'Solo Personal Autorizado';
    }

    function handlePhotoUpload(e) {
        const file = e.target.files[0];
        if (file) {
            const reader = new FileReader();
            reader.onload = function(event) {
                photoPlaceholder.innerHTML = `<img src="${event.target.result}" alt="Foto de Visitante">`;
            };
            reader.readAsDataURL(file);
        }
    }

    function handleLogoUpload(e) {
        const file = e.target.files[0];
        if (file) {
            const reader = new FileReader();
            reader.onload = function(event) {
                logoDisplay.src = event.target.result;
                logoDisplay.style.display = 'block';
            };
            reader.readAsDataURL(file);
        }
    }

    function applyTemplate(templateNumber) {
        // Reset all styles first
        cardHeader.style.background = '';
        
        switch(templateNumber) {
            case '1':
                cardHeader.style.backgroundColor = '#4361ee';
                break;
            case '2':
                cardHeader.style.background = 'linear-gradient(45deg, #4361ee, #3a0ca3)';
                break;
            case '3':
                cardHeader.style.backgroundColor = '#3f37c9';
                break;
            case '4':
                cardHeader.style.background = 'linear-gradient(45deg, #4895ef, #4361ee)';
                break;
            case '5':
                cardHeader.style.backgroundColor = '#4cc9f0';
                break;
        }
        
        // Update selected color option to match template
        const headerColor = window.getComputedStyle(cardHeader).backgroundColor;
        colorOptions.forEach(option => {
            option.classList.remove('selected');
            if (option.dataset.color === rgbToHex(headerColor) || 
                (templateNumber === '2' && option.dataset.color === '#4361ee') ||
                (templateNumber === '4' && option.dataset.color === '#4895ef')) {
                option.classList.add('selected');
            }
        });
    }

    function rgbToHex(rgb) {
        // This is a simplified version that works for solid colors
        if (rgb.includes('gradient')) {
            return null; // Can't convert gradient to hex
        }
        
        const rgbValues = rgb.match(/\d+/g);
        if (!rgbValues || rgbValues.length < 3) return null;
        
        const r = parseInt(rgbValues[0]);
        const g = parseInt(rgbValues[1]);
        const b = parseInt(rgbValues[2]);
        
        return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
    }

    function generateCard() {        
        // Show signature if name is entered
        signatureDisplay.style.display = nameInput.value ? 'block' : 'none';
        
        // Add slight animation to show card was updated
        card.classList.add('fade-in');
        setTimeout(() => {
            card.classList.remove('fade-in');
        }, 500);
    }

    function resetForm() {
        // Reset all inputs
        cardTitleInput.value = 'Pase de Visitante';
        nameInput.value = '';
        companyInput.value = '';
        purposeInput.value = '';
        contactInput.value = '';
        validUntilInput.valueAsDate = tomorrow;
        photoInput.value = '';
        logoInput.value = '';
        additionalInfoInput.value = '';
        
        // Reset preview
        updateCardTitle();
        updateName();
        updateCompany();
        updatePurpose();
        updateContact();
        updateValidUntilDisplay();
        updateAdditionalInfo();
        
        // Reset photo and logo
        photoPlaceholder.innerHTML = '<i class="fas fa-user"></i>';
        logoDisplay.src = '';
        logoDisplay.style.display = 'none';
        
        // Reset to template 1
        document.querySelector('.template.selected').classList.remove('selected');
        document.querySelector('.template-1').classList.add('selected');
        applyTemplate('1');
        
        // Hide signature
        signatureDisplay.style.display = 'none';
    }

function downloadCard() {
    const originalOpacity = card.style.opacity;
    card.style.opacity = '1';

    html2canvas(card, {
        scale: 3,
        logging: false,
        useCORS: true,
        allowTaint: true,
        backgroundColor: '#ffffff'
    }).then(canvas => {
        canvas.toBlob(blob => {
            const link = document.createElement('a');
            link.download = `visitor-card-${nameInput.value || 'visitor'}.png`;
            const url = URL.createObjectURL(blob);
            link.href = url;
            link.click();
            setTimeout(() => URL.revokeObjectURL(url), 100);
            // Restaurar opacidad
            card.style.opacity = originalOpacity;
        }, 'image/png', 1);
    }).catch(() => {
        card.style.opacity = originalOpacity;
    });
}




    function saveTemplate() {
  // ...
  // Identifica el elemento seleccionado, si existe
  const sel = document.querySelector('.template.selected');
  const tpl = sel ? sel.dataset.template : '1';

  const templateData = {
    title: cardTitleInput.value,
    name: nameInput.value,
    company: companyInput.value,
    purpose: purposeInput.value,
    contact: contactInput.value,
    validUntil: validUntilInput.value,
    additionalInfo: additionalInfoInput.value,
    headerColor: window.getComputedStyle(cardHeader).backgroundColor,
    template: tpl    // ← uso de tpl en lugar de acceso directo
  };
  localStorage.setItem('visitorCardTemplate', JSON.stringify(templateData));
  alert('¡Plantilla guardada correctamente! Puedes cargarla la próxima vez.');
}


    // Check for saved template on load
    function loadTemplate() {
        const savedTemplate = localStorage.getItem('visitorCardTemplate');
        if (savedTemplate) {
            const templateData = JSON.parse(savedTemplate);
            
            cardTitleInput.value = templateData.title;
            nameInput.value = templateData.name;
            companyInput.value = templateData.company;
            purposeInput.value = templateData.purpose;
            contactInput.value = templateData.contact;
            validUntilInput.value = templateData.validUntil;
            additionalInfoInput.value = templateData.additionalInfo;
            includeQRCheckbox.checked = templateData.includeQR;
            
            // Apply template design
            document.querySelector('.template.selected').classList.remove('selected');
            document.querySelector(`.template-${templateData.template}`).classList.add('selected');
            applyTemplate(templateData.template);
            
            // Update all displays
            updateCardTitle();
            updateName();
            updateCompany();
            updatePurpose();
            updateContact();
            updateValidUntilDisplay();
            updateAdditionalInfo();
            toggleQRCode();
        }
    }

    // Load any saved template when page loads
    loadTemplate();
});
style.css
/* CSS Mejorado - Generador de Tarjetas de Visitante */
:root {
    --primary-color: #4361ee;
    --secondary-color: #3f37c9;
    --accent-color: #4895ef;
    --light-color: #f8f9fa;
    --dark-color: #212529;
    --success-color: #4cc9f0;
    --warning-color: #f72585;
    --card-width: 350px;
    --card-height: 220px;
    --gradient-1: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    --gradient-2: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
    --gradient-3: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
    --shadow-light: 0 4px 20px rgba(0, 0, 0, 0.08);
    --shadow-medium: 0 8px 30px rgba(0, 0, 0, 0.12);
    --shadow-heavy: 0 15px 40px rgba(0, 0, 0, 0.15);
}

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: 'Poppins', sans-serif;
    background: linear-gradient(135deg, #667eea 0%, #0d0019 100%);
    background-attachment: fixed;
    color: var(--dark-color);
    line-height: 1.6;
    padding: 20px;
    min-height: 100vh;
    position: relative;
}

/* Efecto de partículas en el fondo */
body::before {
    content: '';
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-image: 
        radial-gradient(circle at 20% 20%, rgba(255, 255, 255, 0.1) 1px, transparent 1px),
        radial-gradient(circle at 80% 80%, rgba(255, 255, 255, 0.1) 1px, transparent 1px),
        radial-gradient(circle at 40% 40%, rgba(255, 255, 255, 0.1) 1px, transparent 1px);
    background-size: 50px 50px, 100px 100px, 75px 75px;
    pointer-events: none;
    z-index: -1;
    animation: float 20s ease-in-out infinite;
}

@keyframes float {
    0%, 100% { transform: translateY(0px); }
    50% { transform: translateY(-10px); }
}

.container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 20px;
    position: relative;
    z-index: 1;
}

header {
    text-align: center;
    margin-bottom: 40px;
    background: rgba(255, 255, 255, 0.1);
    backdrop-filter: blur(10px);
    border-radius: 20px;
    padding: 30px;
    border: 1px solid rgba(255, 255, 255, 0.2);
    box-shadow: var(--shadow-light);
}

h1 {
    color: white;
    margin-bottom: 10px;
    font-weight: 700;
    font-size: 2.5rem;
    text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
    background: linear-gradient(45deg, #fff, #f0f2f5);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
}

.subtitle {
    color: rgba(255, 255, 255, 0.9);
    font-weight: 300;
    font-size: 1.1rem;
    text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3);
}

.app-container {
    display: flex;
    flex-wrap: wrap;
    gap: 30px;
    justify-content: center;
    align-items: flex-start;
}

.form-section {
    flex: 1;
    min-width: 300px;
    background: rgba(255, 255, 255, 0.95);
    backdrop-filter: blur(15px);
    padding: 30px;
    border-radius: 20px;
    box-shadow: var(--shadow-medium);
    border: 1px solid rgba(255, 255, 255, 0.3);
    position: relative;
    overflow: hidden;
}

.form-section::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 4px;
    background: linear-gradient(90deg, var(--primary-color), var(--accent-color), var(--success-color));
    border-radius: 20px 20px 0 0;
}

.form-section h2 {
    color: var(--primary-color);
    margin-bottom: 25px;
    font-weight: 600;
    font-size: 1.3rem;
    position: relative;
    padding-bottom: 10px;
}

.form-section h2::after {
    content: '';
    position: absolute;
    bottom: 0;
    left: 0;
    width: 50px;
    height: 3px;
    background: linear-gradient(90deg, var(--primary-color), var(--accent-color));
    border-radius: 2px;
}

.preview-section {
    flex: 1;
    min-width: 300px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 25px;
}

.preview-section h2 {
    color: white;
    text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
    font-weight: 600;
    font-size: 1.3rem;
    text-align: center;
    margin-bottom: 10px;
}

.form-group {
    margin-bottom: 25px;
    position: relative;
}

label {
    display: block;
    margin-bottom: 8px;
    font-weight: 500;
    color: #333;
    font-size: 0.9rem;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    position: relative;
}

input, select, textarea {
    width: 100%;
    padding: 12px 18px;
    border: 2px solid rgba(67, 97, 238, 0.1);
    border-radius: 12px;
    font-family: 'Poppins', sans-serif;
    font-size: 14px;
    transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
    background: rgba(255, 255, 255, 0.8);
    backdrop-filter: blur(10px);
    position: relative;
}

input:focus, select:focus, textarea:focus {
    border-color: var(--primary-color);
    outline: none;
    box-shadow: 0 0 0 4px rgba(67, 97, 238, 0.15);
    transform: translateY(-2px);
    background: rgba(255, 255, 255, 0.95);
}

input:hover, select:hover, textarea:hover {
    border-color: rgba(67, 97, 238, 0.3);
    transform: translateY(-1px);
}

.color-options {
    display: flex;
    gap: 12px;
    margin-top: 15px;
    flex-wrap: wrap;
}

.color-option {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    cursor: pointer;
    border: 3px solid rgba(255, 255, 255, 0.8);
    transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
    box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
    position: relative;
    overflow: hidden;
}

.color-option::before {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: 0;
    height: 0;
    background: rgba(255, 255, 255, 0.3);
    border-radius: 50%;
    transform: translate(-50%, -50%);
    transition: all 0.3s ease;
}

.color-option:hover {
    transform: scale(1.15);
    box-shadow: 0 8px 25px rgba(0, 0, 0, 0.2);
}

.color-option:hover::before {
    width: 100%;
    height: 100%;
}

.color-option.selected {
    border-color: var(--dark-color);
    transform: scale(1.1);
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
}

.color-option.selected::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    color: white;
    font-weight: bold;
    font-size: 16px;
    text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
}

.btn {
    display: inline-block;
    padding: 12px 24px;
    background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
    color: white;
    border: none;
    border-radius: 12px;
    cursor: pointer;
    font-family: 'Poppins', sans-serif;
    font-size: 16px;
    font-weight: 600;
    transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
    text-align: center;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    position: relative;
    overflow: hidden;
    box-shadow: 0 4px 15px rgba(67, 97, 238, 0.3);
}

.btn::before {
    content: '';
    position: absolute;
    top: 0;
    left: -100%;
    width: 100%;
    height: 100%;
    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
    transition: left 0.5s;
}

.btn:hover::before {
    left: 100%;
}

.btn:hover {
    transform: translateY(-3px);
    box-shadow: 0 8px 25px rgba(67, 97, 238, 0.4);
}

.btn:active {
    transform: translateY(-1px);
}

.btn-block {
    display: block;
    width: 100%;
}

.btn-group {
    display: flex;
    gap: 15px;
    margin-top: 25px;
}

.btn-outline {
    background: transparent;
    border: 2px solid var(--primary-color);
    color: var(--primary-color);
    box-shadow: 0 4px 15px rgba(67, 97, 238, 0.1);
}

.btn-outline:hover {
    background: var(--primary-color);
    color: white;
    transform: translateY(-3px);
    box-shadow: 0 8px 25px rgba(67, 97, 238, 0.3);
}

.btn-warning {
    background: linear-gradient(135deg, var(--warning-color), #d91a6d);
    box-shadow: 0 4px 15px rgba(247, 37, 133, 0.3);
}

.btn-warning:hover {
    box-shadow: 0 8px 25px rgba(247, 37, 133, 0.4);
}

.card-container {
    width: var(--card-width);
    background: linear-gradient(145deg, #ffffff, #f0f2f5);
    border-radius: 15px;
    overflow: hidden;
    box-shadow: var(--shadow-heavy);
    position: relative;
    display: flex;
    flex-direction: column;
    padding-bottom: 50px;
    border: 1px solid rgba(255, 255, 255, 0.3);
    transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
}

.card-container:hover {
    transform: translateY(-5px);
    box-shadow: 0 20px 50px rgba(0, 0, 0, 0.2);
}

.card-header {
    height: 70px;
    background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
    display: flex;
    align-items: center;
    justify-content: center;
    color: white;
    position: relative;
    overflow: hidden;
}

.card-header::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grain" width="100" height="100" patternUnits="userSpaceOnUse"><circle cx="25" cy="25" r="1" fill="rgba(255,255,255,0.1)"/><circle cx="75" cy="75" r="1" fill="rgba(255,255,255,0.1)"/><circle cx="50" cy="10" r="0.5" fill="rgba(255,255,255,0.1)"/><circle cx="10" cy="50" r="0.5" fill="rgba(255,255,255,0.1)"/><circle cx="90" cy="30" r="0.5" fill="rgba(255,255,255,0.1)"/></pattern></defs><rect width="100%" height="100%" fill="url(%23grain)"/></svg>');
    opacity: 0.3;
}

.card-header h2 {
    font-size: 18px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 2px;
    text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
    position: relative;
    z-index: 1;
}

.card-body {
    padding: 20px;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    background: linear-gradient(145deg, #ffffff, #f8f9fa);
}

.card-photo {
    width: 30%;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 15px;
}

.photo-placeholder {
    width: 85px;
    height: 85px;
    border-radius: 50%;
    background: linear-gradient(145deg, #f0f2f5, #e9ecef);
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    border: 4px solid white;
    box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
    position: relative;
    transition: all 0.3s ease;
}

.photo-placeholder:hover {
    transform: scale(1.05);
    box-shadow: 0 12px 35px rgba(0, 0, 0, 0.2);
}

.photo-placeholder::before {
    content: '';
    position: absolute;
    top: -2px;
    left: -2px;
    right: -2px;
    bottom: -2px;
    background: linear-gradient(45deg, var(--primary-color), var(--accent-color));
    border-radius: 50%;
    z-index: -1;
}

.photo-placeholder img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: 50%;
}

.photo-placeholder i {
    font-size: 35px;
    color: #999;
    background: linear-gradient(45deg, var(--primary-color), var(--accent-color));
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
}

.card-details {
    width: 100%;
    display: flex;
    flex-direction: column;
    justify-content: center;
}

.card-details p {
    margin: 4px 0;
    font-size: 12px;
    color: #555;
}

.card-details .name {
    font-size: 17px;
    font-weight: 700;
    margin-bottom: 8px;
    color: var(--dark-color);
    text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
}

.card-details .company {
    font-weight: 600;
    color: var(--primary-color);
    margin-bottom: 10px;
    font-size: 13px;
}

.card-footer {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    padding: 10px 15px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 10px;
    color: #666;
    background: linear-gradient(145deg, #f8f9fa, #e9ecef);
    border-top: 1px solid rgba(0, 0, 0, 0.1);
}

.card-logo {
    max-height: 25px;
    max-width: 80px;
    filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));
}

.action-buttons {
    display: flex;
    gap: 15px;
    flex-wrap: wrap;
    justify-content: center;
}

.qr-code {
    position: absolute;
    bottom: 15px;
    right: 15px;
    width: 50px;
    height: 50px;
    background: linear-gradient(145deg, #ffffff, #f0f2f5);
    padding: 3px;
    border-radius: 8px;
    box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15);
    border: 1px solid rgba(0, 0, 0, 0.1);
}

.qr-code img {
    width: 100%;
    height: 100%;
    border-radius: 5px;
}

.signature {
    position: absolute;
    bottom: 15px;
    right: 130px;
    width: 80px;
    height: 30px;
    border-top: 2px solid var(--primary-color);
    text-align: center;
    font-size: 10px;
    color: var(--primary-color);
    font-weight: 600;
    padding-top: 5px;
}

.template-selector {
    display: flex;
    gap: 15px;
    margin-bottom: 25px;
    flex-wrap: wrap;
}

.template {
    width: 70px;
    height: 45px;
    border-radius: 8px;
    cursor: pointer;
    border: 3px solid rgba(67, 97, 238, 0.2);
    transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
    overflow: hidden;
    position: relative;
    box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
}

.template:hover {
    transform: scale(1.08);
    box-shadow: 0 8px 25px rgba(0, 0, 0, 0.2);
}

.template.selected {
    border-color: var(--primary-color);
    transform: scale(1.05);
    box-shadow: 0 6px 20px rgba(67, 97, 238, 0.3);
}

.template.selected::after {
    content: '';
    position: absolute;
    top: 5px;
    right: 5px;
    background: var(--primary-color);
    color: white;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 12px;
    font-weight: bold;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}

.template .template-header {
    height: 30%;
    width: 100%;
    position: relative;
    overflow: hidden;
}

.template .template-header::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="dots" width="20" height="20" patternUnits="userSpaceOnUse"><circle cx="10" cy="10" r="1" fill="rgba(255,255,255,0.2)"/></pattern></defs><rect width="100%" height="100%" fill="url(%23dots)"/></svg>');
    opacity: 0.5;
}

.template .template-body {
    height: 70%;
    width: 100%;
    background: linear-gradient(145deg, #ffffff, #f8f9fa);
    position: relative;
}

.template .template-photo {
    position: absolute;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: linear-gradient(145deg, #e9ecef, #dee2e6);
    top: 12px;
    left: 12px;
    border: 2px solid white;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.template-1 .template-header {
    background: linear-gradient(135deg, #4361ee, #3a0ca3);
}

.template-2 .template-header {
    background: linear-gradient(135deg, #4361ee, #3a0ca3);
}

.template-3 .template-header {
    background: linear-gradient(135deg, #3f37c9, #7209b7);
}

.template-4 .template-header {
    background: linear-gradient(135deg, #4895ef, #4361ee);
}

.template-5 .template-header {
    background: linear-gradient(135deg, #4cc9f0, #3a86ff);
}

@media (max-width: 768px) {
    .app-container {
        flex-direction: column;
    }
    
    .form-section, .preview-section {
        width: 100%;
    }
    
    h1 {
        font-size: 2rem;
    }
    
    .container {
        padding: 10px;
    }
    
    .form-section {
        padding: 20px;
    }
    
    .btn-group {
        flex-direction: column;
        gap: 10px;
    }
    
    .template-selector {
        justify-content: center;
    }
    
    .color-options {
        justify-content: center;
    }
    
    .action-buttons {
        flex-direction: column;
        align-items: center;
    }
}

@media (max-width: 480px) {
    .card-container {
        width: 320px;
    }
    
    .template {
        width: 60px;
        height: 40px;
    }
    
    .color-option {
        width: 35px;
        height: 35px;
    }
}

/* Animaciones mejoradas */
@keyframes fadeIn {
    from { 
        opacity: 0; 
        transform: translateY(30px);
    }
    to { 
        opacity: 1; 
        transform: translateY(0);
    }
}

@keyframes slideIn {
    from {
        opacity: 0;
        transform: translateX(-30px);
    }
    to {
        opacity: 1;
        transform: translateX(0);
    }
}

@keyframes scaleIn {
    from {
        opacity: 0;
        transform: scale(0.95);
    }
    to {
        opacity: 1;
        transform: scale(1);
    }
}

.fade-in {
    animation: fadeIn 0.6s cubic-bezier(0.25, 0.8, 0.25, 1) forwards;
}

.form-section {
    animation: slideIn 0.6s cubic-bezier(0.25, 0.8, 0.25, 1) forwards;
}

.card-container {
    animation: scaleIn 0.6s cubic-bezier(0.25, 0.8, 0.25, 1) forwards;
}

/* Efectos de glassmorphism */
.form-section, header {
    backdrop-filter: blur(15px);
    -webkit-backdrop-filter: blur(15px);
}

/* Scrollbar personalizada */
::-webkit-scrollbar {
    width: 8px;
}

::-webkit-scrollbar-track {
    background: rgba(255, 255, 255, 0.1);
    border-radius: 4px;
}

::-webkit-scrollbar-thumb {
    background: linear-gradient(135deg, var(--primary-color), var(--accent-color));
    border-radius: 4px;
}

::-webkit-scrollbar-thumb:hover {
    background: linear-gradient(135deg, var(--accent-color), var(--primary-color));
}

/* Efectos de hover para inputs */
.form-group {
    position: relative;
}

.form-group::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: linear-gradient(135deg, rgba(67, 97, 238, 0.05), rgba(72, 149, 239, 0.05));
    border-radius: 12px;
    opacity: 0;
    transition: opacity 0.3s ease;
    z-index: -1;
}

.form-group:hover::before {
    opacity: 1;
}

Este generador básico es ideal para pequeños eventos o necesidades simples de identificación.


💻 Versiones Premium y Funcionalidades

Nuestro Sistema de Gestión de Carnés con QR en PHP ofrece versiones premium con características adicionales:

VersiónFunciones principalesPrecioEnlace Demo
BásicaGeneración y almacenamiento de carnés en MySQL.12 USDhttps://demoscweb.com/id-card-basic/login.php
ValidadoraIncluye versión básica + validación de QR para comprobar autenticidad.20 USDhttps://demoscweb.com/id-card-validador/login.php
Control de AccesosGestión de entradas por carné, ideal para eventos con accesos limitados.25 USDhttps://demoscweb.com/id-card-admin/login.php
Gestión de UsuariosRoles de administrador y validador para control multiusuario.35 USDhttps://demoscweb.com/id-card-max/login.php
Completa (Web + Escritorio)Todas las funciones anteriores + versión instalable para Windows con PHP Desktop y base de datos SQLite.50 USDNo aplica demo, viene con ejecutable para windows

Tutorial – Usando la Versión Gratuita

  1. Accede a la demo gratuita.
  2. Rellena los datos del visitante.
  3. Sube la foto.
  4. Haz clic en “Generar Carné”.
  5. Descarga la imagen lista para imprimir.

Este tutorial muestra lo fácil que es comenzar con la versión gratuita del Sistema de Gestión de Carnés con QR en PHP antes de pasar a versiones más completas.


Funciones Avanzadas de las Versiones Premium

  1. Gestión de Visitantes: Base de datos robusta para almacenar y consultar registros de visitantes.
  2. Validación de QR: Lectura y verificación de carnés mediante QR para mayor seguridad.
  3. Control de Accesos: Establece límites de entradas y salidas para cada visitante.
  4. Gestión de Usuarios: Múltiples roles y permisos para un control granular.
  5. Versión de Escritorio: Funcionamiento offline con PHP Desktop y SQLite.

Referencias Internas y Externas


Cómo Comprar y Soporte

Cualquier duda me puedes contactar vía Whatsapp en el siguiente enlace: Contáctame vía Whatsapp me puedes comprar la aplicación vía PayPal, Global66, Binance con USDT o Western Union si te encuentras fuera de Colombia.

Para quienes residen en Colombia además de los medios de pago anteriores, tengo la opción de Nequi, solo es cuestión de que nos pongamos de acuerdo vía Whatsapp.


Conclusión

Con el Sistema de Gestión de Carnés con QR en PHP, puedes iniciar con la versión gratuita y escalar hasta soluciones empresariales completas que incluyen validación QR, control de accesos, gestión de usuarios y versiones instalables.

👉 Prueba gratis y lleva tu control de accesos al siguiente nivel.

Comentarios

Aún no hay comentarios. ¿Por qué no comienzas el debate?

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *