Tutorial WordPress: Personalizar el paginador numérico con CSS y estados activos

·

·

Introducción

En este artículo encontrarás un tutorial completo para personalizar el paginador numérico de WordPress usando CSS y gestionando correctamente los estados activos, hover, foco y deshabilitado. Verás desde cómo generar la estructura HTML desde PHP en tu tema hasta ejemplos de CSS moderno (con variables, transiciones y accesibilidad). Todo listo para aplicar en tu tema hijo o plugin.

Qué vamos a conseguir

1. Generar la estructura de paginación desde PHP

WordPress ofrece funciones como paginate_links() o get_the_posts_pagination(). paginate_links() con type => array es muy cómoda porque devuelve cada enlace por separado, incluyendo el elemento current (actual) y los puntos suspensivos. A continuación tienes un ejemplo completo que convierte la salida en una lista ul/li con clases manejables desde CSS.

lt?php
// Coloca este código en el archivo de tu loop (archive.php, index.php) o en una plantilla parcial.
// Asegúrate de usarlo donde haya un query paginado (main loop).
links = paginate_links( array(
    base      =gt str_replace( 999999999, %#%, esc_url( get_pagenum_link( 999999999 ) ) ),
    format    =gt ?paged=%#%,
    current   =gt max( 1, get_query_var( paged ) ),
    total     =gt wp_query-gtmax_num_pages,
    prev_text =gt laquo,
    next_text =gt raquo,
    type      =gt array,
    mid_size  =gt 1,
) )

if ( is_array( links ) ) :
    echo ltnav class=custom-pagination role=navigation aria-label=Paginacióngt
    echo ltul class=paginationgt
    foreach ( links as link ) {
        // Paginate_links devuelve:
        // - anchor lta class=page-numbers href=...gtNlt/agt
        // - span ltspan class=page-numbers currentgtNlt/spangt
        // - span ltspan class=page-numbers dotsgt...lt/spangt
        if ( strpos( link, current ) !== false ) {
            // Página actual: convierte el span en un elemento no clickable con clase page-link current
            echo ltli class=page-itemgt . str_replace( page-numbers current, page-link current, link ) . lt/ligt
        } elseif ( strpos( link, dots ) !== false ) {
            // Puntos suspensivos: mantén un span con clase page-link dots y aria-hidden
            dots = str_replace( page-numbers dots, page-link dots, link )
            // Añadir aria-hidden para que no sea leído como control
            dots = str_replace( ltspan, ltspan aria-hidden=true, dots )
            echo ltli class=page-item dotsgt . dots . lt/ligt
        } else {
            // Enlaces normales: añade clase page-link a los anchors
            echo ltli class=page-itemgt . str_replace( page-numbers, page-link, link ) . lt/ligt
        }
    }
    echo lt/ulgt
    echo lt/navgt
endif
?gt

Con este código obtendrás HTML coherente como:

  1. ltnav class=custom-pagination role=navigationgt
  2. ltul class=paginationgt
  3. ltli class=page-itemgtlta class=page-link href=…gt1lt/agtlt/ligt …
  4. ltli class=page-itemgtltspan class=page-link currentgt2lt/spangtlt/ligt
  5. ltli class=page-item dotsgtltspan class=page-link dots aria-hidden=truegt…lt/spangtlt/ligt

2. Estilos CSS detallados

A continuación tienes una hoja de estilos CSS completa y comentada. Usa variables CSS para integrar fácilmente con el resto del tema y modificar colores/espaciados. Añádela a tu style.css del tema hijo o a un archivo CSS encolado por functions.php.

/ Variables globales para fácil personalización /
:root {
  --pag-bg: transparent
  --pag-border: #e0e0e0
  --pag-radius: 6px
  --pag-color: #333
  --pag-accent: #0073aa / color principal (activo) /
  --pag-accent-contrast: #ffffff
  --pag-hover: #f5faff
  --pag-disabled: #cfcfcf
  --pag-gap: 8px
  --pag-font-size: 0.95rem
  --pag-padding: 8px 12px
}

/ Contenedor /
.custom-pagination {
  margin: 28px 0
  text-align: center
}

/ Lista horizontal /
.custom-pagination .pagination {
  display: inline-flex
  flex-wrap: wrap
  list-style: none
  padding: 0
  margin: 0
  gap: var(--pag-gap)
  align-items: center
}

/ Elemento de lista (no afecta estado) /
.custom-pagination .page-item {
  display: inline-flex
}

/ Enlaces y spans: botón base /
.custom-pagination .page-link {
  display: inline-flex
  align-items: center
  justify-content: center
  min-width: 40px
  padding: var(--pag-padding)
  font-size: var(--pag-font-size)
  color: var(--pag-color)
  background: var(--pag-bg)
  border: 1px solid var(--pag-border)
  border-radius: var(--pag-radius)
  text-decoration: none
  transition: transform 0.12s ease, box-shadow 0.12s ease, background-color 0.12s ease
  cursor: pointer
}

/ Hover y focus: mejora la interacción /
.custom-pagination .page-link:hover,
.custom-pagination .page-link:focus {
  background: var(--pag-hover)
  transform: translateY(-2px)
  box-shadow: 0 4px 12px rgba(16,24,40,0.06)
}

/ Focus visible (mejor accesibilidad) /
.custom-pagination .page-link:focus-visible {
  outline: 3px solid rgba(0,115,170,0.18)
  outline-offset: 2px
}

/ Estado activo (página actual) /
.custom-pagination .page-link.current {
  background: var(--pag-accent)
  color: var(--pag-accent-contrast)
  border-color: transparent
  transform: none
  cursor: default
  box-shadow: 0 6px 18px rgba(0,115,170,0.12)
  font-weight: 600
}

/ Prev/Next estilizados si son links /
.custom-pagination .page-item:first-child .page-link,
.custom-pagination .page-item:last-child .page-link {
  padding-left: 10px
  padding-right: 10px
  min-width: auto
}

/ Puntos suspensivos: estilo neutro y sin interacción /
.custom-pagination .page-item.dots .page-link {
  cursor: default
  background: transparent
  border: none
  color: var(--pag-disabled)
  transform: none
  box-shadow: none
  font-weight: 500
}

/ Estado disabled si lo necesitas (por ejemplo prev/next en extremos) /
.custom-pagination .page-link[aria-disabled=true],
.custom-pagination .page-link.disabled {
  opacity: 0.6
  pointer-events: none
  color: var(--pag-disabled)
  background: transparent
  border-color: var(--pag-border)
}

/ Responsive: botones más pequeños en pantallas estrechas /
@media (max-width: 480px) {
  .custom-pagination .page-link {
    padding: 6px 8px
    min-width: 34px
    font-size: 0.88rem
  }
  .custom-pagination .pagination {
    gap: 6px
  }
}

Notas sobre el CSS

3. Mejora de accesibilidad y ARIA

La estructura nav ul/li con un label aria-label ya proporciona contexto. Algunas prácticas recomendadas adicionales:

// Ejemplo: añadir aria-current al span current (si quieres)
/ ... dentro del loop que procesa links ... /
if ( strpos( link, current ) !== false ) {
    link = str_replace( page-numbers current, page-link current, link )
    // Añadir aria-current si el elemento es un span:
    link = str_replace( ltspan, ltspan aria-current=page, link )
    echo ltli class=page-itemgt . link . lt/ligt
}

4. Manejo de prev/next deshabilitado

paginate_links puede devolver prev/next incluso cuando no hay página anterior o siguiente. En ese caso, detecta la ausencia y marca el botón como deshabilitado para que el lector de pantalla lo entienda y el CSS lo oculte de la interacción:

// Ejemplo rápido para marcar prev/next como deshabilitados
global wp_query
current = max( 1, get_query_var( paged ) )
total   = wp_query-gtmax_num_pages

prev_disabled = ( current lt= 1 )
next_disabled = ( current gt= total )

// Al imprimir botones prev/next manualmente:
if ( prev_disabled ) {
    echo ltli class=page-itemgtltspan class=page-link aria-disabled=truegtlaquolt/spangtlt/ligt
} else {
    echo ltli class=page-itemgtlta class=page-link href= . get_pagenum_link( current - 1 ) . gtlaquolt/agtlt/ligt
}

5. Integración con get_the_posts_pagination()

Si prefieres usar get_the_posts_pagination(), puedes usar su parámetro type => list que ya devuelve una lista ul. Después solo aplica CSS adaptando selectores. Ejemplo rápido:

echo get_the_posts_pagination( array(
    mid_size  =gt 1,
    prev_text =gt laquo,
    next_text =gt raquo,
    screen_reader_text =gt  ,
    type      =gt list,
) )

Nota: la salida de get_the_posts_pagination puede incluir clases distintas inspecciónala y ajusta el CSS o usa selectores más concretos (p.ej. .custom-pagination .pagination a).

6. Variantes estéticas y micro-interacciones

Algunas ideas para personalización avanzada:

7. Rendimiento y caché

La paginación en sí no suele causar problemas de rendimiento, pero si tu loop es pesado:

8. Pruebas y comprobaciones

Antes de publicar en producción:

  1. Prueba con distintos tamaños de contenido y páginas (1, 2, muchas páginas) para comprobar el comportamiento de dots y prev/next.
  2. Verifica con teclado (tab) que el foco llega a enlaces y que :focus-visible se aplica.
  3. Prueba en móviles para asegurarte que el tamaño mínimo de toque es cómodo (al menos 40px recomendado).
  4. Comprueba con lector de pantalla que la navegación se anuncia correctamente (nav con aria-label y aria-current en página actual).

9. Resumen rápido de pasos a seguir

  1. Decide si usas paginate_links() o get_the_posts_pagination().
  2. Genera la estructura semántica nav gt ul gt li con clases coherentes.
  3. Añade CSS moderno (variables, transiciones, focus-visible, responsive).
  4. Maneja puntos suspensivos y estados disabled para prev/next.
  5. Comprueba accesibilidad, pruebas en dispositivos y performance.

Ejemplo final: integración mínima

Implementa el PHP del apartado 1 y pega el CSS en style.css. Cambia las variables :root para adecuarlas al diseño. Con eso tendrás un paginador numérico estilizado, accesible y con estados activos bien definidos.

Fin del tutorial — listo para aplicar

Aplica estos fragmentos directamente en tu tema hijo: el PHP en la plantilla correspondiente y el CSS en style.css o en un archivo encolado. El resultado será un paginador numérico profesional, con estados activos, hover y focus adecuados para una buena experiencia y accesibilidad.



Leave a Reply

Your email address will not be published. Required fields are marked *