FASE 1 · SESIÓN 2

CSS Moderno

De estilos básicos a layouts profesionales. Construirás cualquier interfaz sin frameworks, usando CSS nativo moderno.

⏱ 3 horas 🎨 Flexbox + Grid 📱 Responsive

AL TERMINAR ESTA SESIÓN VAS A SABER

Variables CSS: tu design system

Las custom properties son la base de cualquier design system profesional. Definen los valores fundamentales una vez y se reutilizan en todo el proyecto.

:root {
  /* COLORES */
  --color-bg:      #0d0d0d;
  --color-surface: #161616;
  --color-border:  #2a2a2a;
  --color-text:    #e8e8e8;
  --color-muted:   #888888;
  --color-accent:  #f0e040;
  --color-danger:  #ff6b6b;
  --color-success: #6bffb8;

  /* TIPOGRAFÍA */
  --font-body:  'Syne', sans-serif;
  --font-mono:  'DM Mono', monospace;
  --text-sm:    13px;
  --text-base:  15px;
  --text-lg:    18px;
  --text-xl:    24px;

  /* ESPACIADO */
  --space-xs:   4px;
  --space-sm:   8px;
  --space-md:   16px;
  --space-lg:   24px;
  --space-xl:   40px;
  --space-2xl:  64px;

  /* BORDES */
  --radius-sm:      2px;
  --radius-md:      4px;
  --border-default: 1px solid var(--color-border);
}

/* Uso: */
.card {
  background:    var(--color-surface);
  border:        var(--border-default);
  padding:       var(--space-lg);
  border-radius: var(--radius-md);
  font-size:     var(--text-base);
}

Las variables CSS se pueden sobreescribir en contexto. Definir --color-accent: red dentro de .seccion-roja {} cambia el acento para todos los elementos dentro de esa sección, sin tocar el :root.

Box model: todo elemento es una caja

Antes de Flexbox y Grid necesitas entender cómo CSS calcula el tamaño de cada elemento. Este es el error más común entre principiantes.

Las 4 capas del box model

margin
border
padding
content

Cada capa añade espacio alrededor del contenido. Margin es el espacio exterior (no afecta el fondo). Padding es interior (hereda el fondo del elemento). Border rodea el padding.

Por qué box-sizing: border-box

/* SIN box-sizing: border-box (por defecto) */
.caja {
  width:   300px;
  padding: 20px;     /* +40px */
  border:  2px solid;  /* +4px  */
  /* Ancho real: 300+40+4 = 344px ← SORPRESA */
}

/* CON box-sizing: border-box (lo correcto) */
* {
  box-sizing: border-box;
}

.caja {
  width:   300px;
  padding: 20px;
  border:  2px solid;
  /* Ancho real: SIEMPRE 300px ← predecible */
}

Siempre añade * { box-sizing: border-box; } como primera regla de tu CSS. Sin excepción.

Flexbox: alineamiento en un eje

Flexbox resuelve el alineamiento en una dimensión. Es tu herramienta para navbars, listas de cards, centrado vertical y distribución de elementos en fila o columna.

LOS DOS EJES DE FLEXBOX

item 1
item 2
item 3

→ eje principal (justify-content)

↕ eje cruzado (align-items)

justify-content

flex-start

elementos al inicio

flex-end

elementos al final

center

centrado

space-between

espacio entre

space-around

espacio alrededor

space-evenly

espacio idéntico

align-items:

flex-start center flex-end stretch baseline

Propiedades de los hijos (flex items)

.flex-item {
  flex-grow:   1;       /* cuánto crece (0 = no crece) */
  flex-shrink: 0;       /* 0 = no se encoge */
  flex-basis:  200px;  /* tamaño base */

  /* Shorthand: */
  flex: 1;          /* grow:1, shrink:1, basis:0 */
  flex: 0 0 200px;  /* tamaño fijo */

  align-self: flex-start; /* sobreescribe align-items del padre */
  order:      -1;         /* cambia el orden visual */
}

Navbar con Flexbox

.navbar {
  display:         flex;
  justify-content: space-between;
  align-items:     center;
  padding:         0 40px;
  height:          64px;
  border-bottom:   1px solid var(--color-border);
}

.nav-logo { flex-shrink: 0; }  /* no se encoge */

.nav-links {
  display:    flex;
  gap:        32px;
  list-style: none;
}

.nav-actions {
  display:     flex;
  gap:         12px;
  align-items: center;
}

CSS Grid: layouts bidimensionales

Grid controla filas Y columnas simultáneamente. Para el layout general de la página, dashboards y cualquier estructura matricial. Regla: Flexbox para componentes, Grid para layouts.

grid-template-columns — patrones esenciales

