Bootstrap 5: crea un sitio web profesional sin pelearte con el CSS

Grid, componentes y formularios listos para usar. Montas una landing page decente en una tarde, con CDN, sin npm ni historias raras.

Llevas un rato con CSS. Sabes poner colores, ajustar márgenes, incluso has peleado con flexbox. El problema llega cuando intentas montar algo que se parezca a una web real: la navbar no queda bien en móvil, las cards no se alinean, el formulario parece de 2009. Acabas pasando más tiempo depurando CSS que construyendo la página.

Bootstrap existe exactamente para eso. Es una librería de componentes CSS ya hechos — navbar, grid, botones, formularios, modales — que funcionan desde el primer momento y se ven bien en cualquier pantalla. La idea no es que nunca más escribas CSS, sino que no tengas que reinventar la rueda cada vez que necesitas un layout básico.

Lo que vas a tener al terminar este tutorial

Una landing page funcional y responsive con navbar, sección hero, tarjetas de precios y formulario de contacto. Todo montado con Bootstrap 5 vía CDN, sin instalar nada en tu máquina.

Qué es Bootstrap y cuándo tiene sentido usarlo

Bootstrap es un framework CSS creado originalmente por Twitter. La versión 5, lanzada en 2021, eliminó la dependencia de jQuery y está construida en CSS moderno con variables nativas. Según el Stack Overflow Developer Survey 2024, sigue siendo el framework CSS más usado a nivel mundial, por encima de Tailwind y Material UI.

Tiene sentido usarlo cuando montas algo rápido — un portfolio, una landing page, un proyecto de prácticas — y no quieres invertir semanas en CSS vanilla. No tiene sentido cuando el diseño tiene que ser muy específico o cuando el equipo ya tiene un sistema de diseño propio. Para aprender CSS a fondo, Bootstrap puede ser un atajo que te quite comprensión; para construir cosas reales mientras aprendes, es una herramienta muy útil.

❌ Error habitual al empezar con Bootstrap

Mezclar clases de Bootstrap con CSS propio sin orden. Acabas con conflictos de estilos imposibles de depurar. La regla práctica: usa Bootstrap para estructura y layout, tu CSS para personalización visual. Nunca sobrescribas clases internas de Bootstrap directamente.

Instalación con npm: la forma correcta desde el principio

Hay tutoriales que te dicen que empieces con el CDN porque es más rápido. El problema es que eso te acostumbra a cargar código de servidores externos sin saber qué hay dentro. npm te da control real sobre lo que instalas, una versión fija que no cambia sin que tú lo decidas, y te enseña el flujo de trabajo que vas a usar en cualquier proyecto profesional.

Antes de instalar nada, una advertencia que no suele aparecer en los tutoriales de principiantes:

⚠️ Seguridad en npm: lo que nadie te cuenta al empezar

En los últimos años ha habido varios ataques de supply chain a través de npm — paquetes maliciosos que roban variables de entorno, credenciales o hacen minería de criptomonedas en tu máquina. Algunos se colaban con nombres casi idénticos a paquetes legítimos (typosquatting): boostrap en vez de bootstrap, recat en vez de react.

Tres hábitos que deberías tener desde el principio: primero, verifica siempre el nombre exacto del paquete en npmjs.com antes de instalarlo. Segundo, ejecuta npm audit después de instalar dependencias nuevas. Tercero, en Windows especialmente, ten la ejecución de scripts de PowerShell restringida (Get-ExecutionPolicy debería devolver RemoteSigned o Restricted, no Unrestricted) — algunos scripts de instalación maliciosos intentan aprovechar una política demasiado permisiva.

Con eso claro, la instalación:

# 1. Inicia el proyecto (crea el package.json)
npm init -y

# 2. Instala Bootstrap — verifica que el nombre es exactamente este
npm install bootstrap@5.3.3

# 3. Comprueba que no hay vulnerabilidades conocidas
npm audit

Esto crea una carpeta node_modules/bootstrap/ con todos los archivos. Ahora enlaza los que necesitas desde ahí en tu HTML:

<!-- En el <head>: CSS de Bootstrap desde node_modules -->
<link rel="stylesheet" href="./node_modules/bootstrap/dist/css/bootstrap.min.css">

<!-- Justo antes del </body>: JS de Bootstrap -->
<script src="./node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>

Por qué bootstrap.bundle.min.js y no bootstrap.min.js

