Entrada

Despliegue manual de sitios web estáticos

Netlify: Despliegue manual de sitios web estáticos

Netlify es una plataforma de hosting y automatización orientada a sitios estáticos y aplicaciones frontend modernas. Permite desplegar proyectos de forma simple, ya sea conectando un repositorio o subiendo los archivos manualmente, sin necesidad de configurar servidores.

En este artículo aprenderás a publicar un proyecto en netlify subiendo los archivos de forma manual.

Iniciar sesión en Netlify

Netlify permite iniciar sesión de forma rápida mediante proveedores externos como GitHub, GitLab, Bitbucket o Google.

Página de inicio de sesión Página de inicio de sesión

Este método facilita la integración directa con repositorios, integración que abordaremos en otro artículo.

Cuando inicias sesión por primera vez, la plataforma te permite configurar algunas preferencias. Al finalizar, solo debes desplazarte hasta el botón Continue to deploy.

Preferencias al iniciar sesión Preferencias al iniciar sesión

El siguiente paso es omitir la pantalla que muestra las opciones para desplegar el primer proyecto.

Saltar paso de desplegar primer proyecto Saltar paso de desplegar primer proyecto

Una vez autenticado, tendrás acceso al panel de control para crear y administrar tus sitios.

Desplegar un sitio de forma manual en Netlify

Netlify también permite desplegar un sitio de forma manual, sin necesidad de conectar un repositorio.

El despliegue manual es ideal para proyectos simples o sitios estáticos ya compilados (HTML, CSS y JavaScript).

Proyecto web

Ejemplo de los archivos que vamos a subir para el despliegue:

