Tema
Bootstrap
Bootstrap es el framework de CSS más usado de la historia: millones de sitios, paneles administrativos y sistemas internos están hechos con él. Te da un grid responsive y componentes listos (navbar, tarjetas, modales, formularios) con sólo añadir clases. En esta lección aprenderás a incluirlo, a dominar su grid de 12 columnas, sus utilidades y componentes, a personalizarlo para que no "se vea a Bootstrap", y a decidir cuándo usarlo.
Concepto
Bootstrap es un framework de componentes: en lugar de piezas pequeñas como Tailwind, te entrega piezas armadas. Escribes class="btn btn-primary" y obtienes un botón completo con colores, estados y accesibilidad.
Analogía
Si Tailwind es un juego de LEGO, Bootstrap es una tienda de muebles armados: eliges un sillón, una mesa y un librero, y en una tarde tienes la sala lista. El costo: tu sala se parece a la de mucha gente, salvo que la personalices.
Incluye tres cosas:
- Grid de 12 columnas, responsive.
- Componentes (CSS + JavaScript): navbar, cards, modal, dropdown, carousel, acordeón, toasts...
- Utilidades: clases de una sola propiedad para espaciado, colores, flex, texto, etc.
Instalación con CDN (Bootstrap 5.3)
La forma más rápida: un link en el head y un script al final del body.
html
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mi sitio con Bootstrap</title>
<!-- CSS de Bootstrap -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Tu CSS va DESPUÉS, para poder sobrescribir -->
<link href="css/estilos.css" rel="stylesheet">
</head>
<body>
<div class="container py-5">
<h1 class="display-5 fw-bold">¡Hola, Bootstrap!</h1>
<button class="btn btn-primary">Mi primer botón</button>
</div>
<!-- JS de Bootstrap (incluye Popper para dropdowns y tooltips) -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>Con npm (para personalizar con Sass): npm install bootstrap.
Buena práctica
Bootstrap 5 ya no necesita jQuery. Si ves tutoriales que lo incluyen, son de Bootstrap 4 o anteriores. Busca siempre documentación de la versión 5.3.
Contenedores
Todo layout empieza con un contenedor:
| Clase | Comportamiento |
|---|---|
.container | Ancho máximo que cambia por breakpoint (540, 720, 960, 1140, 1320px) y centrado |
.container-fluid | Siempre 100 % del ancho |
.container-md | 100 % hasta md, luego se comporta como .container |
Grid de 12 columnas
El grid de Bootstrap se basa en filas (.row) y columnas (.col-*). Cada fila se divide en 12 partes y tú decides cuántas ocupa cada columna.
txt
┌────────────────────────── .row (12 partes) ──────────────────────────┐
│ .col-12 ─────────────────────────────────────────────────────────── │
│ .col-6 ───────────────────────── .col-6 ────────────────────────── │
│ .col-4 ────────────── .col-4 ────────────── .col-4 ─────────────── │
│ .col-3 ────── .col-9 ──────────────────────────────────────────── │
└──────────────────────────────────────────────────────────────────────┘Breakpoints
| Infijo | Desde | Típico |
|---|---|---|
| (ninguno) | 0 | Móviles |
sm | 576px | Móviles grandes |
md | 768px | Tablets |
lg | 992px | Laptops |
xl | 1200px | Escritorio |
xxl | 1400px | Pantallas grandes |
Igual que en Responsive design, es mobile-first: col-md-6 significa "6 de 12 columnas desde 768px; antes, ancho completo".
html
<div class="container">
<!-- g-4: separación (gutter) entre columnas -->
<div class="row g-4">
<!-- Móvil: 12/12 (una por fila). Tablet: 6/12 (dos). Escritorio: 4/12 (tres) -->
<div class="col-12 col-md-6 col-lg-4">Diseño</div>
<div class="col-12 col-md-6 col-lg-4">Desarrollo</div>
<div class="col-12 col-md-6 col-lg-4">SEO</div>
</div>
</div>Otras herramientas del grid:
html
<!-- Columnas automáticas: se reparten el espacio por igual -->
<div class="row">
<div class="col">1</div>
<div class="col">2</div>
<div class="col">3</div>
</div>
<!-- Lateral fijo de 3 columnas + contenido de 9, desde lg -->
<div class="row">
<aside class="col-lg-3">Filtros</aside>
<main class="col-lg-9">Productos</main>
</div>
<!-- row-cols: define cuántas columnas por fila desde la fila -->
<div class="row row-cols-1 row-cols-sm-2 row-cols-lg-4 g-3">
<div class="col">...</div>
<div class="col">...</div>
</div>
<!-- Desplazar y reordenar -->
<div class="row">
<div class="col-md-6 offset-md-3">Centrado en tablet y escritorio</div>
<div class="col-md-6 order-md-first">Va primero desde md</div>
</div>Error común
Las columnas siempre deben ser hijas directas de un .row, y la .row debe estar dentro de un contenedor. Si pones contenido directamente en .row o anidas .col sin .row, los márgenes negativos del gutter generan scroll horizontal.
Utilidades
Bootstrap trae utilidades parecidas a las de Tailwind, con nombres más cortos:
html
<!-- Espaciado: m/p + lado (t, b, s=start, e=end, x, y) + tamaño 0-5 (0 a 3rem) -->
<div class="mt-4 px-3 py-5 mb-0">...</div>
<!-- Flexbox -->
<div class="d-flex justify-content-between align-items-center gap-3">...</div>
<!-- Responsive en utilidades: d-none d-md-block = oculto en móvil, visible desde md -->
<div class="d-none d-md-block">Sólo en tablet y escritorio</div>
<!-- Texto y color -->
<p class="text-center text-body-secondary fs-5 fw-semibold">...</p>
<div class="bg-primary text-white rounded-3 shadow">...</div>
<div class="bg-success-subtle text-success-emphasis border border-success-subtle">Éxito</div>
<!-- Tamaños -->
<img class="w-100 h-auto" src="..." alt="...">Componentes
Navbar responsive
html
<nav class="navbar navbar-expand-lg bg-body-tertiary sticky-top">
<div class="container">
<a class="navbar-brand fw-bold" href="#">Estudio Nova</a>
<!-- Botón hamburguesa: Bootstrap ya maneja aria-expanded por ti -->
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
data-bs-target="#menu" aria-controls="menu" aria-expanded="false" aria-label="Abrir menú">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="menu">
<ul class="navbar-nav ms-auto mb-2 mb-lg-0">
<li class="nav-item"><a class="nav-link active" aria-current="page" href="#">Inicio</a></li>
<li class="nav-item"><a class="nav-link" href="#servicios">Servicios</a></li>
<li class="nav-item"><a class="nav-link" href="#contacto">Contacto</a></li>
</ul>
<a class="btn btn-primary ms-lg-3" href="#contacto">Cotizar</a>
</div>
</div>
</nav>navbar-expand-lg indica desde qué breakpoint el menú deja de ser colapsable. El comportamiento del botón viene de los atributos data-bs-*: no escribes ni una línea de JavaScript.
Cards
html
<div class="card h-100 shadow-sm">
<img src="img/servicio.webp" class="card-img-top" alt="Diseño web">
<div class="card-body d-flex flex-column">
<h5 class="card-title">Diseño web</h5>
<p class="card-text text-body-secondary">Sitios rápidos, modernos y fáciles de administrar.</p>
<a href="#" class="btn btn-outline-primary mt-auto">Ver más</a>
</div>
</div>Botones
html
<button class="btn btn-primary">Primario</button>
<button class="btn btn-secondary">Secundario</button>
<button class="btn btn-success">Éxito</button>
<button class="btn btn-outline-danger">Peligro</button>
<button class="btn btn-primary btn-lg">Grande</button>
<button class="btn btn-link">Como enlace</button>Modal
html
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#modalCotizar">
Pedir cotización
</button>
<div class="modal fade" id="modalCotizar" tabindex="-1" aria-labelledby="modalCotizarTitulo" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title fs-5" id="modalCotizarTitulo">Pedir cotización</h2>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Cerrar"></button>
</div>
<div class="modal-body">Te respondemos en menos de 24 horas.</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cerrar</button>
<a href="#contacto" class="btn btn-primary">Ir al formulario</a>
</div>
</div>
</div>
</div>Formularios
html
<form class="row g-3 needs-validation" novalidate>
<div class="col-md-6">
<label for="nombre" class="form-label">Nombre</label>
<input type="text" class="form-control" id="nombre" required>
<div class="invalid-feedback">Escribe tu nombre.</div>
</div>
<div class="col-md-6">
<label for="correo" class="form-label">Correo</label>
<input type="email" class="form-control" id="correo" required>
<div class="invalid-feedback">Escribe un correo válido.</div>
</div>
<div class="col-12">
<label for="servicio" class="form-label">Servicio</label>
<select id="servicio" class="form-select">
<option>Diseño web</option>
<option>Tienda en línea</option>
</select>
</div>
<div class="col-12 form-check ms-2">
<input class="form-check-input" type="checkbox" id="acepto" required>
<label class="form-check-label" for="acepto">Acepto el aviso de privacidad</label>
</div>
<div class="col-12">
<button class="btn btn-primary" type="submit">Enviar</button>
</div>
</form>js
// Validación visual de Bootstrap: añade .was-validated al intentar enviar
document.querySelectorAll('.needs-validation').forEach((form) => {
form.addEventListener('submit', (e) => {
if (!form.checkValidity()) {
e.preventDefault()
e.stopPropagation()
}
form.classList.add('was-validated')
})
})Así se ve todo junto:
Personalización
El mayor reproche a Bootstrap es que "todos los sitios se ven iguales". Tienes dos niveles para evitarlo.
Nivel 1: variables CSS (sin compilar nada)
Bootstrap 5.3 expone sus valores como variables CSS con prefijo --bs-. Sobrescríbelas en tu propio archivo, cargado después de Bootstrap:
css
:root {
--bs-primary: #7c3aed;
--bs-primary-rgb: 124, 58, 237;
--bs-body-font-family: "Inter", system-ui, sans-serif;
--bs-border-radius: 0.75rem;
--bs-link-color-rgb: 124, 58, 237;
}
/* Los botones tienen sus propias variables locales */
.btn-primary {
--bs-btn-bg: #7c3aed;
--bs-btn-border-color: #7c3aed;
--bs-btn-hover-bg: #6d28d9;
--bs-btn-hover-border-color: #6d28d9;
--bs-btn-active-bg: #5b21b6;
}Bootstrap 5.3 también trae modo oscuro: añade data-bs-theme="dark" a html o a cualquier contenedor.
Nivel 2: Sass (la forma completa)
Con npm install bootstrap sass puedes cambiar las variables antes de compilar, y todo (botones, alertas, utilidades bg-primary, bordes) se regenera con tus valores. También puedes importar sólo los componentes que usas para reducir el peso:
scss
// scss/main.scss
// 1. Tus variables primero (Bootstrap usa !default, así que respeta las tuyas)
$primary: #7c3aed;
$font-family-base: "Inter", system-ui, sans-serif;
$border-radius: 0.75rem;
$enable-shadows: true;
// 2. Después, Bootstrap completo
@import "bootstrap/scss/bootstrap";
// 3. Tus estilos propios
.hero { padding-block: 6rem; }Sobre @import aquí
En la lección de Sass aprendiste que @import está obsoleto. Bootstrap 5.3 todavía se distribuye con esa sintaxis, así que su documentación oficial lo usa (verás avisos de deprecación al compilar; puedes silenciarlos con --quiet-deps). En tus propios archivos usa siempre @use.
¿Bootstrap, Tailwind o CSS propio?
| Situación | Mejor opción |
|---|---|
| Panel administrativo, sistema interno, prototipo rápido | Bootstrap: componentes listos con JS incluido |
| Equipo con backend fuerte y poco diseño | Bootstrap |
| Diseño a medida desde Figma, landing pages de marca | Tailwind o CSS propio |
| Proyecto con componentes (Vue, React, Astro) | Tailwind encaja muy bien |
| Sitio pequeño, máximo rendimiento, control total | CSS propio con variables y BEM |
| Proyecto heredado que ya usa uno | El que ya usa: no mezcles frameworks |
Buena práctica
Sea cual sea tu elección, nunca cargues dos frameworks a la vez (Bootstrap y Tailwind chocan en nombres como .container o .hidden). Y si usas Bootstrap, aprovecha sus componentes: si vas a sobrescribirlo casi todo, probablemente no lo necesitabas.
Ejercicios
- Crea una fila con 4 tarjetas: 1 por fila en móvil, 2 en tablet (
md) y 4 en escritorio (xl). - Haz un layout de blog: contenido de 8 columnas y barra lateral de 4 desde
lg; en móvil, la barra lateral debajo. - Cambia el color primario de Bootstrap a verde
#059669usando sólo variables CSS para los botones primarios. - Crea un botón que abra un modal con el título "Suscríbete" y un campo de correo.
Ver solución
html
<!-- 1 -->
<div class="container">
<div class="row g-4">
<div class="col-12 col-md-6 col-xl-3"><div class="card card-body">1</div></div>
<div class="col-12 col-md-6 col-xl-3"><div class="card card-body">2</div></div>
<div class="col-12 col-md-6 col-xl-3"><div class="card card-body">3</div></div>
<div class="col-12 col-md-6 col-xl-3"><div class="card card-body">4</div></div>
</div>
</div>
<!-- 2 -->
<div class="container">
<div class="row g-5">
<main class="col-lg-8">Artículo</main>
<aside class="col-lg-4">Barra lateral</aside>
</div>
</div>
<!-- 4 -->
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#suscribir">Suscribirme</button>
<div class="modal fade" id="suscribir" tabindex="-1" aria-labelledby="suscribirTitulo" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title fs-5" id="suscribirTitulo">Suscríbete</h2>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Cerrar"></button>
</div>
<div class="modal-body">
<label for="correo-sus" class="form-label">Correo</label>
<input type="email" id="correo-sus" class="form-control" placeholder="tu@correo.com">
</div>
<div class="modal-footer">
<button class="btn btn-primary">Enviar</button>
</div>
</div>
</div>
</div>css
/* 3 */
.btn-primary {
--bs-btn-bg: #059669;
--bs-btn-border-color: #059669;
--bs-btn-hover-bg: #047857;
--bs-btn-hover-border-color: #047857;
--bs-btn-active-bg: #065f46;
--bs-btn-active-border-color: #065f46;
}Resumen
- Bootstrap = grid + componentes con JS + utilidades. Inclúyelo con el CDN 5.3 (CSS +
bootstrap.bundle.min.js). - Grid:
.container→.row→.col-*; 12 columnas, mobile-first, breakpointssm md lg xl xxl. - Utilidades:
m-*,p-*,d-flex,d-none d-md-block,text-*,bg-*. - Componentes con
data-bs-*: navbar colapsable, modal, dropdown, sin escribir JS. - Personaliza con variables
--bs-*o con Sass para que no parezca "otro sitio Bootstrap". - Elige herramienta según el proyecto y nunca mezcles frameworks.
Siguiente: CSS moderno.