/* Columnas fijas */
grid-template-columns: 280px 1fr;

/* Tres columnas iguales */
grid-template-columns: repeat(3, 1fr);

/* Responsive sin media queries (el más útil) */
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));

/* Sidebar + contenido */
grid-template-columns: 240px 1fr;

/* 12-column grid */
grid-template-columns: repeat(12, 1fr);

grid-template-areas — layouts con nombre

.layout {
  display: grid;
  grid-template-areas:
    "header  header  header"
    "sidebar main    main"
    "footer  footer  footer";
  grid-template-columns: 240px 1fr;
  grid-template-rows:    64px 1fr 48px;
  min-height:           100vh;
}

.header  { grid-area: header;  }
.sidebar { grid-area: sidebar; }
.main    { grid-area: main;    }
.footer  { grid-area: footer;  }

Grid spanning — ocupar múltiples celdas

/* Ocupar múltiples columnas/filas */
.featured {
  grid-column: 1 / 3;    /* columnas 1 y 2 */
  grid-row:    1 / 3;    /* filas 1 y 2 */
}

/* Con span: */
.wide { grid-column: span 2; }
.tall { grid-row:    span 2; }

gap — espacio entre celdas

.grid {
  gap:        24px;        /* igual en filas y columnas */
  row-gap:    32px;        /* solo entre filas */
  column-gap: 16px;        /* solo entre columnas */
  gap:        32px 16px;  /* filas columnas */
}

/* place-items: shorthand de align-items + justify-items */
.grid-centered {
  place-items: center;
}

Responsive: una web para todos

El 60% del tráfico global es móvil. Tu web debe verse bien en 375px y en 1920px. Los breakpoints definen cuándo cambia el layout.

✓ MOBILE-FIRST (recomendado)

/* Base: móvil */
.grid {
  display:        flex;
  flex-direction: column;
  gap:            16px;
}

/* Tablet+ */
@media (min-width: 768px) {
  .grid {
    flex-direction: row;
    flex-wrap:      wrap;
  }
}

/* Desktop */
@media (min-width: 1200px) {
  .grid {
    display:               grid;
    grid-template-columns: repeat(3, 1fr);
  }
}

DESKTOP-FIRST (evitar)

Escribes para desktop y sobreescribes con max-width. Genera más código y es más difícil de mantener.

/* Base: desktop (evitar) */
.grid {
  display:               grid;
  grid-template-columns: repeat(3, 1fr);
}

/* Sobreescribir todo para móvil */
@media (max-width: 767px) {
  .grid {
    display:        flex;
    flex-direction: column;
    /* más código, más cascada */
  }
}
Nombre Min-width Dispositivos Uso
xs < 480px — Móvil pequeño Estilos base
sm 480px Móvil grande Primera expansión
md 768px Tablet 2 columnas
lg 1024px Laptop 3 columnas
xl 1280px Desktop Layout completo

El patrón moderno: auto-fit + minmax

Para grids de cards muchas veces no necesitas media queries:

/* 1 col en móvil, 2-3 en tablet, 3-4 en desktop — automático */
.cards {
  display:               grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap:                   20px;
}

Pseudo-clases y pseudo-elementos

Las pseudo-clases seleccionan elementos en estados específicos. Son la diferencia entre CSS estático y CSS que responde al usuario.

:hover

Mouse encima del elemento

.btn:hover { opacity: 0.85; }

:focus

Elemento recibe foco (teclado o clic)

input:focus { border-color: var(--accent); }

:active

Durante el clic (mientras se presiona)

.btn:active { transform: scale(0.98); }

:disabled

Elemento de formulario deshabilitado

button:disabled { opacity: 0.4; }

:checked

Checkbox o radio marcado

input:checked + label { color: var(--accent); }

:nth-child(n)

El n-ésimo hijo del padre

li:nth-child(odd) { background: var(--surface2); }

:first-child

El primer hijo de su padre

li:first-child { border-top: none; }

:last-child

El último hijo de su padre

li:last-child { border-bottom: none; }

:not(selector)

Todo elemento excepto el indicado

.card:not(.featured) { opacity: 0.8; }

:focus-visible

Foco de teclado (no de clic). Mejor accesibilidad que :focus

.btn:focus-visible { outline: 2px solid var(--accent); }

Pseudo-elementos

/* ::before y ::after — contenido generado */
.section-label::after {
  content:    '';
  flex:       1;
  height:     1px;
  background: var(--border);
}

.required::after {
  content: ' *';
  color:   var(--danger);
}

/* ::placeholder — estilo del placeholder */
input::placeholder { color: var(--muted); }

/* ::selection — texto seleccionado */
::selection {
  background: var(--accent);
  color:      var(--bg);
}