FOLDERS
    Sin archivo x
              
                
               
    mi-sitio/background.jpeg
              
            
                <!DOCTYPE html>
    <html lang="es">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Mis Certificados</title>
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <div class="container">
            <header>
                <h1>Mis Certificados</h1>
                <p class="subtitle">Una colección de mis logros y certificaciones profesionales obtenidas a lo largo de mi carrera.</p>
            </header>
    
            <div class="filters">
                <button class="filter-btn active" data-filter="all">Todos</button>
                <button class="filter-btn" data-filter="tecnologia">Tecnología</button>
                <button class="filter-btn" data-filter="desarrollo">Desarrollo</button>
                <button class="filter-btn" data-filter="diseño">Diseño</button>
                <button class="filter-btn" data-filter="negocios">Negocios</button>
                <button class="filter-btn" data-filter="idiomas">Idiomas</button>
            </div>
    
            <div class="certificates-grid" id="certificates-container">
                <!-- Certificados se cargarán aquí con JavaScript -->
            </div>
    
            <footer>
                <p>© <span id="current-year"></span> Mi Portafolio de Certificados</p>
                <p>Total de certificados: <span id="certificate-count">0</span></p>
            </footer>
        </div>
    
        <!-- Modal para vista detallada -->
        <div class="modal" id="certificate-modal">
            <div class="modal-content">
                <div class="close-modal" id="close-modal">×</div>
                <div class="modal-body" id="modal-body">
                    <!-- Contenido del modal se cargará con JavaScript -->
                </div>
            </div>
        </div>
    
        <script src="script.js"></script>
    </body>
    </html>
    
            
          
              
            
                // Datos de los certificados
    const certificates = [
        {
            id: 1,
            title: "Desarrollo Web Full Stack",
            issuer: "Coursera",
            date: "Junio 2023",
            description: "Certificación completa en desarrollo web full stack, incluyendo tecnologías frontend y backend, bases de datos y despliegue de aplicaciones.",
            tags: ["tecnologia", "desarrollo"],
            category: "tecnologia",
            icon: "fas fa-laptop-code"
        },
        {
            id: 2,
            title: "Diseño UX/UI Avanzado",
            issuer: "Google",
            date: "Marzo 2023",
            description: "Curso avanzado de diseño de experiencia de usuario e interfaz de usuario, incluyendo investigación, prototipado y pruebas de usabilidad.",
            tags: ["diseño", "ux", "ui"],
            category: "diseño",
            icon: "fas fa-palette"
        },
        {
            id: 3,
            title: "JavaScript Moderno ES6+",
            issuer: "Udemy",
            date: "Enero 2023",
            description: "Dominio de las características modernas de JavaScript, incluyendo ES6+, promesas, async/await y patrones de diseño.",
            tags: ["tecnologia", "desarrollo"],
            category: "tecnologia",
            icon: "fab fa-js-square"
        },
        {
            id: 4,
            title: "Inglés Profesional C1",
            issuer: "Cambridge University",
            date: "Diciembre 2022",
            description: "Certificación de nivel C1 (Avanzado) de inglés según el Marco Común Europeo de Referencia para las lenguas.",
            tags: ["idiomas", "inglés"],
            category: "idiomas",
            icon: "fas fa-language"
        },
        {
            id: 5,
            title: "Gestión de Proyectos Ágiles",
            issuer: "Scrum.org",
            date: "Octubre 2022",
            description: "Certificación en metodologías ágiles y Scrum para la gestión eficiente de proyectos de desarrollo de software.",
            tags: ["negocios", "gestión"],
            category: "negocios",
            icon: "fas fa-tasks"
        },
        {
            id: 6,
            title: "React.js Avanzado",
            issuer: "Meta",
            date: "Agosto 2022",
            description: "Curso avanzado de React.js que cubre hooks, context API, renderizado del lado del servidor y patrones avanzados.",
            tags: ["tecnologia", "desarrollo"],
            category: "desarrollo",
            icon: "fab fa-react"
        },
        {
            id: 7,
            title: "Diseño Gráfico Digital",
            issuer: "Adobe",
            date: "Mayo 2022",
            description: "Certificación en herramientas de diseño gráfico digital, incluyendo Photoshop, Illustrator y After Effects.",
            tags: ["diseño", "gráfico"],
            category: "diseño",
            icon: "fas fa-paint-brush"
        },
        {
            id: 8,
            title: "Análisis de Datos con Python",
            issuer: "IBM",
            date: "Febrero 2022",
            description: "Especialización en análisis de datos utilizando Python, pandas, numpy y visualización con matplotlib y seaborn.",
            tags: ["tecnologia", "datos"],
            category: "tecnologia",
            icon: "fas fa-chart-line"
        }
    ];
    
    // Elementos del DOM
    const certificatesContainer = document.getElementById('certificates-container');
    const filterButtons = document.querySelectorAll('.filter-btn');
    const modal = document.getElementById('certificate-modal');
    const closeModal = document.getElementById('close-modal');
    const modalBody = document.getElementById('modal-body');
    const certificateCount = document.getElementById('certificate-count');
    const currentYear = document.getElementById('current-year');
    
    // Estado de filtro activo
    let activeFilter = 'all';
    
    // Inicializar la página
    function init() {
        // Establecer el año actual
        currentYear.textContent = new Date().getFullYear();
        
        // Mostrar todos los certificados
        displayCertificates(certificates);
        
        // Agregar eventos a los botones de filtro
        filterButtons.forEach(button => {
            button.addEventListener('click', () => {
                // Remover clase activa de todos los botones
                filterButtons.forEach(btn => btn.classList.remove('active'));
                
                // Agregar clase activa al botón clickeado
                button.classList.add('active');
                
                // Actualizar filtro activo
                activeFilter = button.getAttribute('data-filter');
                
                // Filtrar certificados
                filterCertificates(activeFilter);
            });
        });
        
        // Cerrar modal al hacer clic en la X
        closeModal.addEventListener('click', () => {
            modal.style.display = 'none';
        });
        
        // Cerrar modal al hacer clic fuera del contenido
        modal.addEventListener('click', (e) => {
            if (e.target === modal) {
                modal.style.display = 'none';
            }
        });
    }
    
    // Mostrar certificados en el grid
    function displayCertificates(certificatesToShow) {
        certificatesContainer.innerHTML = '';
        certificateCount.textContent = certificatesToShow.length;
        
        certificatesToShow.forEach(cert => {
            const card = document.createElement('div');
            card.className = 'certificate-card';
            card.dataset.id = cert.id;
            card.dataset.category = cert.category;
            
            card.innerHTML = `
                <div class="certificate-img">
                    <i class="${cert.icon}"></i>
                </div>
                <div class="certificate-info">
                    <h3 class="certificate-title">${cert.title}</h3>
                    <div class="certificate-issuer">
                        <i class="fas fa-building"></i>
                        ${cert.issuer}
                    </div>
                    <div class="certificate-date">
                        <i class="far fa-calendar-alt"></i>
                        ${cert.date}
                    </div>
                    <div class="certificate-tags">
                        ${cert.tags.map(tag => `<span class="tag">${tag}</span>`).join('')}
                    </div>
                </div>
            `;
            
            // Agregar evento para abrir modal
            card.addEventListener('click', () => openCertificateModal(cert));
            
            certificatesContainer.appendChild(card);
        });
    }
    
    // Filtrar certificados por categoría
    function filterCertificates(filter) {
        if (filter === 'all') {
            displayCertificates(certificates);
        } else {
            const filteredCertificates = certificates.filter(cert => 
                cert.category === filter || cert.tags.includes(filter)
            );
            displayCertificates(filteredCertificates);
        }
    }
    
    // Abrir modal con detalles del certificado
    function openCertificateModal(certificate) {
        modalBody.innerHTML = `
            <div class="modal-img">
                <i class="${certificate.icon}" style="font-size: 6rem; color: #4a6491;"></i>
            </div>
            <h2 class="modal-title">${certificate.title}</h2>
            <div class="modal-details">
                <div class="detail-item">
                    <span class="detail-label">Institución</span>
                    <span>${certificate.issuer}</span>
                </div>
                <div class="detail-item">
                    <span class="detail-label">Fecha de emisión</span>
                    <span>${certificate.date}</span>
                </div>
                <div class="detail-item">
                    <span class="detail-label">Categoría</span>
                    <span>${certificate.category.charAt(0).toUpperCase() + certificate.category.slice(1)}</span>
                </div>
                <div class="detail-item">
                    <span class="detail-label">ID de certificado</span>
                    <span>#${certificate.id.toString().padStart(3, '0')}</span>
                </div>
            </div>
            <div class="modal-description">
                <p>${certificate.description}</p>
            </div>
            <div class="certificate-tags" style="margin-top: 20px;">
                ${certificate.tags.map(tag => `<span class="tag">${tag}</span>`).join('')}
            </div>
        `;
        
        modal.style.display = 'flex';
    }
    
    // Inicializar cuando el DOM esté cargado
    document.addEventListener('DOMContentLoaded', init);
    
            
          
              
            
                * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
        font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    }
    
    body {
        background-color: #f8f9fa;
        color: #333;
        line-height: 1.6;
        /* Fondo con imagen elegante */
        background-image: url('background.jpeg');
        background-size: cover;
        background-position: center;
        background-attachment: fixed;
        background-repeat: no-repeat;
        position: relative;
        min-height: 100vh;
    }
    
    /* Capa overlay para mejorar legibilidad */
    body::before {
        content: '';
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background: linear-gradient(135deg, rgba(248, 249, 250, 0.62) 0%, rgba(248, 249, 250, 0.85) 100%);
        z-index: -1;
    }
    
    .container {
        max-width: 1200px;
        margin: 0 auto;
        padding: 20px;
        position: relative;
        z-index: 1;
    }
    
    /* Header */
    header {
        text-align: center;
        padding: 40px 0;
        background: linear-gradient(135deg, rgba(44, 62, 80, 0.95), rgba(74, 100, 145, 0.95));
        color: white;
        border-radius: 10px;
        margin-bottom: 40px;
        box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
        backdrop-filter: blur(5px);
        border: 1px solid rgba(255, 255, 255, 0.1);
    }
    
    h1 {
        font-size: 2.5rem;
        margin-bottom: 10px;
        font-weight: 300;
    }
    
    .subtitle {
        font-size: 1.1rem;
        opacity: 0.9;
        max-width: 600px;
        margin: 0 auto;
    }
    
    /* Filtros */
    .filters {
        display: flex;
        justify-content: center;
        flex-wrap: wrap;
        gap: 10px;
        margin-bottom: 30px;
    }
    
    .filter-btn {
        padding: 10px 20px;
        background-color: rgba(255, 255, 255, 0.9);
        border: 1px solid rgba(221, 221, 221, 0.5);
        border-radius: 50px;
        cursor: pointer;
        transition: all 0.3s ease;
        font-weight: 500;
        backdrop-filter: blur(5px);
    }
    
    .filter-btn:hover {
        background-color: rgba(240, 240, 240, 0.95);
        transform: translateY(-2px);
        box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
    }
    
    .filter-btn.active {
        background-color: rgba(44, 62, 80, 0.95);
        color: white;
        border-color: rgba(44, 62, 80, 0.8);
    }
    
    /* Grid de certificados */
    .certificates-grid {
        display: grid;
        grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
        gap: 30px;
        margin-bottom: 40px;
    }
    
    .certificate-card {
        background-color: rgba(255, 255, 255, 0.95);
        border-radius: 10px;
        overflow: hidden;
        box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
        transition: all 0.3s ease;
        cursor: pointer;
        border: 1px solid rgba(255, 255, 255, 0.2);
        backdrop-filter: blur(10px);
    }
    
    .certificate-card:hover {
        transform: translateY(-5px);
        box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
        background-color: rgba(255, 255, 255, 0.98);
    }
    
    .certificate-img {
        height: 200px;
        background: linear-gradient(135deg, rgba(233, 236, 239, 0.9), rgba(206, 212, 218, 0.9));
        display: flex;
        align-items: center;
        justify-content: center;
        color: #4a6491;
        position: relative;
        overflow: hidden;
    }
    
    .certificate-img::before {
        content: '';
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background: linear-gradient(45deg, transparent 30%, rgba(255, 255, 255, 0.3) 50%, transparent 70%);
        animation: shine 3s infinite linear;
    }
    
    @keyframes shine {
        0% { transform: translateX(-100%); }
        100% { transform: translateX(100%); }
    }
    
    .certificate-img img {
        width: 100%;
        height: 100%;
        object-fit: cover;
        transition: transform 0.5s ease;
    }
    
    .certificate-card:hover .certificate-img img {
        transform: scale(1.05);
    }
    
    .certificate-img i {
        font-size: 4rem;
        z-index: 1;
        position: relative;
    }
    
    .certificate-info {
        padding: 20px;
    }
    
    .certificate-title {
        font-size: 1.2rem;
        font-weight: 600;
        margin-bottom: 8px;
        color: #2c3e50;
    }
    
    .certificate-issuer {
        color: #4a6491;
        font-weight: 500;
        margin-bottom: 10px;
        display: flex;
        align-items: center;
        gap: 5px;
    }
    
    .certificate-date {
        color: #6c757d;
        font-size: 0.9rem;
        margin-bottom: 15px;
        display: flex;
        align-items: center;
        gap: 5px;
    }
    
    .certificate-tags {
        display: flex;
        flex-wrap: wrap;
        gap: 5px;
    }
    
    .tag {
        background-color: rgba(233, 236, 239, 0.8);
        color: #495057;
        padding: 4px 10px;
        border-radius: 50px;
        font-size: 0.8rem;
        backdrop-filter: blur(5px);
    }
    
    /* Modal para vista detallada */
    .modal {
        display: none;
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background-color: rgba(0, 0, 0, 0.85);
        z-index: 1000;
        align-items: center;
        justify-content: center;
        padding: 20px;
        backdrop-filter: blur(5px);
    }
    
    .modal-content {
        background-color: rgba(255, 255, 255, 0.98);
        border-radius: 10px;
        max-width: 800px;
        width: 100%;
        max-height: 90vh;
        overflow-y: auto;
        position: relative;
        box-shadow: 0 15px 40px rgba(0, 0, 0, 0.3);
        border: 1px solid rgba(255, 255, 255, 0.2);
        backdrop-filter: blur(20px);
    }
    
    .close-modal {
        position: absolute;
        top: 15px;
        right: 20px;
        font-size: 1.8rem;
        color: #333;
        cursor: pointer;
        z-index: 10;
        background: rgba(255, 255, 255, 0.9);
        width: 40px;
        height: 40px;
        border-radius: 50%;
        display: flex;
        align-items: center;
        justify-content: center;
        transition: all 0.3s ease;
        border: 1px solid rgba(0, 0, 0, 0.1);
    }
    
    .close-modal:hover {
        background: rgba(44, 62, 80, 0.9);
        color: white;
        transform: rotate(90deg);
    }
    
    .modal-body {
        padding: 30px;
    }
    
    .modal-img {
        width: 100%;
        height: 300px;
        background: linear-gradient(135deg, rgba(233, 236, 239, 0.9), rgba(206, 212, 218, 0.9));
        display: flex;
        align-items: center;
        justify-content: center;
        margin-bottom: 20px;
        border-radius: 8px;
        overflow: hidden;
        position: relative;
    }
    
    .modal-img::before {
        content: '';
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background: linear-gradient(45deg, transparent 30%, rgba(255, 255, 255, 0.2) 50%, transparent 70%);
        animation: shine 3s infinite linear;
    }
    
    .modal-img img {
        width: 100%;
        height: 100%;
        object-fit: contain;
        z-index: 1;
        position: relative;
    }
    
    .modal-title {
        font-size: 1.8rem;
        color: #2c3e50;
        margin-bottom: 15px;
    }
    
    .modal-details {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
        gap: 20px;
        margin-bottom: 25px;
    }
    
    .detail-item {
        display: flex;
        flex-direction: column;
        padding: 15px;
        background-color: rgba(248, 249, 250, 0.7);
        border-radius: 8px;
        border: 1px solid rgba(0, 0, 0, 0.05);
    }
    
    .detail-label {
        font-weight: 600;
        color: #4a6491;
        margin-bottom: 5px;
        font-size: 0.9rem;
        text-transform: uppercase;
        letter-spacing: 0.5px;
    }
    
    .modal-description {
        line-height: 1.7;
        color: #555;
        padding: 20px;
        background-color: rgba(248, 249, 250, 0.5);
        border-radius: 8px;
        border-left: 4px solid #4a6491;
    }
    
    /* Footer */
    footer {
        text-align: center;
        padding: 20px 0;
        color: #6c757d;
        border-top: 1px solid rgba(233, 236, 239, 0.5);
        margin-top: 40px;
        background-color: rgba(255, 255, 255, 0.7);
        border-radius: 10px;
        backdrop-filter: blur(10px);
    }
    
    /* Responsive */
    @media (max-width: 768px) {
        .certificates-grid {
            grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
        }
    
        h1 {
            font-size: 2rem;
        }
    
        .modal-body {
            padding: 20px;
        }
        
        body {
            background-attachment: scroll;
        }
    }
    
    @media (max-width: 480px) {
        .certificates-grid {
            grid-template-columns: 1fr;
        }
    
        .filters {
            justify-content: flex-start;
            overflow-x: auto;
            padding-bottom: 10px;
        }
        
        .modal-img {
            height: 200px;
        }
    }
    
    /* Animación de carga suave */
    @keyframes fadeIn {
        from { opacity: 0; transform: translateY(20px); }
        to { opacity: 1; transform: translateY(0); }
    }
    
    .certificate-card {
        animation: fadeIn 0.5s ease forwards;
    }
    
    /* Desplazamiento suave */
    html {
        scroll-behavior: smooth;
    }
    
            
          

    Selecciona un archivo para ver su contenido

    Desde el panel principal, solo debes arrastrar la carpeta del proyecto (con los archivos ya mencionados) al área indicada como “Drag and drop your site project here”.

    Arrastrar sitio Arrastrar sitio

    Netlify se encargará automáticamente de subir los archivos y generar una URL pública para tu sitio.

    Sitio desplegado Sitio desplegado

    Y eso es todo. Nuestro sitio web ya está en internet, con HTTPS habilitado, y lo único que debes hacer ahora es compartir tu URL.

    En este caso, la dirección que nos proporciona Netlify es la siguiente:

    https://storied-cajeta-59d0b0.netlify.app

    Como puedes observar, se trata de una URL generada automáticamente, bastante larga y poco representativa de nuestro proyecto. Esto es totalmente normal en Netlify y no afecta en absoluto al funcionamiento del sitio.

    Cambiar el nombre del sitio en Netlify

    A continuación, veremos cómo personalizar el nombre del sitio para obtener una URL más limpia. En la pantalla anterior, presiona el botón de “Quick setup”.

    Configuración rápida Configuración rápida

    Una vez dentro de la configuración rápida, puedes elegir un nuevo nombre para actualizar la URL del sitio. Al presionar Update site name, Netlify verificará automáticamente la disponibilidad del nombre.

    Si el nombre elegido ya está en uso, la plataforma te lo notificará de inmediato:

    Nombre en uso Nombre en uso

    Una vez tengas un nombre válido, presiona el botón de “update project name”:

    Actualizar nombre del proyecto Actualizar nombre del proyecto

    Ahora contamos con una URL más amigable, disponible de forma inmediata para su acceso.

    Cambio de nombre realizado Cambio de nombre realizado

    https://mis-certificados.netlify.app/

    Con esto, hemos completado el despliegue manual en Netlify de forma exitosa. El sitio ya se encuentra disponible en línea y listo para ser accedido, lo que nos permite validar el resultado final antes de avanzar a configuraciones más avanzadas como dominios personalizados, variables de entorno o despliegues automatizados.

    Esta entrada está licenciada bajo CC BY 4.0 por el autor.