Módulo del sitio · Tarjeta de categoría

La Tarjeta de categoría: la vitrina que vende

La tarjeta de la rejilla que presenta cada categoría como una mini portada: imagen real con alt descriptivo, badge opcional, título (H3), texto de venta breve, chips de subcategorías con anchor text real y un solo CTA al fondo —alturas iguales en cualquier grid—.

Esta página no es una ficha técnica: es el módulo entero, abierto y explicado. Qué problema resuelve y por qué se ganó su lugar en cada vitrina del sitio, de qué piezas se compone, cómo se comporta en el teléfono, dónde encaja en el armado de la página y, al final, cómo está construido por dentro —del criterio de diseño a la línea de código—.

Con una particularidad que conviene marcar desde el principio: la tarjeta es UNA, las vitrinas son MUCHAS. El mismo componente alimenta la home (SHOWCASE), el roadmap de módulos, las landings de servicios y cobertura (TAXONOMY) y el listado del blog (Content Collection). Cambiar la tipografía del título o el radio del botón se hace en un solo archivo y se propaga al sitio entero.

Definición

¿Qué es una tarjeta de categoría?

La pieza visual que presenta una categoría como si fuera un producto: imagen, badge, título (H3), texto de venta, chips de subcategorías y un solo CTA al fondo, dentro de un grid mobile-first.

La tarjeta de categoría (o «category card») es el módulo que convierte una categoría —un grupo de productos, servicios o artículos— en una pieza visual con su propia portada. No es un bullet con foto: es un patrón estructurado —media + título + blurb + chips + CTA— que se repite N veces dentro de una rejilla. Visto de lejos parece un escaparate; visto de cerca, es lo que evita que el visitante mire una lista de texto y se vaya.

En este proyecto vive en un único componente, CategoryCard.astro, con una API pequeña (label, href, image, imageAlt, badge, blurb, subcategories, ctaLabel, index, disabled). Reusar un solo componente para todas las vitrinas significa que la home, el roadmap de módulos, las landings de servicios y cobertura y el listado del blog usan exactamente la misma tarjeta —cero diseño duplicado, cero CSS que mantener por sección—.

Función e importancia

¿Para qué sirve?

Hace tres trabajos a la vez: convierte una lista de categorías en una vitrina escaneable, reparte autoridad interna con anchor text real y mantiene el ritmo visual del sitio sin pedir CSS por vitrina.

Su función es transformar el catálogo en un mapa. Una lista de categorías (productos, servicios, cobertura, blog) en texto plano obliga a leer línea por línea para decidir. La misma información repartida en tarjetas con foto y chips se entiende de un vistazo: el visitante recorre las imágenes, lee los títulos, escanea los chips y decide a qué entrar. Es la diferencia entre un menú y un escaparate.

Y por eso pesa fuera de proporción a su tamaño. Para los buscadores, los chips de subcategorías son enlaces internos con anchor text descriptivo —reparten autoridad a las páginas hijas mejor que cualquier «ver más»—; para la accesibilidad, el H3 mantiene la jerarquía y el alt describe la imagen; para el equipo que mantiene el sitio, una sola tarjeta para todas las vitrinas significa que ajustar la tipografía o el radio del botón se hace en un archivo y se propaga sin sorpresas.

Vitrina escaneable, no lista

En lugar de listar las categorías como bullets, las presenta como pequeñas portadas. Imagen + título + bajada + atajos a subcategorías + acción: el visitante decide a qué entrar sin leer línea por línea. Es la diferencia entre un menú de texto y un escaparate.

SEO interno con anchor text real

Cada chip de subcategoría es un enlace interno con texto descriptivo («Cascos», «Botas»), no un genérico «ver más». Eso reparte autoridad a las páginas hijas y le da a Google la mejor pista del contenido de cada subpágina, sin esfuerzo extra del editor.

Una tarjeta, varias vitrinas

El mismo componente alimenta SHOWCASE (home), TAXONOMY (servicios, cobertura), una Content Collection (blog) y hasta el bloque «por qué elegirnos». Cambiar la tipografía del título o el radio del botón se hace en un solo archivo, y el sitio entero responde.

Anatomía

¿Qué lleva la tarjeta?

Cuatro piezas: el media con su alt (y badge opcional), el título H3 enlazable, el blurb con los chips de subcategorías y el CTA al fondo —anclado para mantener alturas iguales en el grid—.

