Cómo corregir CLS en la cabecera de WooCommerce sin romper el sticky header

Cómo corregir CLS en la cabecera de WooCommerce sin romper el sticky header

¡Vamos a quitar esos saltos molestos! Aquí tienes un enfoque paso a paso para reducir el CLS en la cabecera de WooCommerce sin cargarte el sticky header. Vale para la mayoría de temas (Astra, GeneratePress, Blocksy, Kadence, Storefront, etc.); adapta los selectores a tu tema.

1) Reserva espacio fijo para la cabecera (estado inicial y sticky iguales)

El 90% del CLS en cabecera viene de alturas que cambian cuando carga el logo, las fuentes o la barra de avisos.

/* Altura base de cabecera en desktop */
.site-header,
header.site-header,
.header-wrap {
min-height: 84px; /* ajusta a tu diseño real */
}

/* Estado sticky: MISMA altura para evitar “encogerse” al fijarse */
.is-sticky .site-header,
.sticky-enabled header.site-header {
min-height: 84px; /* no lo reduzcas a 60px si quieres 0 CLS */
}

/* Evita que el sticky cambie el padding vertical */
.site-header .inner,
.is-sticky .site-header .inner {
padding-top: 12px;
padding-bottom: 12px;
}

Si tu diseño necesita una cabecera más compacta al hacer sticky, hazlo solo con transform/opacity (no cambiando alto/padding), o aplica la reducción después del primer scroll (ver punto 7).

2) Fija dimensiones del logo (sin depender del load de la imagen)

Define ancho/alto o aspect-ratio para que el layout no “espere” al logo.

/* Si conoces dimensiones reales del logo */
.custom-logo,
.site-logo img,
.header-logo img {
width: 160px; /* tu ancho real */
height: 40px; /* tu alto real */
object-fit: contain;
}

/* Alternativa moderna si el logo es flexible */
.header-logo img {
aspect-ratio: 4 / 1; /* ajusta a tu proporción real */
height: auto;
max-height: 40px; /* reserva consistente */
}

En el HTML del logo, añade también width y height (WordPress lo permite de serie si subes el logo a Medios).

3) Evita que el menú cambie de tamaño por la fuente

Cuando carga la webfont, las palabras del menú pueden ocupar más/menos espacio y empujar elementos.

a) Precarga y swap de la fuente usada en el menú:

<!-- en header.php o vía hooks: -->
<link rel="preload" href="/wp-content/themes/tu-tema/assets/fonts/Inter-Variable.woff2" as="font" type="font/woff2" crossorigin>
@font-face {
font-family: "Inter";
src: url("/wp-content/themes/tu-tema/assets/fonts/Inter-Variable.woff2") format("woff2");
font-display: swap; /* reduce FOIT y la “sacudida” */
}

b) Ajusta el fallback para que la métrica sea parecida:

.main-navigation a {
font-family: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
letter-spacing: .01em; /* micro-ajuste si la fallback “corre” */
}

4) Dimensiona iconos y elementos dinámicos

Carrito, buscador, hamburguesa, badges de cantidad… dales un cuadro fijo.

/* Iconos del header */
.header-icons .icon,
.header-cart .icon,
.menu-toggle .icon {
width: 24px;
height: 24px;
display: inline-block;
}

/* Contador del mini-carrito no debe empujar */
.header-cart .count {
min-width: 18px;
height: 18px;
line-height: 18px;
font-size: 11px;
text-align: center;
position: absolute;
top: -6px; right: -6px; /* superpuesto, no empuja layout */
}

5) WooCommerce notices (avisos) sin empujar la cabecera

Los avisos (cupones, “producto añadido”, etc.) a veces aparecen antes del header y empujan todo. Fíjalos debajo de la cabecera o dentro del flujo con altura reservada.

/* Coloca notices bajo la cabecera sin desplazarla */
.woocommerce-notices-wrapper {
position: relative;
min-height: 0; /* no reserves altura aquí si los haces overlay */
}
.site-notice-bar {
position: absolute; /* o fixed bajo el header */
top: 84px; /* la misma altura del header */
left: 0; right: 0;
z-index: 1000;
}

Alternativa simple: reserva un contenedor fijo para notices debajo del header con altura mínima conocida (p.ej., 40px) y muéstralos dentro sin variar la altura.

6) Admin bar (solo usuarios logueados)

El admin bar añade 32px arriba y puede engañarte en pruebas.