La versión bundle incluye Popper.js, que Bootstrap necesita para posicionar dropdowns y tooltips. Si usas la versión sin bundle tendrás que añadir Popper por separado. Usa siempre el bundle para proyectos simples.

El node_modules no se sube a Git

Crea un archivo .gitignore en la raíz del proyecto con esta línea: node_modules/. Esa carpeta puede pesar cientos de megas y cualquiera que clone tu repo puede regenerarla con npm install. Si la subes estás haciendo el repositorio inmanejable y además exponiendo versiones exactas de dependencias que no necesita nadie más.

El grid de Bootstrap: cómo funciona de verdad

El grid es lo más importante de Bootstrap. Todo lo demás son componentes que puedes aprender cuando los necesites, pero sin entender el grid vas a estar perdido desde el primer momento.

Funciona con tres capas: container, row y col. El container centra el contenido y le pone márgenes laterales. La row crea una fila. Dentro de la row, las columnas se reparten el espacio.

<div class="container">
    <div class="row">
        <div class="col-md-6">Columna izquierda</div>
        <div class="col-md-6">Columna derecha</div>
    </div>
</div>

El número después del guion (6, en este caso) indica cuántas de las 12 columnas disponibles ocupa ese elemento. Dos columnas de 6 = 12 = una fila completa. Tres columnas de 4 = 12 = otra forma de llenar la fila.

El -md- es el breakpoint: esas columnas se aplican en pantallas medianas (768px) y mayores. En móvil, por defecto, cada columna ocupa el 100% del ancho y se apila verticalmente. Eso es el diseño responsive que Bootstrap te da sin escribir una sola media query.

❌ El error que me costó una hora

Las columnas deben ir siempre dentro de una row, y la row siempre dentro de un container. Si rompes esa jerarquía, el grid se descuadra de formas extrañas que son muy difíciles de depurar. Nunca pongas una col directamente dentro de un container sin la row en medio.

Breakpoints de Bootstrap 5

Bootstrap define seis breakpoints. Los más usados en el día a día son tres:

/* col-sm = 576px y superior */
/* col-md = 768px y superior  */
/* col-lg = 992px y superior  */

/* Ejemplo: 1 columna en móvil, 2 en tablet, 3 en desktop */
<div class="col-12 col-md-6 col-lg-4">...</div>

Puedes combinar varios breakpoints en la misma clase. El de la izquierda aplica primero (mobile-first), los de la derecha sobreescriben en pantallas más grandes.

Componentes que vas a usar desde el primer día

Navbar responsive

Una navbar que funciona en móvil con menú hamburguesa, sin escribir una línea de JavaScript:

<nav class="navbar navbar-expand-lg bg-dark navbar-dark">
    <div class="container">

        <a class="navbar-brand" href="#">MiSitio</a>

        <!-- Botón hamburguesa (solo visible en móvil) -->
        <button class="navbar-toggler"
                type="button"
                data-bs-toggle="collapse"
                data-bs-target="#navMenu">
            <span class="navbar-toggler-icon"></span>
        </button>

        <!-- Menú colapsable -->
        <div class="collapse navbar-collapse" id="navMenu">
            <ul class="navbar-nav ms-auto">
                <li class="nav-item">
                    <a class="nav-link active" href="#">Inicio</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="#">Proyectos</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="#contacto">Contacto</a>
                </li>
            </ul>
        </div>

    </div>
</nav>

El atributo data-bs-toggle="collapse" y data-bs-target="#navMenu" son los que conectan el botón hamburguesa con el menú. Por eso necesitas el JS de Bootstrap: esos data-bs-* son los que activa el script.

Cards

Las cards son el componente más versátil de Bootstrap. Sirven para mostrar proyectos en un portfolio, artículos en un blog, productos en una tienda:

<div class="card" style="width: 18rem;">
    <img src="proyecto.jpg" class="card-img-top" alt="Descripción del proyecto">
    <div class="card-body">
        <h5 class="card-title">Nombre del proyecto</h5>
        <p class="card-text">Descripción breve de lo que hace.</p>
        <a href="#" class="btn btn-primary">Ver proyecto</a>
    </div>
</div>

Para mostrar varias cards en una fila responsive, envuélvelas en el grid:

<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
    <div class="col">
        <div class="card h-100">...</div>
    </div>
    <div class="col">
        <div class="card h-100">...</div>
    </div>
</div>

