Tutorial WordPress: Crear badges de “Nuevo” o “Actualizado” con pseudo-elementos CSS

·

·

Introducción y objetivo

Este tutorial muestra, con todo lujo de detalles, cómo crear badges visibles de “Nuevo” y “Actualizado” en un sitio WordPress usando pseudo-elementos CSS. La idea es mantener el marcado HTML lo más limpio posible y delegar la presentación al CSS mediante ::before/::after, mientras que la lógica que determina si una entrada es nueva o ha sido actualizada se implementa en PHP (por ejemplo en functions.php). El resultado: badges ligeros, fáciles de mantener y compatibles con temas tradicionales y con muchos bloques.

Visión general del enfoque

Paso 1 — Añadir clases dinámicas en PHP (functions.php)

El siguiente snippet añade las clases is-new y is-updated al array de clases del post. Ajusta las variables new_days y updated_days según tu criterio (por ejemplo 7 días para “Nuevo”, 3-7 días para “Actualizado”).

lt?php
// functions.php
add_filter( post_class, mi_agregar_clases_nuevo_actualizado, 10, 3 )
function mi_agregar_clases_nuevo_actualizado( classes, class, post_id ) {
    post = get_post( post_id )
    if ( ! post ) {
        return classes
    }

    // Configuración: umbrales en días
    new_days     = 7 // considera Nuevo publicaciones de los últimos 7 días
    updated_days = 7 // considera Actualizado modificaciones en los últimos 7 días

    // Fechas en timestamp respetando zona horaria de WP
    now      = current_time( timestamp )
    published = get_post_time( U, true, post )
    modified  = get_post_modified_time( U, true, post )

    // Si la publicación es reciente
    if ( ( now - published ) <= ( new_days  DAY_IN_SECONDS ) ) {
        classes[] = is-new
    }

    // Si ha sido modificada y la modificación es reciente
    if ( modified > published  ( now - modified ) <= ( updated_days  DAY_IN_SECONDS ) ) {
        classes[] = is-updated
    }

    return classes
}
?gt

Notas:

Paso 2 — Marcado en las plantillas (loop / entry)

Lo ideal es no introducir elementos visibles extra sólo para el badge. No obstante, para accesibilidad añadiremos un texto escondido dentro del título para que lectores de pantalla anuncien “Nuevo” o “Actualizado”. Aquí un ejemplo de fragmento típico de plantilla (loop) que incluye un span con clase screen-reader-text. El span debe estar presente para que el contenido del pseudo-elemento no sea la única pista para lectores de pantalla.

ltarticle id=post-lt?php the_ID() ?gt 

Si tu tema ya tiene una clase para esconder texto a los lectores visuales (screen-reader-text), úsala. Si no, añade una regla CSS para ocultarla visualmente pero mantenerla accesible desde el lector de pantalla (ejemplo más abajo dentro de CSS).

Paso 3 — Estilos con pseudo-elementos CSS

Usaremos pseudo-elementos para no tocar el DOM visual. Asegúrate de que el contenedor del post tenga position: relative para poder posicionar el badge con position: absolute. A continuación un conjunto de estilos básicos y variantes:

/ Base: asegurar contexto de posicionamiento /
.article, .post, article {
  position: relative / si tu tema ya tiene layout distinto, apunta al selector correcto (por ejemplo .post, .entry) /
}

/ Badge Nuevo sobre el título (esquina superior derecha) /
.post.is-new .entry-title {
  position: relative / para que ::before se posicione respecto al título /
}

.post.is-new .entry-title::before {
  content: Nuevo
  position: absolute
  top: -0.5rem
  right: -0.5rem
  background: #e53935 / rojo /
  color: #fff
  font-weight: 700
  font-size: 0.65rem
  padding: 0.35rem 0.6rem
  border-radius: 0.25rem
  text-transform: uppercase
  box-shadow: 0 2px 6px rgba(0,0,0,0.12)
  transform: translateY(-50%)
  line-height: 1
  white-space: nowrap
  z-index: 5
}

/ Badge Actualizado con otro color /
.post.is-updated .entry-title::before {
  content: Actualizado
  position: absolute
  top: -0.5rem
  right: -0.5rem
  background: #f57c00 / naranja /
  color: #fff
  font-weight: 700
  font-size: 0.62rem
  padding: 0.3rem 0.6rem
  border-radius: 0.25rem
  text-transform: uppercase
  box-shadow: 0 2px 6px rgba(0,0,0,0.12)
  z-index: 5
}

/ Variante: badge sobre la miniatura/imagen destacada /
.post .post-thumbnail {
  position: relative
}

.post.is-new .post-thumbnail::after,
.post.is-updated .post-thumbnail::after {
  content: attr(data-badge) / si quieres contenido dinámico con data-, alternativa a content literal /
  position: absolute
  top: 0.5rem
  left: 0.5rem
  background: rgba(0,0,0,0.65)
  color: #fff
  padding: 0.35rem 0.5rem
  border-radius: 0.25rem
  font-size: 0.7rem
  font-weight: 700
  z-index: 6
}

/ Transiciones suaves (pero respetar preferencias de movimiento) /
.post .entry-title::before,
.post .post-thumbnail::after {
  transition: transform 0.18s ease, opacity 0.18s ease
}

/ Reducir tamaño en pantallas pequeñas /
@media (max-width: 480px) {
  .post.is-new .entry-title::before,
  .post.is-updated .entry-title::before {
    font-size: 0.55rem
    padding: 0.25rem 0.45rem
    top: -0.4rem
    right: -0.4rem
  }
}