Cada pieza cumple un papel claro. El media (imagen + alt + badge) entra por los ojos y reserva el hueco con width/height fijos para evitar el CLS; el título H3 da la jerarquía semántica y es enlace real; el blurb une beneficio + keyword + confianza en 1–2 frases; los chips son enlaces a páginas hijas con anchor text descriptivo; y el CTA cierra al fondo de la tarjeta con margin-top: auto, así todas las tarjetas del grid acaban a la misma altura aunque su texto difiera.

Abajo, el ejemplo en vivo —réplica anotada a escala del componente—. Cada punto numerado se desglosa en su tarjeta: qué resuelve y de qué prop del componente sale. Y para que el mapeo sea inequívoco, los pines del espécimen apuntan a los mismos elementos del CategoryCard que aparece en la home: lo que ves arriba es exactamente lo que se documenta abajo.

1

Media (imagen + alt + badge)

La foto de cabecera con su texto alternativo y, opcional, una etiqueta corta sobre la imagen (gancho: «Entrega 24 h», «Certificado»). Lleva width/height fijos (640×400, ratio 16:10) para reservar el hueco y evitar el salto de maquetación (CLS). Las cuatro primeras tarjetas cargan en eager; el resto, en lazy.

Dato props image · imageAlt · badge · index

2

Título (H3) enlazable

El encabezado de la tarjeta, semánticamente H3 (un escalón debajo del H2 de la sección que la contiene). Es el ancla que el visitante y el buscador leen como nombre de la categoría. Es enlace de verdad —apunta al mismo href de la tarjeta— para que un clic en el título también navegue.

Dato props label · href

3

Blurb + chips de subcategorías

El texto de venta (1–2 frases que unen beneficio + keyword + confianza, ~120–160 caracteres) y debajo los enlaces internos a subcategorías con anchor text real («Línea profesional», «Accesorios»). Los chips reparten autoridad a las páginas hijas y le dan al visitante el atajo exacto.

Dato props blurb · subcategories[]

4

CTA al fondo (alturas iguales)

Un solo botón claro, anclado al fondo de la tarjeta con margin-top: auto. Eso garantiza que en un grid con tarjetas de distinta altura todos los CTAs queden a la misma línea horizontal —la vitrina se ve cuadrada—. Su texto por defecto es «Ver más»; cambia con ctaLabel.

Dato prop ctaLabel · href

Variantes

Otros diseños y aplicaciones

La de esta plantilla es la canónica (imagen + título + bajada), pero la tarjeta cambia de cara según lo que el grid necesita presentar: catálogo, promoción, módulo en construcción, listado editorial.

No hay un único modelo: hay una misma idea —una pieza con media, título, blurb y CTA al fondo— que cada tipo de vitrina ajusta. Un catálogo con promociones añade badge sobre la imagen; un roadmap muestra módulos sin foto y los próximos como deshabilitados; un blog largo se beneficia de tarjetas horizontales; una sección destacada las quiere oscuras sobre fondo de marca.

Abajo, seis variantes —cuatro son configuraciones REALES del componente actual (cambiando solo las props image, badge y disabled), y dos son extensiones propuestas que requieren añadir CSS o una prop al componente—. Cada una con su réplica en vivo y el tipo de proyecto donde rinde mejor.

  • Imagen + título + bajada (esta plantilla)

    Catálogo · Vitrina

    La canónica: foto a 16:10, título H3 enlazado, blurb de venta, chips de subcategorías y CTA al fondo. Es lo que alimenta la home (SHOWCASE) y se reusa en /modulos/, /servicios/, /cobertura/, /blog/. Configuración real: image + label + blurb + subcategories + ctaLabel.

  • Con badge sobre la imagen

    E-commerce · Promociones

    Misma tarjeta, sumando el prop badge: una etiqueta corta sobre la foto (gancho de marketing) —«Envío 24 h», «Certificado», «-15 %»—. Va arriba a la izquierda, en color de marca. Configuración real: badge="…". Útil para diferenciar categorías destacadas sin romper el ritmo del grid.

  • Sin imagen (texto + chevron)

    Módulos · Roadmap · Listas

    Cuando no hay foto disponible o conviene una vitrina más austera, se omite image. El badge cae inline al inicio del cuerpo, el título queda sin foto y la tarjeta se ancla en el texto + chips + CTA con su flecha. Configuración real: omitir image; usar badge="Próximamente" para inventario en construcción.

  • Próximamente (deshabilitada)

    Roadmap · Catálogo en construcción

    Tarjeta inactiva: ni la imagen ni los chips son enlaces y el CTA muestra «Próximamente» sin link. El badge cambia a estilo sutil («Próximamente»). Útil para mostrar inventario futuro sin generar 404s. Configuración real: disabled={true}. Ya en producción en el roadmap de /modulos/.

  • Horizontal (landscape)

    Editorial · Posts del blog

    Extensión propuesta: imagen a la izquierda (40 %) y cuerpo a la derecha (60 %), en lugar del stack vertical. Pensada para listados de blog donde una rejilla landscape se lee mejor que cuatro tarjetas verticales. Requiere una variante CSS (.ccard--landscape) y cambiar el aspect-ratio de la media a 4:3.

  • Oscura sobre superficie

    CTA banner · Sección destacada

    Extensión propuesta: misma estructura pero con fondo oscuro y texto claro, para colocar la vitrina sobre una sección hero secundaria o un fondo de marca. Requiere añadir un prop dark al componente —similar al de SectionHeading— y derivar los colores del título, el blurb y el border desde la nueva variante.