row-cols-1 significa 1 columna en móvil, row-cols-md-2 significa 2 en tablet, row-cols-lg-3 significa 3 en desktop. El g-4 añade el hueco entre cards (gap). La clase h-100 en la card hace que todas tengan la misma altura aunque el contenido sea distinto — sin eso, la fila queda desnivelada y es muy feo.

Botones y variantes

Bootstrap tiene un sistema de colores semántico que funciona con casi todos los componentes. Los colores son: primary, secondary, success, danger, warning, info, light y dark.

<!-- Botones sólidos -->
<button class="btn btn-primary">Guardar</button>
<button class="btn btn-danger">Eliminar</button>
<button class="btn btn-success">Confirmar</button>

<!-- Botones solo con borde -->
<button class="btn btn-outline-primary">Ver más</button>

<!-- Tamaños -->
<button class="btn btn-primary btn-lg">Grande</button>
<button class="btn btn-primary btn-sm">Pequeño</button>

Formularios con Bootstrap

Los formularios son donde Bootstrap más ayuda. Sin él, conseguir que un input se vea bien en todos los navegadores y dispositivos es un trabajo ingrato. Con Bootstrap añades unas clases y listo:

<form>
    <div class="mb-3">
        <label for="nombre" class="form-label">Nombre</label>
        <input type="text"
               id="nombre"
               class="form-control"
               placeholder="Tu nombre">
    </div>

    <div class="mb-3">
        <label for="email" class="form-label">Email</label>
        <input type="email"
               id="email"
               class="form-control"
               placeholder="tu@email.com">
        <div class="form-text">No compartimos tu email con nadie.</div>
    </div>

    <div class="mb-3">
        <label for="mensaje" class="form-label">Mensaje</label>
        <textarea id="mensaje"
                  class="form-control"
                  rows="4"></textarea>
    </div>

    <button type="submit" class="btn btn-primary">Enviar</button>
</form>

La clase mb-3 añade un margen inferior entre campos. Es una clase de utilidad — Bootstrap tiene cientos de ellas para espaciado, tipografía, colores de fondo y demás. Una vez que entiendes la nomenclatura (m = margin, p = padding, b = bottom, el número de 0 a 5 es la cantidad), puedes componer layouts sin escribir una línea de CSS propio.

❌ Validación de formularios con Bootstrap

Bootstrap tiene clases para mostrar estados de validación (was-validated, is-valid, is-invalid), pero estas solo afectan al estilo visual. La validación real — comprobar que el email es un email, que el campo no está vacío — la tienes que hacer tú en JavaScript. No te confíes pensando que Bootstrap lo valida por ti.

Utilidades que vas a usar constantemente

Bootstrap incluye clases de utilidad para casi todo. Estas son las que aparecen en cualquier proyecto:

<!-- Texto -->
<p class="text-center">Centrado</p>
<p class="text-muted">Texto secundario en gris</p>
<p class="fw-bold">Negrita</p>

<!-- Espaciado (m=margin, p=padding, t=top, b=bottom, x=horizontal, y=vertical) -->
<div class="mt-4 mb-2 px-3">...</div>

<!-- Fondos y texto de color -->
<div class="bg-dark text-white p-4">Sección oscura</div>
<div class="bg-light p-3">Sección clara</div>

<!-- Display responsive -->
<div class="d-none d-md-block">Solo visible en tablet y desktop</div>
<div class="d-md-none">Solo visible en móvil</div>

Proyecto: landing page completa

