Introducción
Este tutorial explica paso a paso cómo crear en WordPress un layout para documentación por secciones con una navegación sticky (fija) lateral o superior. Cubriremos varias estrategias (ACF/repeater, parseo de encabezados en el contenido, y menú de anclas), el HTML/PHP necesario, el CSS para el layout y el comportamiento sticky, y el JavaScript para resaltar la sección activa (scrollspy) y navegación suave. También incluimos buenas prácticas de accesibilidad, rendimiento y responsive design.
Requisitos previos
- Instancia de WordPress accesible para modificar temas o crear un child theme.
- Conocimientos básicos de PHP, CSS y JavaScript.
- Opcional: el plugin ACF (Advanced Custom Fields) si prefieres un enfoque con campos repetibles.
Decidir la estrategia
Tres enfoques habituales, con sus pros y contras:
- ACF / Repeater: Control total sobre títulos y contenido de cada sección. Ideal si quieres estructura rígida y editor amigable.
- Parsear encabezados del contenido: Fácil de usar con el editor Gutenberg: el autor escribe H2/H3 en la página y el sistema genera la navegación automáticamente. Menos control fino pero muy cómodo.
- Menú de anclas manual: Creas un menú (wp_nav_menu) con enlaces a anclas. Útil si quieres que la navegación sea editabledesde Apariencia → Menús.
Recomendación
Si publicas documentación frecuente y quieres editar secciones como elementos separados, usar ACF Repeater o un Custom Post Type con relaciones es lo más escalable. Si prefieres simplicidad y que el autor use el editor, parsear encabezados es rápido y robusto.
Plantilla básica (estructura HTML/PHP)
Ejemplo de plantilla de página que genera una navegación lateral y un área de contenido con secciones. Aquí mostramos la variante con ACF Repeater (título y contenido por sección).
lt?php
/
Template Name: Documentación con navegación sticky
/
get_header()
?gt
ltmain class=doc-layoutgt
ltnav class=doc-nav aria-label=Índice de la documentacióngt
ltul class=doc-nav-listgt
lt?php if( have_rows(secciones) ): i=0
while( have_rows(secciones) ): the_row() i
titulo = get_sub_field(titulo)
slug = sanitize_title(titulo) . - . i
?gt
ltligtlta href=#lt?php echo esc_attr(slug) ?gtgtlt?php echo esc_html(titulo) ?gtlt/agtlt/ligt
lt?php endwhile endif ?gt
lt/ulgt
lt/navgt
ltsection class=doc-contentgt
lt?php if( have_rows(secciones) ): i=0
while( have_rows(secciones) ): the_row() i
titulo = get_sub_field(titulo)
contenido = get_sub_field(contenido)
slug = sanitize_title(titulo) . - . i
?gt
ltarticle id=lt?php echo esc_attr(slug) ?gt class=doc-section tabindex=-1gt
lth2gtlt?php echo esc_html(titulo) ?gtlt/h2gt
ltdiv class=doc-section-bodygtlt?php echo wp_kses_post(contenido) ?gtlt/divgt
lt/articlegt
lt?php endwhile endif ?gt
lt/sectiongt
lt/maingt
lt?php get_footer() ?gt
Notas sobre la plantilla
- Usamos IDs únicos por sección basados en el título y en el contador para evitar colisiones.
- El enlace en la navegación apunta a ese ID para saltar a la sección.
- tabindex=-1 en el article ayuda a manejar el foco para accesibilidad tras navegación por ancla.
Opción alternativa: parsear H2 del contenido
Si prefieres que el editor solo use encabezados H2/H3 y el sistema genere el índice automáticamente, aquí tienes una función que extrae H2 y genera el índice.
function doc_extract_headings( content ) {
headings = array()
if ( preg_match_all( /lth2.?gt(.?)lt/h2gt/i, content, matches ) ) {
i = 0
foreach ( matches[1] as text ) {
i
plain = wp_strip_all_tags( text )
slug = sanitize_title( plain ) . - . i
headings[] = array( text =gt plain, id =gt slug )
// Replace the H2 in content with an id for linking
content = preg_replace( /lth2(.?)gt.preg_quote(text).lt/h2gt/i,
lth2 id=.esc_attr(slug).1gt.text.lt/h2gt, content, 1 )
}
}
return array( content =gt content, headings =gt headings )
}
En el template cargarías post_content, pasarías por doc_extract_headings(), imprimirías la navegación usando el array headings y finalmente el contenido modificado.
CSS para layout y navegación sticky
A continuación un CSS base para un layout de dos columnas con navegación sticky en el lateral. Ajusta colores y tamaños a tu tema.
/ Contenedor principal /
.doc-layout{
display: grid
grid-template-columns: 300px 1fr
gap: 32px
align-items: start
padding: 24px
}
/ Navegación lateral /
.doc-nav{
position: relative
}
.doc-nav-list{
list-style: none
margin: 0
padding: 0
max-height: calc(100vh - 48px) / dejar espacio para encabezado fijo /
overflow: auto
position: sticky
top: 24px
border-left: 3px solid transparent
}
/ Enlaces /
.doc-nav-list a{
display: block
padding: 8px 12px
color: #1a1a1a
text-decoration: none
}
.doc-nav-list a:hover,
.doc-nav-list a:focus{
background: rgba(0,0,0,0.04)
outline: none
}
/ Enlace activo (sección visible) /
.doc-nav-list a.is-active{
font-weight: 700
color: #0066cc
border-left: 3px solid #0066cc
padding-left: 9px / compensar borde /
}
/ Contenido principal /
.doc-content{
min-width: 0
}
.doc-section{
margin-bottom: 48px
scroll-margin-top: 96px / compensar encabezado fijo si existe /
}
/ Responsive: en pantallas pequeñas la nav va arriba /
@media (max-width: 880px){
.doc-layout{
grid-template-columns: 1fr
}
.doc-nav{
order: -1
margin-bottom: 16px
}
.doc-nav-list{
position: static
max-height: none
}
}
JavaScript: scrollspy con IntersectionObserver y smooth scrolling
Este script detecta qué sección está visible y añade la clase is-active al enlace correspondiente. Usa IntersectionObserver para eficiencia y navegación suave al hacer clic en enlaces de la navegación.
document.addEventListener(DOMContentLoaded, function(){
const navLinks = document.querySelectorAll(.doc-nav-list a)
const sections = Array.from(navLinks).map(link =gt {
const id = link.getAttribute(href).replace(#,)
return document.getElementById(id)
}).filter(Boolean)
// Smooth scroll for nav links
navLinks.forEach(link =gt {
link.addEventListener(click, function(e){
e.preventDefault()
const targetId = this.getAttribute(href).slice(1)
const target = document.getElementById(targetId)
if(target){
target.focus({preventScroll: true})
window.scrollTo({
top: target.getBoundingClientRect().top window.scrollY - 80, // ajustar offset si cabezal fijo
behavior: smooth
})
// update URL without jumping
history.replaceState(null, , # targetId)
}
})
})
// IntersectionObserver para marcar el enlace activo
const observer = new IntersectionObserver((entries) =gt {
entries.forEach(entry =gt {
const id = entry.target.id
const link = document.querySelector(.doc-nav-list a[href=# id ])
if(entry.isIntersecting){
navLinks.forEach(l =gt l.classList.remove(is-active))
if(link) link.classList.add(is-active)
}
})
}, { root: null, rootMargin: -40% 0px -40% 0px, threshold: 0 })
sections.forEach(sec =gt observer.observe(sec))
// Fallback: en navegadores muy antiguos, podrías añadir un scroll listener si IntersectionObserver no existe.
})
Notas sobre el script
- rootMargin está configurado para que el área activa sea el centro de la pantalla ajústalo a tus necesidades.
- Actualizamos la URL con history.replaceState para permitir deep linking sin salto abrupto.
- El foco se mueve al artículo para accesibilidad, evitando que lectores de pantalla pierdan el contexto.
Encolar scripts y estilos en functions.php
Ejemplo de cómo añadir los assets al tema correctamente.
function doc_enqueue_assets(){
wp_enqueue_style( doc-layout, get_stylesheet_directory_uri() . /css/doc-layout.css, array(), 1.0 )
wp_enqueue_script( doc-scrollspy, get_stylesheet_directory_uri() . /js/doc-scrollspy.js, array(), 1.0, true )
}
add_action( wp_enqueue_scripts, doc_enqueue_assets )
Accesibilidad (a11y) y UX
- Usar roles y aria-labels: ltnav aria-label=Índice de la documentacióngt ayuda a usuarios de lectores.
- Permitir navegación por teclado: enlaces deben ser fácilmente tabulables y con :focus visible.
- Gestionar foco tras navegación por ancla: mover el foco al contenedor de la sección con tabindex=-1.
- Contrastes y tamaños legibles en la navegación, especialmente el indicador de sección activa.
- Considera añadir un botón Volver arriba o un índice colapsable en móvil.
Responsive y dispositivos móviles
En pantallas pequeñas es mejor mover la navegación arriba del contenido o convertirla en un panel colapsable (accordion/drawer). Evita position: sticky con top grande que provoque que el nav quede fuera de la pantalla en móviles.
Optimización y rendimiento
- Evita recalculos de layout costosos en scroll: IntersectionObserver es eficiente.
- Minifica y combina CSS/JS del layout si es posible y cachea los assets.
- Si tu documento tiene muchas imágenes, lazy-load para mejorar el rendimiento inicial.
Pruebas y depuración
- Verifica en distintos navegadores y tamaños de pantalla que la navegación no tape el contenido.
- Probar con lectura por teclado y con lectores de pantalla (NVDA, VoiceOver).
- Comprobar deep linking: al abrir una URL con #id el navegador debe posicionar correctamente la sección y el nav destacar el enlace.
Ejemplo completo mínimo (resumen)
Resumen rápido de los pasos para implementar en un sitio:
- Crear un template de página o página específica.
- Elegir cómo generar las secciones (ACF, parseo de H2 o menú manual).
- Generar IDs únicos para cada sección y enlaces que apunten a esas IDs.
- Estilizar el layout con CSS, usando position: sticky en la navegación.
- Añadir JS con IntersectionObserver para scrollspy y smooth scroll.
- Encolar assets correctamente y probar accesibilidad y responsive.
Consejos finales
- Si tu documentación crece mucho, considera convertir cada sección en un post o CPT y mostrar un índice con links a cada post (mejor SEO y navegación).
- Mantén una estructura consistente de encabezados (H2 para secciones principales y H3 para subsecciones) para mejor usabilidad.
- Documenta en el equipo cómo crear IDs o títulos para evitar duplicados y garantizar enlaces permanentes coherentes.
Ejemplo ACF: estructura de campos recomendada
Si usas ACF, crea un campo repeater llamado secciones con subcampos:
- titulo (Text)
- contenido (Wysiwyg o Flexible Content según preferencia)
- uid (opcional, para forzar un ID estable si renombráis títulos)
Checklist antes de publicar
- Enlaces de navegación apuntan a IDs válidos.
- La clase is-active se actualiza correctamente al hacer scroll.
- El offset de scroll considera cualquier header fijo del theme.
- Comprobación de accesibilidad y tests en móvil.
Conclusión
Crear un layout de documentación por secciones con navegación sticky en WordPress es una mejora de usabilidad potente y relativamente sencilla si se planifica bien. Elige la estrategia que encaje con tu flujo editorial (ACF para control, parseo de H2 para agilidad, menú manual para edición directa por administradores) y aplica los patrones mostrados: IDs únicos, position: sticky, IntersectionObserver para scrollspy y buenas prácticas de accesibilidad. Con esto tendrás una documentación clara, navegable y profesional lista para publicar.
Leave a Reply