Responsive y móvil

Cómo se comporta en el teléfono

La tarjeta no se encoge: cambia de modo según la rejilla que la contenga. Stack a ancho completo de fábrica; grid 1 → 2 → 3 para vitrinas cortas; scroll horizontal con snap o carrusel con dots para listas largas.

La tarjeta en sí es agnóstica al grid: no impone columnas. Quien define la rejilla es el padre. La regla del proyecto es mobile-first: arrancar en una sola columna (cada tarjeta al 100 % del ancho, alturas iguales por el flex column + margin-top: auto del CTA) y crecer con min-width —2 columnas en tablet, 4 en escritorio para el catálogo, 3 para vitrinas más cortas (servicios, cobertura)—. Es lo más seguro, y lo que ya hace el sitio sin un molde aparte por sección.

A partir de ahí, hay dos patrones que conviene conocer como extensión opcional: scroll horizontal con snap (cuando la vitrina es muy larga y apilarla hace la pantalla demasiado alta) y carrusel con dots (la misma idea con indicador de posición). Las cuatro están abajo, cada una con su vista en el teléfono y una receta comentada lista para adaptar.

1 · Stack a ancho completo (default)

Es lo que ya da la rejilla por defecto. El padre declara grid-template-columns: 1fr en móvil y cada tarjeta ocupa el ancho. La card es flex: column; height: 100%, así que el CTA queda anclado al fondo. Cero esfuerzo: nada que tocar a nivel componente.

CSS · grid mobile-first 1 → 2 → 4 (default)
/* MÓVIL · STACK A ANCHO COMPLETO (lo que ya da el grid default)
   El componente no impone grid; lo aporta el padre. Cuando el
   padre declara una columna, cada tarjeta ocupa el 100 % del
   ancho disponible. La card en sí ya es flex column con
   height: 100 %, así que el CTA queda anclado al fondo. */

.showcase {
  display: grid;
  grid-template-columns: 1fr;       /* una columna por defecto */
  gap: var(--sp-5);
}

/* A partir de 640 px crece a 2 columnas, y a 1024 px a 4. Es la
   regla mobile-first: el caso base (el teléfono) es el más simple. */