Juntando todo lo anterior, aquí tienes una landing page funcional y responsive. Cópiala en un archivo index.html y ábrela en el navegador:

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Mi Portfolio</title>
    <link rel="stylesheet" href="./node_modules/bootstrap/dist/css/bootstrap.min.css">
    <style>
        .hero { background: #0d1117; color: white; padding: 80px 0; }
        .hero h1 { font-size: 2.5rem; font-weight: 700; }
        .seccion { padding: 60px 0; }
    </style>
</head>
<body>

<!-- NAVBAR -->
<nav class="navbar navbar-expand-lg bg-dark navbar-dark sticky-top">
    <div class="container">
        <a class="navbar-brand fw-bold" href="#">DevPortfolio</a>
        <button class="navbar-toggler" type="button"
                data-bs-toggle="collapse" data-bs-target="#nav">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="nav">
            <ul class="navbar-nav ms-auto">
                <li class="nav-item"><a class="nav-link" href="#proyectos">Proyectos</a></li>
                <li class="nav-item"><a class="nav-link" href="#contacto">Contacto</a></li>
            </ul>
        </div>
    </div>
</nav>

<!-- HERO -->
<section class="hero">
    <div class="container text-center">
        <h1>Hola, soy Ana García</h1>
        <p class="lead mt-3">Frontend developer en formación. Creo webs que funcionan.</p>
        <a href="#proyectos" class="btn btn-primary btn-lg mt-4">Ver proyectos</a>
    </div>
</section>

<!-- PROYECTOS -->
<section class="seccion" id="proyectos">
    <div class="container">
        <h2 class="mb-4">Proyectos</h2>
        <div class="row row-cols-1 row-cols-md-3 g-4">
            <div class="col">
                <div class="card h-100">
                    <div class="card-body">
                        <h5 class="card-title">Carrito de compra</h5>
                        <p class="card-text text-muted">JavaScript + localStorage</p>
                        <a href="#" class="btn btn-outline-primary btn-sm">Ver código</a>
                    </div>
                </div>
            </div>
            <div class="col">
                <div class="card h-100">
                    <div class="card-body">
                        <h5 class="card-title">API del tiempo</h5>
                        <p class="card-text text-muted">Fetch API + OpenWeather</p>
                        <a href="#" class="btn btn-outline-primary btn-sm">Ver código</a>
                    </div>
                </div>
            </div>
            <div class="col">
                <div class="card h-100">
                    <div class="card-body">
                        <h5 class="card-title">Todo list</h5>
                        <p class="card-text text-muted">JavaScript vanilla</p>
                        <a href="#" class="btn btn-outline-primary btn-sm">Ver código</a>
                    </div>
                </div>
            </div>
        </div>
    </div>
</section>

<!-- CONTACTO -->
<section class="seccion bg-light" id="contacto">
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-6">
                <h2 class="mb-4">Contacto</h2>
                <form>
                    <div class="mb-3">
                        <input type="text" class="form-control" placeholder="Tu nombre">
                    </div>
                    <div class="mb-3">
                        <input type="email" class="form-control" placeholder="tu@email.com">
                    </div>
                    <div class="mb-3">
                        <textarea class="form-control" rows="4" placeholder="Tu mensaje"></textarea>
                    </div>
                    <button type="submit" class="btn btn-primary w-100">Enviar</button>
                </form>
            </div>
        </div>
    </div>
</section>

<script src="./node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

Con ese código tienes navbar responsive con menú hamburguesa, hero centrado, grid de tres proyectos que se apila en móvil y formulario de contacto centrado. Todo con menos de 20 líneas de CSS propio.

Resultado final de la landing page con Bootstrap 5: navbar oscura, hero con texto centrado, grid de proyectos y formulario de contacto
Así queda la landing page completa con Bootstrap 5 — navbar, hero, cards y formulario.

Personalizar los colores de Bootstrap

Bootstrap 5 usa variables CSS nativas, lo que hace la personalización mucho más limpia que en versiones anteriores. Para cambiar el color primario sin tocar los archivos de Bootstrap, añade esto en tu CSS después del link del CDN:

:root {
    /* Sobreescribir el azul de Bootstrap por el color de tu marca */
    --bs-primary: #e63946;
    --bs-primary-rgb: 230, 57, 70;
}

Ojo: necesitas sobreescribir tanto --bs-primary como --bs-primary-rgb porque Bootstrap usa la versión RGB para las transparencias y gradientes. Si solo cambias una, verás que algunos componentes quedan con el color incorrecto. Este detalle me costó un buen rato pillarlo la primera vez.

Por dónde seguir

Con lo que has visto aquí tienes suficiente para montar un portfolio o una landing page decente. El siguiente paso práctico es añadir interactividad real al formulario de contacto: que al enviarlo haga una llamada a una API en lugar de recargar la página. Para eso necesitas combinar Bootstrap con Fetch API.

Si quieres profundizar en Bootstrap, la documentación oficial está muy bien organizada. Cada componente tiene ejemplos de código que puedes copiar directamente. La forma más eficiente de aprenderlo es tener la doc abierta en una pestaña y construir algo real en paralelo — no leer la doc de cabo a rabo sin hacer nada.

Siguiente lectura recomendada

El formulario de contacto de tu portfolio necesita hacer algo cuando el usuario lo envía. Aquí tienes cómo conectarlo a una API sin recargar la página:

👉 JavaScript Fetch API: tu primera llamada HTTP