/ Respetar usuarios con preferencia por reducir movimiento /
@media (prefers-reduced-motion: reduce) {
  .post .entry-title::before,
  .post .post-thumbnail::after {
    transition: none
  }
}

/ Ejemplo de .screen-reader-text (si tu tema no la define) /
.screen-reader-text {
  position: absolute
  width: 1px
  height: 1px
  padding: 0
  margin: -1px
  overflow: hidden
  clip: rect(0,0,0,0)
  white-space: nowrap
  border: 0
}

Notas sobre el CSS:

  • content: Nuevo / Actualizado es simple y directo. Si necesitas traducciones, genera la clase en PHP y usa reglas CSS con content en pseudo-elemento o bien incluye el texto accesible en el DOM (recomendado para i18n).
  • Si usas content: attr(data-badge), tendrás que generar el atributo data-badge en el markup del thumbnail desde PHP/plantilla.
  • Asegúrate de que los badges no solapen elementos importantes (prueba con distintos anchos y tamaños de imagen).

Variantes avanzadas y ejemplos

Badge con “ribbon” o triángulo

/ Ribbon simple - esquina superior derecha /
.post.is-new .entry-title::before {
  content: Nuevo
  position: absolute
  top: 0
  right: 0
  padding: 0.35rem 0.9rem
  background: #e53935
  color: #fff
  transform: rotate(10deg) translate(6px, -6px)
  box-shadow: 0 2px 6px rgba(0,0,0,0.12)
}

Usar data-attributes para contenido dinámico (ejemplo PHP CSS)

Si prefieres no codificar el texto en CSS (mejor para traducciones), añade un atributo data-badge desde PHP y usa content: attr(data-badge) en CSS.

lt?php
// En la plantilla, al output de la miniatura:
badge = 
if ( in_array( is-new, get_post_class( , null ) ) ) {
    badge = Nuevo
} elseif ( in_array( is-updated, get_post_class( , null ) ) ) {
    badge = Actualizado
}
?gt

ltdiv class=post-thumbnail

Compatibilidad con WooCommerce y otros post types

El mismo filtro post_class funciona para productos si el código de la plantilla usa post_class o product_class. Si WooCommerce no usa post_class en tu plantilla, añade la lógica en la plantilla de archivo de productos o usa hooks específicos del plugin para inyectar clases o atributos.

// Ejemplo: limitar a post_type product
add_filter( post_class, clases_producto_nuevo_actualizado, 10, 3 )
function clases_producto_nuevo_actualizado( classes, class, post_id ) {
    if ( product !== get_post_type( post_id ) ) {
        return classes
    }
    // ... lógica igual que antes ...
    return classes
}

Pruebas y depuración

  1. Crea entradas con fecha actual y fechas pasadas para comprobar el umbral de “Nuevo”.
  2. Modifica la entrada (cambia contenido y guarda) y comprueba el badge “Actualizado”.
  3. En entornos locales puedes forzar fechas con WP-CLI o editando la fecha de publicación/modificación desde el editor.
  4. Comprueba en móvil y en pantallas pequeñas que el badge no ocultE información importante.
  5. Valida con un lector de pantalla que el texto accesible (screen-reader-text) se anuncia correctamente.

Consideraciones de rendimiento y accesibilidad

  • Rendimiento: la lógica añade poca carga (simple cálculo de timestamps). Evita consultas adicionales por post dentro del loop.
  • Accesibilidad: los pseudo-elementos no son leídos por lectores de pantalla. Por eso la práctica recomendada es incluir una porción de texto accesible (ej. screen-reader-text) en el HTML que anuncie “Nuevo” o “Actualizado”.
  • SEO: los badges son puramente presentación no alteran el contenido principal ni el HTML visible importante.
  • Internacionalización: si tu sitio es multilenguaje, genera la cadena del badge en PHP usando funciones de traducción (esc_html__ / esc_attr__ / __) y pásala al data-attribute o imprime el span traducido.

Resumen práctico

  1. En functions.php: añade clases dinámicas (is-new / is-updated) con el filtro post_class basándote en las fechas.
  2. En plantillas: mantén el DOM limpio añade un pequeño span con clase de lector de pantalla para accesibilidad cuando sea necesario.
  3. En CSS: crea los badges con ::before / ::after, posicionándolos donde convenga (título, miniatura) y ofreciendo variantes responsivas.
  4. Prueba y ajusta umbrales y posición para que los badges no interfieran con la experiencia de lectura.

Ejemplo final — flujo mínimo completo

A continuación resume el flujo mínimo: PHP que añade clases, plantilla que muestra el título y thumbnail, y CSS que dibuja el badge.

/ 1) functions.php (añadir clases): ya mostrado anteriormente /
lt?php
// ... código de mi_agregar_clases_nuevo_actualizado ...
?gt

ltarticle id=post-lt?php the_ID() ?gt 
/ 3) CSS (ejemplo mínimo de estilos) /
.article, .post, article { position: relative }
.post .post-thumbnail { position: relative }

/ Badge desde data-badge /
.post .post-thumbnail::after {
  content: attr(data-badge)
  position: absolute
  top: 0.5rem
  left: 0.5rem
  background: #e53935
  color: #fff
  padding: 0.3rem 0.55rem
  border-radius: 0.25rem
  font-size: 0.7rem
  font-weight: 700
  z-index: 6
}

Con esto tienes una solución completa, accesible y personalizable para mostrar badges de “Nuevo” o “Actualizado” en WordPress usando pseudo-elementos CSS y una mínima lógica en PHP. Ajusta colores, posiciones y umbrales según la estética y necesidades de tu sitio.



Leave a Reply

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