Product Craft Bible
Sticky Elements
Inicio Componentes UI Sticky Elements
Componentes UI

Sticky Elements

8 reglas Baymard Institute CTA Research 2023 · Luke Wroblewski "Mobile First" · ConversionXL Sticky Bar StudiesApple HIG Safe Áreas · Chrome Mobile UI Research 2022 · Luke Wroblewski "Mobile First" · Google Web.dev Layout ShiftsWCAG 2.2 Success Criterion 2.4.11 · MDN scroll-padding-top · Adrian Roselli "Sticky Headers and Focus" 2022 · WebAIM Keyboard AccessibilityMDN overflow:clip · Lea Verou CSS research 2021 · Stack Overflow "position:sticky not working" · CSS Tricks sticky positioning · Chrome DevTools sticky debugging
15

Sticky Elements

8 reglas
154

Sticky CTA solo si página tiene más de 2 scroll-heights

Una barra sticky de CTA solo añade valor cuando el usuario no puede ver el botón primario original sin hacer scroll. En páginas cortas donde el CTA inline siempre está visible, el sticky duplica la acción sin ningún beneficio, añade ruido visual y compite con el contenido. La regla práctica: si la página tiene menos de 2 viewports de altura, omitir el sticky. Por encima de ese umbral, la barra sticky reduce el tiempo hasta el primer clic en el CTA un promedio del 34% en landing pages B2C.

Baymard Institute CTA Research 2023 · Luke Wroblewski "Mobile First" · ConversionXL Sticky Bar Studies
Preferir
Plan Premium
Contratar ahora
Página corta, CTA inline visible
Contratar ahora
✗ Redundante
CTA duplicado sin valor.
Ambos visibles a la vez.
Plan Premium
más contenido ↓
Contratar ahora
✓ Útil
CTA inline oculto por scroll.
Sticky siempre accesible.
155

Budget mobile: máx 100px total de elementos sticky del autor

En mobile, el espacio vertical es el recurso más escaso. El OS consume ~28px con la barra de estado, el navegador añade 44–56px de chrome, y la barra de dirección puede aparecer o desaparecer. El contenido disponible para el usuario es el que queda. Cuando el autor añade una nav sticky arriba y un CTA sticky abajo, el presupuesto disponible para ambos elementos combinados no debe superar 100px, de lo contrario el contenido real queda comprimido a menos del 60% de la pantalla. Prioridad: orientación (nav) sobre conversión (CTA); si hay que elegir, el CTA sticky cede primero.

Apple HIG Safe Áreas · Chrome Mobile UI Research 2022 · Luke Wroblewski "Mobile First" · Google Web.dev Layout Shifts
Preferir
Status bar OS 28px
28px OS
Browser nav 50px
50px Browser
Sticky nav del sitio P1
Orientación, prioridad máxima
48px
48px Sitio ↑P1
Contenido disponible
Sticky CTA bottom P2
Conversión, cede si falta espacio
44px
44px CTA ↑P2
Total sticky del autor: 92px ✓ < 100px
156

WCAG 2.4.11: ningún elemento con foco oculto por sticky

El criterio WCAG 2.4.11 (Focus Not Obscured, nivel AA en WCAG 2.2) exige que cuando un componente recibe foco por teclado, al menos una parte del indicador de foco sea visible. Un sticky header que oculta el elemento con foco detrás de él viola este criterio directamente. La solución canónica es declarar scroll-padding-top en el elemento raíz con un valor igual o ligeramente mayor que la altura del sticky header. Esto hace que el navegador ajuste automáticamente la posición de scroll al navegar con Tab, dejando el elemento enfocado completamente visible debajo del header.

WCAG 2.2 Success Criterion 2.4.11 · MDN scroll-padding-top · Adrian Roselli "Sticky Headers and Focus" 2022 · WebAIM Keyboard Accessibility
Preferir
Mi Aplicación
scroll-padding-top
Botón con foco ← TAB
html { scroll-padding-top: 72px; }
Foco completamente visible
Cumple WCAG 2.4.11
Evitar
Mi Aplicación
Botón con foco ← TAB
⚠ Foco oculto
Siguiente acción
Focus oculto por sticky
Falla WCAG 2.4.11
157

overflow:hidden en ancestro rompe sticky; fix = overflow:clip

El valor overflow:hidden en cualquier ancestro del elemento sticky establece un nuevo bloque contenedor para el posicionamiento, lo que hace que position:sticky se ancle a ese contenedor en lugar del viewport. El elemento parece "sticky" dentro del div padre pero no del viewport, lo que es exactamente el comportamiento incorrecto en casi todos los casos de uso. La solución es reemplazar overflow:hidden con overflow:clip, que preserva el recorte visual sin crear un nuevo contexto de posicionamiento. Para casos en flex/grid donde el sticky falla aunque no haya overflow, la propiedad align-self:start es el fix correcto.

MDN overflow:clip · Lea Verou CSS research 2021 · Stack Overflow "position:sticky not working" · CSS Tricks sticky positioning · Chrome DevTools sticky debugging
Preferir
✗ Con overflow:hidden
<body>
<div overflow:
  hidden> ← problema
.sticky
position:sticky
Se pega al div, no al viewport
✓ Con overflow:clip
<body>
<div overflow:
  clip> ← fix