/* Evita que la prueba sintética cuente el admin bar */
@media screen and (min-width: 783px) {
body.admin-bar .site-header { top: 32px; }
body.admin-bar .is-sticky .site-header { top: 32px; }
}

Para medir CLS real de usuarios, prueba en ventana privada o con usuarios no logueados.

7) Sticky sin “encogerse” (si quieres animación, que no cambie layout)

Usa transform y opacity, no alto/padding.

/* Header fijo sin variar altura */
.site-header {
position: sticky; /* o fixed si tu tema lo requiere */
top: 0;
z-index: 999;
will-change: transform, opacity;
transition: transform .2s ease, opacity .2s ease;
}

/* Al hacer scroll, aplica una clase vía JS solo para animar sin cambio de layout */
.header--scrolled {
transform: translateY(0); /* si venía con -6px para efecto */
opacity: .98; /* efecto sutil sin tocar altura */
}
/* Importante: NO modifiques padding/top/border que cambien la caja */

JS mínimo (opcional) para añadir la clase sin tocar tamaños:

document.addEventListener('scroll', () => {
const h = document.querySelector('.site-header');
if (!h) return;
if (window.scrollY > 10) h.classList.add('header--scrolled');
else h.classList.remove('header--scrolled');
});

8) Prealoca el contenedor del mini-cart/buscador

Los paneles off-canvas no deben “empujar” el header al abrirse.

/* Botones con ancho fijo */
.header-cart, .header-search {
width: 40px; height: 40px;
}

/* Panel off-canvas fuera del flujo (no empuja) */
.mini-cart-panel, .search-panel {
position: fixed; /* o absolute sobre un overlay */
right: 0; top: 84px; /* debajo del header */
width: 360px; max-width: 100%;
height: calc(100dvh - 84px);
}

9) Evita CLS por imágenes/brand en retina

Si sirves logos 2x/3x, mantén las mismas dimensiones CSS siempre; no uses height:auto sin width fijo o aspect-ratio.

10) Chequeo rápido en Lighthouse / Web Vitals

  • Pasa Lighthouse en modo móvil.
  • Fíjate en “Elementos con cambio de layout” → te dirá qué selector mueve la cabecera.
  • Verifica en la pestaña Performance → Experience → Layout Shifts (en DevTools) para ver cuándo y qué se mueve.

Recetas rápidas por tema

Astra/GeneratePress/Kadence

  • Asegura min-height consistente en .main-header/.site-header.
  • Logo con width/height o aspect-ratio.
  • Menú con font-family precargada y font-display: swap.
  • Sticky: no cambies paddings entre normal y sticky; usa transform/opacity.

Storefront

  • Fija altura del .site-header y tamaño del .custom-logo-link img.
  • Woo notices: muévelos debajo del header o resérvales contenedor.

Snippet “todo en uno” (pégalo en Apariencia → Personalizar → CSS adicional)

/* Alturas consistentes */
:root { --header-h: 84px; }

.site-header,
header.site-header,
.header-wrap { min-height: var(--header-h); }

.is-sticky .site-header,
.sticky-enabled header.site-header { min-height: var(--header-h); }

.site-header .inner,
.is-sticky .site-header .inner { padding-block: 12px; }

/* Logo con dimensiones fijas */
.header-logo img, .custom-logo, .site-logo img {
width: 160px; height: 40px; object-fit: contain;
}

/* Menú sin saltos por fuentes */
.main-navigation a {
font-family: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
letter-spacing: .01em;
white-space: nowrap;
}

/* Iconos y contador */
.header-icons .icon, .header-cart .icon, .menu-toggle .icon {
width: 24px; height: 24px; display: inline-block;
}
.header-cart .count {
min-width: 18px; height: 18px; line-height: 18px; font-size: 11px;
position: absolute; top: -6px; right: -6px; text-align: center;
}

/* Sticky sin cambio de layout */
.site-header { position: sticky; top: 0; z-index: 999; transition: transform .2s, opacity .2s; }
.header--scrolled { opacity: .98; }

/* Woo notices sin empujar cabecera (opcional overlay) */
.site-notice-bar { position: absolute; top: var(--header-h); left: 0; right: 0; z-index: 1000; }

/* Admin bar solo en logged-in (no afecta usuarios reales) */
@media (min-width:783px){
body.admin-bar .site-header { top: 32px; }
}

Categoría: Plugin WooCommerce

Scroll al inicio