Movimiento: transiciones y @keyframes

El movimiento bien usado reduce la carga cognitiva. La regla: nunca más de 300ms para micro-interacciones, nunca movimiento sin propósito.

transition — cambios suaves de estado

.button {
  background: var(--surface);
  border:     1px solid var(--border);
  padding:    12px 24px;

  /* propiedad | duración | easing | delay */
  transition: background    0.2s ease,
              border-color 0.2s ease,
              transform    0.15s ease;
}

.button:hover {
  background:   var(--accent);
  border-color: var(--accent);
  color:        var(--bg);
  transform:    translateY(-2px);
}

/* Easings más usados: */
/* ease | ease-in | ease-out | ease-in-out | linear */

@keyframes — animaciones de ciclo

@keyframes fadeIn {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: translateY(0);   }
}

@keyframes pulse {
  0%, 100% { opacity: 1;   }
  50%       { opacity: 0.4; }
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

.card   { animation: fadeIn 0.4s ease-out     both;     }
.dot    { animation: pulse  2s   ease-in-out  infinite; }
.loader { animation: spin   1s   linear       infinite; }

animation — propiedades y stagger

.elemento {
  animation-name:            fadeIn;
  animation-duration:        0.4s;
  animation-timing-function: ease-out;
  animation-delay:           0s;
  animation-iteration-count: 1;      /* o infinite */
  animation-direction:       normal;  /* reverse | alternate */
  animation-fill-mode:        both;    /* forwards | backwards */

  /* Shorthand equivalente: */
  animation: fadeIn 0.4s ease-out 0s 1 normal both;
}

/* Stagger: retrasar animaciones en una lista */
.card:nth-child(1) { animation-delay: 0ms;   }
.card:nth-child(2) { animation-delay: 80ms;  }
.card:nth-child(3) { animation-delay: 160ms; }

/* Respetar preferencias de accesibilidad */
@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.01ms !important; }
}

BEM: nombres de clase que escalan

BEM (Block, Element, Modifier) es la metodología de naming más usada en equipos. Elimina los conflictos de especificidad y hace el CSS predecible.

BLOCK

Un componente independiente. La "cosa" en sí. Tiene sentido por sí mismo.

.card .navbar .button .modal .form

ELEMENT

Parte de un bloque. No tiene sentido fuera de él. Doble underscore __

.card__title .card__image .navbar__logo .button__icon .form__label

MODIFIER

Una variación del bloque o elemento. Doble guión --

.card--featured .button--primary .button--large .form--inline .navbar--dark

Ejemplo completo — componente card

<!-- HTML -->
<article class="card card--featured">
  <img class="card__image" src="..." alt="...">
  <div class="card__body">
    <h2 class="card__title">Título</h2>
    <p class="card__description">Resumen...</p>
    <a class="card__link card__link--primary">Leer más</a>
  </div>
</article>

/* CSS */
.card                { background: var(--surface); border: var(--border-default); }
.card--featured      { border-color: var(--accent); }
.card__image         { width: 100%; display: block; }
.card__body          { padding: 20px; }
.card__title         { font-size: 20px; font-weight: 700; }
.card__link          { display: inline-block; margin-top: 12px; }
.card__link--primary { background: var(--accent); color: var(--bg); }

Especificidad plana: todas las clases BEM tienen la misma especificidad (0,1,0). Nunca necesitas !important para sobreescribir. Con CSS anidado (.nav .list .item a) la especificidad crece y se vuelve imposible de mantener.

Ejercicio: dashboard responsive

Construirás el layout completo de un dashboard de métricas con solo CSS moderno. Grid para el layout, Flexbox para los componentes, variables para los colores, responsive sin frameworks.

2:00

Dashboard de métricas responsive

Header, sidebar, grid de KPI cards, tabla de datos. Responsive: sidebar colapsa en móvil.

01

Design tokens

Define todas las variables CSS en :root — colores, fuentes, espaciado, radios. Este paso determina si tu CSS escala.

15 min
02

Layout con Grid

Crea sidebar + área principal con grid-template-areas. Responsive: sidebar colapsa a display:none en móvil.

25 min
03

KPI cards auto-fit

Grid auto-fit minmax(200px, 1fr) para las cards. Hover con transform + transition. Estados: normal, positivo, negativo.

25 min
04

Componentes Flexbox

Flexbox para el header, para cada card, para las filas de tabla. :nth-child(even) para filas alternas.

25 min
05

Responsive y animaciones

Media queries mobile-first. fadeIn en las cards con @keyframes. Prueba en DevTools: iPhone SE, iPad, Desktop.

30 min

Recursos de esta sesión