.sticky
position:sticky
Se pega al viewport correctamente
Fix alternativo, padre flex/grid con align-items:stretch
Cuando el sticky falla en un sidebar dentro de un layout flex o grid, el contenedor padre lo estira a toda la altura, eliminando el "espacio para scrollear" necesario.
/* Aplicar en el elemento sticky, no en el padre */ .sidebar { align-self: start; position: sticky; top: 80px; }
158

Z-index map fijo; nunca valores arbitrarios

Los valores de z-index sin documentar producen regresiones de apilamiento que son extraordinariamente difíciles de depurar. Cuando un desarrollador escribe z-index:9999 para "estar seguro", cualquier capa futura que deba superarlo requiere valores aún más arbitrarios. La solución es definir un z-index map como tokens de diseño: cada capa nombrada tiene un valor fijo, conocido y documentado. Los elementos sticky generalmente viven entre 70 y 150; los modales y tooltips por encima de 200. El mapa debe publicarse en el design system y referenciarse como variables CSS o constantes JS.

Shopify Polaris Z-Index Stack · Radix UI z-index documentation · Material Design 3 Elevation tokens · Tailwind CSS z-index scale
Preferir
Elemento
Z-index
Modal overlay
1200
Tooltip / Popover
200
Sticky nav global
150
Sticky toolbar
100
Sticky section header
90
Sticky CTA bar
80
Sticky table thead
70
159

flex/grid padre con align-items:stretch desactiva sticky; fix = align-self:start

El mecanismo de position:sticky requiere que el elemento tenga espacio para "deslizarse" dentro de su contenedor padre. Cuando el padre es un flex o grid container con el valor default align-items:stretch, el elemento sticky se estira para ocupar toda la altura del contenedor, eliminando ese espacio de deslizamiento. El resultado es un sticky que funciona solo al principio del scroll y luego queda "atrapado". La solución es aplicar align-self:start directamente en el elemento sticky (no en el padre), lo que le permite tener su altura natural y dejar espacio para scrollear.

CSS Working Group sticky spec · Lea Verou "CSS Secrets" · Ahmad Shadeed "position:sticky debugging" 2022 · MDN align-self
Preferir
Nav
Inicio
Proyectos
Config
✓ Sticky funciona align-self: start, sidebar solo ocupa su altura natural, queda espacio para scrollear
Evitar
Nav
Inicio
Proyectos
Config
✗ Sticky roto align-items: stretch, sidebar ocupa toda la altura del contenedor, sin espacio para deslizarse
160

Sticky section headers: solo si la lista tiene 5+ secciones y supera 3× el viewport

Los sticky section headers son un patrón de orientación para listas largas alfabéticas o categorizadas, como contactos, productos en catálogo, o transacciones financieras. En listas cortas el header se convierte en sticky, sale del viewport en menos de un scroll, y el usuario nunca lo aprovecha como indicador de posición. El costo cognitivo (más chrome, más complejidad de implementación) solo se justifica cuando la lista tiene suficiente densidad para que el usuario pierda orientación sin él, al menos 5 secciones con contenido sustancial y una longitud total superior a 3 veces la altura del viewport.

Apple iOS Contacts UX · Material Design 3 Lists · Baymard Institute List Navigation 2023 · Nielsen Norman Group "Alphabetical A-Z" 2019
Preferir
A, 12 contactos
A
Ana García
A
Arturo Méndez
A
Adriana Torres
+ 9 más en A...
B, 8 contactos
B
Beatriz López
+ 7 más en B...
C, 14 contactos
C
Carlos Ruiz
+ 13 más en C...
64 contactos, 5+ secciones
El header sticky orienta al usuario
mientras scrollea secciones largas.
Evitar
A
A
Ana García
A
Arturo Méndez
B
B
Beatriz López
C
C
Carlos Ruiz
C
Carmen Vega
D
D
Diego Soto
8 contactos
Sticky header sale en <1 scroll.
No aporta orientación.
161

will-change:transform solo tras medir jank; nunca preventivo

will-change:transform promueve el elemento a su propia capa de compositing en la GPU, lo que puede eliminar jank en animaciones de sticky. Sin embargo, aplicarlo preventivamente en todos los sticky elements tiene un costo real: consume memoria GPU por cada elemento promovido, puede disparar repaints adicionales y complica el stacking context. La regla es: medir primero con DevTools Performance panel durante un scroll real, verificar que los frames superen 16ms durante la interacción del sticky, y solo entonces aplicar will-change. Además, transform en el mismo elemento sticky invalida el stacking context y puede romper el posicionamiento, aplicarlo solo al sticky directamente, nunca a un transform simultáneo.

Chrome DevTools Performance Panel docs · Paul Irish "will-change" 2015 · MDN will-change · Addy Osmani "Rendering Performance" · CSS Triggers compositing
Preferir
Sticky implementado
¿Hay jank visible al scrollear?
No ↙
No hacer nada.
CSS sticky es suficiente.
✓ Sin overhead de GPU
Sí ↘
Medir con
DevTools Performance
durante el scroll
¿Frames >16ms durante sticky scroll?
No ↙
Otro problema:
investigar layout, paint
o JS bloqueante
Sí ↘
Aplicar en el sticky:
will-change:transform
en scroll start.
Remover en scroll end.
⚠ NUNCA aplicar transform y position:sticky al mismo tiempo en el mismo elemento, invalida el stacking context y puede romper el posicionamiento.
// Solo tras confirmar jank con DevTools:
window.addEventListener('scroll', () => {
  sticky.style.willChange = 'transform'
}, { passive: true })

// Limpiar al parar el scroll:
// (con debounce de ~150ms)
sticky.style.willChange = 'auto'