Crear un grid de categorías con imagen y overlay de color en hover — Tutorial completo para WordPress
Este tutorial explica paso a paso cómo añadir imágenes a las categorías en WordPress, mostrar un grid responsivo de categorías con imagen de portada y aplicar un overlay de color en hover (con transición suave). Incluye el código necesario para el panel de administración (subida de imagen), el guardado del meta dato, la plantilla para mostrar el grid y el CSS para el overlay. También cubre accesibilidad, rendimiento y una alternativa usando ACF.
Resumen de funcionamiento
- Registrar y mostrar un campo de imagen en las pantallas de añadir/editar categoría.
- Guardar la ID del adjunto en term meta usando update_term_meta.
- Crear una plantilla que recupere la imagen de cada categoría y genere un grid HTML.
- Aplicar CSS para el grid, la imagen y un overlay con color personalizable por categoría y transición al pasar el ratón o al recibir foco.
Requisitos
- Tema hijo o plugin donde pegar el código (no modificar el tema padre directamente).
- WordPress 4.4 (para get_term_meta / update_term_meta).
- Conocimientos básicos de PHP, CSS y cómo editar functions.php o crear un plugin simple.
1) Añadir campo de imagen al formulario de categoría
El siguiente fragmento añade el input y botones en las pantallas de añadir y editar categoría en el admin. Se usa la librería de medios de WP para seleccionar la imagen.
/ functions.php o plugin /
add_action(category_add_form_fields, mi_cat_add_image_field, 10, 2)
add_action(category_edit_form_fields, mi_cat_edit_image_field, 10, 2)
function mi_cat_add_image_field(taxonomy) {
?>
term_id, category_image_id, true)
image_url = image_id ? wp_get_attachment_image_url(image_id, medium) :
?>
>
?>
2) Guardar la meta (ID del adjunto)
Usa los hooks de creación y edición para almacenar la ID del adjunto en term meta.
add_action(created_category, mi_save_category_image, 10, 2)
add_action(edited_category, mi_save_category_image, 10, 2)
function mi_save_category_image(term_id) {
if (isset(_POST[category_image_id])) {
image_id = intval(_POST[category_image_id])
if (image_id) {
update_term_meta(term_id, category_image_id, image_id)
} else {
delete_term_meta(term_id, category_image_id)
}
}
}
3) Encolar scripts para la librería de medios y JS para el selector
Encola wp-media y añade un script que abrirá el modal y actualizará el input oculto y la previsualización.
add_action(admin_enqueue_scripts, mi_admin_category_scripts)
function mi_admin_category_scripts(hook) {
screen = get_current_screen()
if (screen screen->taxonomy === category) {
wp_enqueue_media()
wp_enqueue_script(mi-category-media, get_stylesheet_directory_uri() . /js/mi-category-media.js, array(jquery), 1.0, true)
}
}
// archivo: js/mi-category-media.js
jQuery(document).ready(function(){
var file_frame
function setPreview(imgSrc, input) {
var wrap = input.closest(tr, .form-field).find(.mi-image-preview)
wrap.html(
)
input.closest(tr, .form-field).find(.mi-remove-btn).show()
}
(document).on(click, .mi-upload-btn, function(e){
e.preventDefault()
var button = (this)
var container = button.closest(tr, .form-field)
var input = container.find(#category-image-id)
if (file_frame) file_frame.close()
file_frame = wp.media.frames.file_frame = wp.media({
title: Selecciona imagen,
button: { text: Usar esta imagen },
multiple: false
})
file_frame.on(select, function(){
var attachment = file_frame.state().get(selection).first().toJSON()
input.val(attachment.id)
setPreview(attachment.url, input)
})
file_frame.open()
})
(document).on(click, .mi-remove-btn, function(e){
e.preventDefault()
var button = (this)
var container = button.closest(tr, .form-field)
var input = container.find(#category-image-id)
input.val()
container.find(.mi-image-preview).html()
button.hide()
})
})
4) Plantilla: generar el grid de categorías en el front
Ejemplo de código para colocar en la plantilla (por ejemplo archive.php, template parts o un bloque personalizado). Este ejemplo genera un grid con cada categoría como un enlace que incluye la imagen, título, y un overlay. Además muestra cómo indicar un color de overlay por categoría (opcional: guardando category_overlay_color en term meta).
// get-categories-grid.php (incluir donde lo necesites)
terms = get_terms(array(
taxonomy => category,
hide_empty => false,
))
if (!empty(terms) !is_wp_error(terms)) :
echo
foreach (terms as term) {
img_id = get_term_meta(term->term_id, category_image_id, true)
overlay_color = get_term_meta(term->term_id, category_overlay_color, true) // opcional
img_url = img_id ? wp_get_attachment_image_url(img_id, medium) :
term_link = get_term_link(term)
// color por defecto si no existe
overlay_css = overlay_color ? style=--overlay-color: {overlay_color} :
// Si prefieres
por accesibilidad, usa wp_get_attachment_image() en lugar de background-image
echo
if (img_url) {
// usar elemento imagen para accesibilidad y srcset
echo
echo wp_get_attachment_image(img_id, medium, false, array(loading => lazy, alt => esc_attr(term->name)))
echo
echo
} else {
echo
}
echo .esc_html(term->name).
echo
}
echo
endif
5) CSS para el grid, imagen y overlay (hover con transición)
Este CSS crea un grid responsivo, asegura que la imagen cubra el contenedor y aplica el overlay con transición suave. Permite personalizar color por categoría mediante la variable CSS --overlay-color establecida en el HTML generado arriba.
/ Core grid /
.categories-grid {
display: grid
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr))
gap: 20px
}
/ Card genérica /
.category-card {
display: block
position: relative
text-decoration: none
color: inherit
border-radius: 8px
overflow: hidden
background: #f5f5f5
transition: transform .25s ease
}
.category-card:focus {
outline: 3px solid rgba(21,156,228,0.6)
outline-offset: 3px
}
/ Media wrapper (imagen) /
.category-media {
position: relative
width: 100%
height: 0
padding-top: 56.25% / 16:9 ratio /
overflow: hidden
}
.category-media img {
position: absolute
top: 0
left: 0
width: 100%
height: 100%
object-fit: cover
-webkit-font-smoothing: antialiased
transition: transform .45s cubic-bezier(.2,.8,.2,1)
}
/ Overlay usando CSS variable para color personalizable /
.category-media .overlay {
position: absolute
inset: 0
background-color: var(--overlay-color, rgba(0,0,0,0.25))
opacity: 0
transition: opacity .25s ease, background-color .25s ease
}
/ Texto y posicionamiento /
.category-title {
display: block
padding: 12px
font-weight: 700
font-size: 1rem
background: linear-gradient(transparent, rgba(0,0,0,0.08))
color: #222
}
/ Hover / focus effects /
.category-card:hover .category-media img,
.category-card:focus .category-media img {
transform: scale(1.06)
}
.category-card:hover .category-media .overlay,
.category-card:focus .category-media .overlay {
opacity: 1
}
/ Placeholder simple si no hay imagen /
.category-media.placeholder {
background: linear-gradient(135deg,#e2e2e2,#f7f7f7)
height: 140px
padding-top: 0
display:flex
align-items:center
justify-content:center
}
6) Color de overlay por categoría (opcional)
Si quieres permitir que cada categoría tenga su color de overlay, añade un campo adicional tipo color en el admin (o usa ACF). En el render se inyecta la variable CSS --overlay-color. Ejemplo simple de cómo guardar el color:
// Guardar overlay color (suponiendo que el input name sea category_overlay_color)
add_action(created_category, mi_save_category_overlay, 10, 2)
add_action(edited_category, mi_save_category_overlay, 10, 2)
function mi_save_category_overlay(term_id) {
if (isset(_POST[category_overlay_color])) {
color = sanitize_text_field(_POST[category_overlay_color])
update_term_meta(term_id, category_overlay_color, color)
}
}
7) Accesibilidad y SEO
- Imágenes: Si usas ltimggt aprovecha el atributo alt con el nombre de la categoría o una descripción corta. Evita usar únicamente background-image si la imagen transmite información importante.
- Foco: Asegúrate de que los enlaces sean alcanzables por teclado (tab) y que el estado :focus sea claramente visible.
- Texto visible: No relies solo en el overlay para mostrar el nombre ofrece texto visible y semántico (el ltspan class=category-titlegt).
- ARIA: El overlay puede marcarse aria-hidden=true porque es decorativo.
8) Rendimiento y buenas prácticas
- Genera tamaños de imagen adecuados (usa las imágenes de tamaño medio o custom image sizes con add_image_size).
- Activa lazy-loading (wp_get_attachment_image con atributo loading=lazy como en el ejemplo).
- Evita consultas innecesarias: cachea get_terms o usa transients si el número de categorías es grande y no cambia frecuentemente.
- Evita cargar la librería de medios en admin salvo cuando se edite una taxonomía concreta (comprobando get_current_screen como en el ejemplo).
9) Alternativa rápida con ACF (Advanced Custom Fields)
Si tienes ACF Pro o ACF, crea un campo de tipo Image asignado a la taxonomía category. En la plantilla recupera el ID con get_field(mi_imagen, category_.term->term_id) o la URL con get_field(mi_imagen)[sizes][medium]. Esto evita todo el código del admin y la gestión del media uploader.
10) Ejemplo completo de inclusión
Resumen de dónde colocar cada bloque:
- Pegar los hooks PHP (añadir campos y guardar meta) en functions.php o plugin.
- Colocar el archivo js/mi-category-media.js en el directorio del tema y encolar desde functions.php tal como se muestra.
- Crear un template part (get-categories-grid.php) e incluirlo en la plantilla deseada con get_template_part() o require.
- Agregar el CSS en el stylesheet del tema o en un archivo CSS encolado.
Notas finales
Con este enfoque tienes control total sobre la apariencia, accesibilidad y comportamiento del overlay. Puedes extenderlo añadiendo animaciones, estados distintos según cantidad de posts, o integrar lazy-loading más avanzado. Si prefieres no tocar código, usar ACF es la vía más rápida si necesitas máxima personalización, el enfoque con term meta y la integración con la librería de medios es lo más flexible.
Leave a Reply