@media (min-width: 640px)  { .showcase { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .showcase { grid-template-columns: repeat(4, 1fr); } }

2 · Grid 1 → 2 → 3 (vitrinas cortas)

Cuando la vitrina tiene 3 o 6 elementos (SERVICES, COVERAGE_STATES), bajar el último breakpoint a repeat(3, 1fr) evita huecos en la última fila. No es una extensión del componente: es una decisión del padre, así la tarjeta se adapta al ancho que reciba.

CSS · grid 1 → 2 → 3 para 3/6 tarjetas
/* MÓVIL · GRID 1 → 2 → 3 (vitrinas más cortas)
   Cuando la vitrina tiene 3 o 6 elementos (servicios, cobertura),
   la rejilla de 4 columnas deja huecos. Bajarla a 3 columnas en
   escritorio mantiene el ritmo cuadrado sin tarjetas sueltas.
   Es la misma estructura, cambiando solo el segundo breakpoint. */

.grid {
  display: grid;
  grid-template-columns: 1fr;       /* móvil */
  gap: var(--sp-5);
}

@media (min-width: 640px)  { .grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .grid { grid-template-columns: repeat(3, 1fr); } }

/* No es una extensión del componente: es una regla del padre.
   La tarjeta se acomoda a la columna que reciba —100 px o 320 px—,
   manteniendo el aspect-ratio 16:10 de la imagen. */

3 · Scroll horizontal con snap (extensión)

Para vitrinas largas (8–12 tarjetas) el stack alarga demasiado la pantalla. Convertir la rejilla en una fila scrollable con scroll-snap-type: x mandatory alinea cada tarjeta al soltar el dedo. La barra de scroll se oculta y la siguiente tarjeta asoma como affordance.

CSS · fila scrollable + snap en ≤ 640 px
/* MÓVIL · SCROLL HORIZONTAL CON SNAP (extensión opcional)
   En vitrinas largas (8–12 tarjetas) apilarlas todas hace la
   pantalla muy alta. Esta extensión convierte la rejilla en una
   sola fila scrollable —tipo carrusel iOS— solo en móvil. Snap
   para que cada tarjeta se alinee al soltar el dedo. */

@media (max-width: 640px) {
  .showcase {
    display: flex;
    overflow-x: auto;
    gap: var(--sp-4);
    padding-inline: var(--container-px);
    margin-inline: calc(var(--container-px) * -1);  /* sangra el contenedor */

    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;              /* inercia iOS */
    scrollbar-width: none;                          /* Firefox */
  }
  .showcase::-webkit-scrollbar { display: none; }

  /* Cada tarjeta ≈ 78 % del ancho: la siguiente asoma para invitar
     a deslizar (affordance visual). */
  .showcase > .ccard {
    flex: 0 0 78%;
    scroll-snap-align: start;
  }
}

4 · Carrusel con dots (extensión)

Variante del scroll-snap que añade indicadores debajo: 1 de N. Se activan con un IntersectionObserver mínimo (≈ 5 líneas de JS, en la página, no en el componente). Útil en landings de marketing donde el dot da pista de cuánto queda por ver; en el resto del sitio, el scroll mudo basta.

CSS · dots + JS mínimo (IntersectionObserver)
/* MÓVIL · CARRUSEL CON DOTS (extensión opcional)
   Variante del scroll-snap que añade indicadores debajo: «1 de 4».
   Se generan con un IntersectionObserver mínimo (5 líneas de JS)
   que marca el dot activo según la tarjeta visible en el viewport.
   En esta plantilla NO se usa: se documenta como gancho para
   landings donde el carrusel rinde más que el scroll mudo. */

@media (max-width: 640px) {
  .showcase { /* (mismo scroll-snap del patrón anterior) */ }

  .showcase__dots {
    display: flex; justify-content: center; gap: 8px;
    margin-top: var(--sp-3);
  }
  .showcase__dots b {
    width: 8px; height: 8px; border-radius: 99px;
    background: var(--c-border);
    transition: background var(--transition), width var(--transition);
  }
  .showcase__dots b[aria-current="true"] {
    background: var(--c-primary); width: 20px;
  }
}

/* JS mínimo (en el <script> de la página, no del componente):
   const cards = document.querySelectorAll('.showcase > .ccard');
   const dots  = document.querySelectorAll('.showcase__dots > b');
   const io = new IntersectionObserver((entries) => {
     entries.forEach((e) => {
       if (!e.isIntersecting) return;
       const i = [...cards].indexOf(e.target);
       dots.forEach((d, j) => d.setAttribute('aria-current', j === i));
     });
   }, { threshold: 0.6 });
   cards.forEach((c) => io.observe(c)); */

Posición

¿Dónde se coloca?

N veces por página, siempre dentro de un grid (.showcase o .grid) que vive en el .container del 90 %. Cada vitrina del sitio —catálogo, servicios, cobertura, blog, módulos— es una rejilla de estas tarjetas.

A diferencia del hero (una vez por página) o del menú de secciones (una o dos), la tarjeta de categoría se repite tantas veces como la rejilla lo pida. En el catálogo de la home son 4 (SHOWCASE); en el roadmap de módulos, 15 (MODULOS); en cobertura, los estados que haya en TAXONOMY. La tarjeta es una unidad; el patrón de aparición es la rejilla.

Vive SIEMPRE dentro del .container central (90 % del ancho), nunca full-width. La rejilla padre define cuántas columnas a cada breakpoint: 1 → 2 → 4 para el catálogo (mucha oferta), 1 → 2 → 3 para vitrinas más cortas (servicios, cobertura). El componente respeta cualquiera de las dos: no impone columnas, solo se acomoda al ancho que reciba manteniendo el aspect-ratio 16:10 de su imagen.

Implementación

Cómo está construido

Un único componente con API pequeña: label, href, image, imageAlt, badge, blurb, subcategories, ctaLabel, index, disabled. Devuelve un <article> con flex column + CTA anclado al fondo (alturas iguales) y un solo elemento con transición —el botón—.

El componente vive en CategoryCard.astro y expone diez props a propósito: label (obligatorio, el H3), href (obligatorio, destino del título y del CTA), image + imageAlt (opcional, foto de cabecera con texto alternativo descriptivo), badge (etiqueta corta de gancho), blurb (1–2 frases de venta), subcategories[] (enlaces internos a páginas hijas con anchor text real), ctaLabel (texto del botón, default «Ver más»), index (posición en el grid: los 4 primeros cargan eager) y disabled (tarjeta sin enlace, p. ej. módulo «próximo»).

La regla de uso está cableada en los defaults: ctaLabel = «Ver más», index = 99 (lazy), disabled = false. Eso significa que pasando solo label y href ya se obtiene una tarjeta válida. Para una vitrina completa basta con el array de datos (SHOWCASE, TAXONOMY o una Content Collection) y el grid del padre. Cero JavaScript en el componente: HTML + CSS estático generado en build.

Astro · uso básico (rejilla hard-coded)
---
// USO BÁSICO · pásale las 4 props clave y listo. El componente
// se encarga de la jerarquía (H3), el lazy de la imagen y el
// CTA anclado al fondo (alturas iguales en cualquier grid).
import CategoryCard from '@components/CategoryCard.astro'
---

<div class="grid">
  <CategoryCard
    label="Cascos de seguridad"
    href="/productos/cascos"
    image="/images/showcase/cascos-seguridad-industrial.avif"
    imageAlt="Cascos de seguridad industrial certificados, vista de catálogo"
    badge="Certificados"
    blurb="Cascos homologados para industria pesada, ligeros y con barboquejo ajustable. Stock para entrega 24 h en CDMX."
    subcategories={[
      { label: 'Industrial',     href: '/productos/cascos/industrial' },
      { label: 'Construcción',   href: '/productos/cascos/construccion' },
      { label: 'Accesorios',     href: '/productos/cascos/accesorios' },
    ]}
    ctaLabel="Ver catálogo"
    index={0}
  />
  {/* …más tarjetas… */}
</div>

<style>
  /* Rejilla mobile-first: 1 → 2 → 4 (catálogo) — el componente
     no impone grid, lo aporta el padre, así sirve a cualquier
     vitrina del sitio. */
  .grid { display: grid; grid-template-columns: 1fr; gap: var(--sp-5); }
  @media (min-width: 640px)  { .grid { grid-template-columns: repeat(2, 1fr); } }
  @media (min-width: 1024px) { .grid { grid-template-columns: repeat(4, 1fr); } }
</style>
Astro · data-driven desde TAXONOMY (servicios/cobertura)
---
// DATA-DRIVEN DESDE TAXONOMY · cómo lo hace /servicios y /cobertura.
// Una sola fuente (TAXONOMY en site.ts) alimenta el menú, el footer y
// estas vitrinas: agregar un servicio o un estado se refleja en todos.
import { SERVICES, COVERAGE_STATES } from '@config/site'
import CategoryCard from '@components/CategoryCard.astro'

// Para servicios: id → URL, label → título, desc → blurb.
---

<div class="grid">
  {SERVICES.map((s, i) => (
    <CategoryCard
      label={s.label}
      href={`/servicios/${s.id}`}
      blurb={s.desc}
      index={i}
      ctaLabel="Ver servicio"
    />
  ))}
</div>

{/* Mismo patrón para cobertura, con label y slug del estado. */}
<div class="grid">
  {COVERAGE_STATES.map((z, i) => (
    <CategoryCard
      label={z.label}
      href={`/cobertura/${z.slug}`}
      badge={z.type === 'operativo' ? 'Operativo' : 'Comercial'}
      blurb={`Cobertura ${z.type} en ${z.label}.`}
      index={i}
      ctaLabel="Ver cobertura"
    />
  ))}
</div>
Astro · data-driven desde una Content Collection (blog)
---
// DATA-DRIVEN DESDE UNA CONTENT COLLECTION · cómo lo hace /blog.
// El blog vive en src/content/blog/*.md con un schema Zod (title,
// description, image, …). getCollection() devuelve el array tipado
// y la vitrina se arma sin tocar el componente.
import { getCollection } from 'astro:content'
import CategoryCard from '@components/CategoryCard.astro'

const articulos = (await getCollection('blog'))
  .filter((a) => !a.data.draft)
  .sort((a, b) => +new Date(b.data.date) - +new Date(a.data.date))
---

<div class="grid">
  {articulos.map((a, i) => (
    <CategoryCard
      label={a.data.title}
      href={`/blog/${a.id}`}
      image={a.data.image}
      imageAlt={a.data.imageAlt ?? a.data.title}
      badge={a.data.category}
      blurb={a.data.description}
      subcategories={(a.data.tags ?? []).slice(0, 3).map((t: string) => ({
        label: t,
        href: `/blog/tag/${t}`,
      }))}
      ctaLabel="Leer artículo"
      index={i}
    />
  ))}
</div>

En concreto: el componente recibe las props, decide la etiqueta del media link (const MediaTag = disabled ? 'div' : 'a') y la de los chips (const ChipTag = disabled ? 'span' : 'a') para evitar 404s en tarjetas inactivas. Emite un <article class="ccard"> con un media opcional (imagen + overlay + badge), un <h3> con su enlace, el blurb, una lista <ul> de chips con aria-label de subcategoría y el CTA al fondo. La imagen lleva width="640" height="400" (ratio 16:10) para reservar el hueco y evitar CLS, y loading=eager solo cuando index < 4 (LCP friendly).

La accesibilidad es de fábrica: el contenedor es un <article> semántico, el título es un <h3> real, los chips son una <ul> con aria-label descriptivo, la flecha del CTA lleva aria-hidden="true" y el overlay decorativo de la imagen también. El foco se ve (:focus-visible con outline de color de marca) y, para quienes prefieren menos animación (prefers-reduced-motion), la única transición —la del botón— se desactiva. Los tokens del proyecto (--c-primary, --radius-lg, --sp-5) son la única fuente de estilo: cambiar el design system cambia todas las vitrinas.

Buenas prácticas

Qué hacer y qué evitar

La diferencia entre una vitrina que vende y una que se siente «hecha por bloques distintos» cabe en un puñado de hábitos —empezando por reusar CategoryCard en TODAS las rejillas del sitio—.

Ninguno de estos hábitos es capricho: salen de la regla canónica del proyecto («una sola tarjeta para todas las vitrinas»). El sitio se siente coherente porque cada rejilla nace del mismo componente, con la misma tipografía, los mismos chips y el mismo botón. La única decisión real es la rejilla padre: 1 → 2 → 4 para el catálogo, 1 → 2 → 3 para vitrinas más cortas, y agnóstico en cuanto a la fuente de datos (SHOWCASE, TAXONOMY o Content Collection).

La buena noticia es que casi todo se sostiene solo cuando se respeta el componente: las alturas iguales las da el flex column + margin-top: auto del CTA, el LCP lo cuida el eager de las 4 primeras imágenes y la jerarquía H3 está cableada. Abajo, lo que conviene y lo que conviene evitar, enfrentados.

Sí conviene

  • Reusa CategoryCard en TODAS las vitrinas: catálogo, módulos, blog, «por qué elegirnos». Un solo componente, cero CSS duplicado.
  • Mantén la rejilla mobile-first: 1 → 2 → 4 (catálogo) o 1 → 2 → 3 (vitrinas más cortas), nunca al revés.
  • Escribe alt descriptivos con la keyword (no «imagen1.jpg»); declara width/height para reservar el hueco y evitar CLS.
  • Mantén las 4 primeras tarjetas con index < 4 para que el componente cargue su imagen en eager (LCP).
  • Usa los chips para enlazar a páginas hijas reales con anchor text descriptivo, no a la misma landing.

Mejor evita

  • No metas dos CTAs en la misma tarjeta: un solo botón claro al fondo, sin competir con el título.
  • No uses un H2 dentro de la tarjeta: rompe la jerarquía (H1 hero → H2 sección → H3 tarjeta).
  • No olvides el imageAlt: dejarlo vacío rompe a11y y SEO de imagen; el componente cae a label como fallback, pero conviene escribirlo.
  • No mezcles tarjetas con y sin imagen en la misma rejilla sin criterio: pierdes el ritmo visual de alturas iguales.
  • No abuses del badge: 1–2 palabras como gancho; convertido en frase, deja de funcionar como sello.
¿Necesitas ayuda?