Complemento del blog · Tarjeta de artículo

La Tarjeta de artículo: la card del listado del blog

La cara de cada entrada en el listado: imagen, categoría, título, un resumen y «Leer artículo». Convierte la lista del blog en una vitrina que se recorre de un vistazo —y no es una pieza nueva, es la misma card del catálogo—.

Esta página no es una ficha técnica: es el complemento entero, abierto y explicado. Qué problema resuelve y por qué gana su lugar en el listado, de qué partes se compone, cómo se comporta en el teléfono, dónde se reutiliza y, al final, cómo está construida —del criterio de diseño a la línea de código—.

Con una particularidad que conviene marcar desde el principio: no hay una «tarjeta de blog» aparte. Es la misma CategoryCard de la vitrina de productos, alimentada con los datos de cada artículo. Una sola pieza, una sola estética y un solo lugar que mantener; coherencia entre catálogo y blog sin trabajo extra.

Definición

¿Qué es la tarjeta de artículo?

La unidad visual de cada entrada en el listado del blog: un bloque con imagen, badge de categoría, título, resumen y un enlace para leer. Una por artículo, en rejilla.

La tarjeta de artículo (article card) es el rectángulo que representa una entrada en el listado del blog. Reúne lo justo para decidir si abrirla: una imagen de cabecera, la categoría como etiqueta, el título, un resumen breve y un botón «Leer artículo». Puestas en rejilla, varias tarjetas forman la vitrina del blog —el escaparate frente al índice de texto—.

No se inventó aquí, y tampoco es un componente nuevo: es la misma CategoryCard que pinta la vitrina de productos, reutilizada con los datos del artículo. La página del listado (BlogListing) recorre los artículos y emite una tarjeta por cada uno; el componente solo pinta lo que recibe. Misma pieza, misma estética, dos usos.

Función e importancia

¿Para qué sirve?

Hace tres trabajos: vuelve escaneable el listado, reutiliza el card del catálogo (cero diseño nuevo) y aporta rendimiento de fábrica —cero CLS y carga de imagen por prioridad—.

Su función es que el lector decida rápido. Un listado de títulos a secas obliga a leerlo todo; una rejilla de tarjetas se recorre de un vistazo —imagen, categoría, título, resumen— y el ojo salta a lo que interesa. La tarjeta convierte el archivo del blog en una vitrina, y de ahí salen los clics a los artículos.

Y pesa más de lo que parece para su tamaño. Para el mantenimiento, es reutilizar una sola pieza (CategoryCard) en vez de inventar una card de blog: una estética, un lugar que tocar. Para el rendimiento, trae width/height fijos —cero CLS— y carga las imágenes por prioridad según su posición. Tres efectos, una misma tarjeta.

Vitrina escaneable, no un muro de títulos

Una lista de enlaces a secas obliga a leer línea por línea; una rejilla de tarjetas se recorre de un vistazo. Imagen, categoría, título y resumen le dan al lector lo justo para decidir qué abrir en un segundo. La tarjeta convierte el archivo del blog en un escaparate, no en un índice.

Reutiliza el card del catálogo (cero diseño nuevo)

No hay una «card de blog» aparte: es la misma CategoryCard de la vitrina de productos, alimentada con los datos del artículo. Una sola pieza significa una sola estética, un solo lugar que mantener y coherencia automática entre el catálogo y el blog. La lección de siempre: reusar antes que duplicar.

Rendimiento de fábrica (cero CLS, lazy/eager)

La tarjeta trae width/height fijos, así que el navegador reserva el hueco de la imagen y la página no salta (cero CLS). Las primeras cuatro cargan eager —están en pantalla— y el resto lazy, por su index en la rejilla. Las fotos del pool entran en AVIF: livianas sin perder nitidez.

Anatomía

¿Qué lleva la tarjeta?

Cinco partes: la imagen 16:10 con su alt, el badge de categoría encima, el título H3 (enlace), el blurb del frontmatter y el CTA «Leer artículo» al fondo.

Cada parte cumple un papel claro. La imagen engancha y da contexto; el badge agrupa por tema; el título es a la vez encabezado y enlace principal; el blurb adelanta el contenido; y el CTA cierra con la acción, siempre abajo para que las alturas cuadren en la rejilla. Nada sobra, y el orden va de lo visual a lo accionable.

Abajo, el ejemplo en vivo —réplica anotada a escala de una tarjeta real—. Cada punto numerado se desglosa en su tarjeta: qué resuelve y de qué prop o dato del artículo sale. Recuerda que ninguna parte es exclusiva del blog: son las props de CategoryCard, alimentadas aquí con el título, la categoría, la descripción y la imagen del .mdx.

1

Imagen 16:10 (con alt)

La foto de cabecera, en proporción fija 16:10, recortada con object-fit para que ninguna deforme la rejilla. Lleva siempre alt descriptivo —no relleno— y dimensiones width/height fijas, así el navegador reserva el hueco antes de cargar y la página no salta (cero CLS). La imagen del pool entra optimizada (AVIF).

Dato image · imageAlt · width/height · index<4 ? eager : lazy

2

Badge de categoría

La etiqueta corta sobre la esquina de la imagen con la categoría legible del artículo —Guías, Novedades…—. Da contexto de un vistazo y agrupa visualmente el listado por tema. Sale de un mapa categoría→etiqueta (CAT_LABEL), no del slug crudo, para que se lea en humano.

Dato badge · CAT_LABEL[a.data.category]

3

Título H3 (enlace)

El título del artículo como encabezado H3 —respeta la jerarquía H1 hero → H2 sección → H3 card— y, a la vez, el enlace principal a la entrada. Es anchor text real hacia /blog/<slug>: bueno para el lector y para el enlazado interno que el buscador rastrea.

Dato label · href = /blog/${a.id}

4

Blurb (descripción)

El resumen breve, una o dos frases, que adelanta de qué va el artículo y empuja a abrirlo. No se escribe aquí: es la description del frontmatter del .mdx, así que el resumen del listado y el de la metaetiqueta son la misma fuente —cero desincronización—.

Dato blurb · a.data.description

5

CTA «Leer artículo»

La llamada a la acción al fondo de la tarjeta —siempre abajo, para que las alturas cuadren en la rejilla—. Un único botón, sin ambigüedad, que repite el destino del título. En el catálogo el texto por defecto es «Ver más»; en el blog se pasa «Leer artículo».

Dato ctaLabel="Leer artículo" · href = /blog/${a.id}

Variantes

Otros diseños y aplicaciones

La del blog es la tarjeta completa con imagen, pero la misma CategoryCard cambia de cara según el caso: sin imagen, con chips, destacada, compacta o como card de archivo.

No hay un único modelo: hay una misma pieza —CategoryCard— que cada contexto ajusta sin reescribir nada. Un artículo sin foto cae a la forma «solo texto»; una entrada destacada usa el index 0 con más espacio; un sidebar denso pide la versión compacta; y los archivos de categoría y etiqueta reutilizan la tarjeta exacta del listado.

Abajo, seis variantes —casi todas son configuraciones reales del componente (omitiendo image, pasando subcategories, o solo CSS de la página), y un par insinúan extensiones de contexto—. Cada una con su réplica en vivo y el tipo de uso donde rinde mejor.

  • Con imagen (estándar del blog)

    Blog · Listado

    La del listado /blog: imagen de cabecera con badge encima, título, resumen y «Leer artículo». Es la forma completa de la tarjeta y la que mejor convierte una lista en vitrina. Configuración real del componente, alimentada con los datos del artículo.

  • Sin imagen (solo texto)

    Notas · Cambios

    Cuando un artículo no trae foto, la tarjeta se sostiene igual: el badge pasa al cuerpo —inline, sobre el título— y manda el texto. El componente lo soporta de fábrica: si no recibe image, omite el bloque visual sin romper la rejilla.

  • Con chips de subcategorías

    Catálogo · Temas

    La misma tarjeta con una fila de chips bajo el resumen —subcategorías o temas, como enlaces—. Es el modo nativo de CategoryCard en la vitrina de productos; en el blog se activaría pasando subcategories para cruzar a archivos de etiqueta.

  • Destacada (primera, prioridad)

    Portada · Featured

    La primera de la rejilla, en mayor tamaño o ancho completo, como entrada destacada. No cambia el componente: es la misma tarjeta con index 0 (que ya carga su imagen eager / priority) y un poco más de espacio por CSS de la página.

  • Compacta (lista densa)

    Archivo · Sidebar

    Versión apretada sin imagen o con miniatura: título y poco más, en una columna densa. Útil en «lecturas recomendadas» del sidebar o en archivos largos donde caben más entradas por pantalla. Es CSS por encima de la misma tarjeta.

  • Card de archivo (categoría/etiqueta)

    Archivo · SEO

    La misma tarjeta exacta reutilizada en /blog/categoria/&lt;cat&gt; y /blog/tag/&lt;tag&gt;. No es una variante de diseño, sino de contexto: el archivo filtra los artículos y los pinta con la rejilla y la tarjeta del listado. Coherencia gratis.

Responsive y móvil

Cómo se comporta en el teléfono

En móvil la rejilla pasa a una columna y la tarjeta ocupa el ancho; la imagen mantiene su proporción sin deformar y toda la card es táctil, con el CTA cómodo de tocar.

En escritorio las tarjetas se reparten en dos (o, en el catálogo sin sidebar, tres o cuatro) por fila. En el teléfono no hay ancho para varias columnas, así que la rejilla cae a una sola y cada tarjeta ocupa todo el ancho. La pieza no cambia: solo cuántas caben por fila, controlado por media queries —mobile-first—.

A partir de ahí, tres patrones que la tarjeta ya trae: la rejilla que crece de 1 a 2/3 columnas, la imagen fluida en 16:10 que recorta sin estirar (con width/height para cero CLS) y el área táctil —título y CTA al mismo destino, botón de al menos 44 px—. Cada uno, abajo, con su vista en el teléfono y su receta.

1 · Rejilla que crece (mobile-first)

El grid arranca en 1fr y suma columnas al ganar ancho: 2 en pantallas medianas, 2–3 en escritorio según haya sidebar. La tarjeta es la misma; solo cambia cuántas caben por fila. El listado se apila limpio en el teléfono sin tocar el componente.

CSS · la rejilla 1→2→(2/3) columnas
/* MÓVIL · la rejilla crece de 1 a 2 (a 2/3) columnas (mobile-first).
   Arranca en una columna y suma columnas al ganar ancho; la card
   no cambia, solo cuántas caben por fila. */

.grid { display: grid; grid-template-columns: 1fr; gap: var(--sp-5); }

@media (min-width: 560px)  { .grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .grid { grid-template-columns: repeat(2, 1fr); } }
/* En el catálogo (sin sidebar) la misma card va a 3–4 por fila. */

2 · Imagen fluida sin deformar (16:10)

La caja fija la proporción con aspect-ratio: 16 / 10 y object-fit: cover recorta sin estirar: da igual el tamaño real de la foto, la card no se descuadra. Los width/height del <img> reservan el hueco antes de cargar — cero CLS.

CSS · imagen fluida 16:10
/* MÓVIL · imagen fluida 16:10 sin deformar.
   La caja fija la proporción y object-fit recorta sin estirar:
   da igual el tamaño real de la foto, la card no se descuadra. */

.ccard__media { position: relative; aspect-ratio: 16 / 10; overflow: hidden; }
.ccard__img   { width: 100%; height: 100%; object-fit: cover; }

/* width/height en el <img> reservan el hueco antes de cargar (cero CLS). */

3 · Área táctil cómoda (CTA ≥44 px)

En el teléfono se toca con el dedo: el título y el CTA llevan al mismo destino, y el botón «Leer artículo» mide al menos 44 px de alto —el objetivo táctil recomendado—. Evita toques fallidos y respeta las pautas de accesibilidad, sin agrandar el resto de la card.

CSS · objetivo táctil 44px
/* MÓVIL · toda la card invita al toque y el CTA cómodo (≥44 px).
   El título y el CTA enlazan al mismo destino; el botón mide
   al menos 44 px de alto, el objetivo táctil recomendado. */

.ccard__title a { text-decoration: none; color: inherit; }

@media (max-width: 768px) {
  .ccard__cta { min-height: 44px; }   /* se toca con el dedo, no con un cursor */
}

Posición

¿Dónde se coloca?

Dentro de la rejilla del listado, una por artículo, en la columna de contenido —no en el sidebar—. Aparece en /blog, en /blog/pagina/<n> y en los archivos de categoría y etiqueta.

La tarjeta vive en la rejilla del listado, dentro de BlogListing: una por artículo de la página actual, en la columna principal (a la izquierda del sidebar). Es la unidad que se repite; la rejilla decide cuántas por fila y la paginación, cuántas por página. No se usa suelta ni en el chrome: siempre en una rejilla de contenido.

Y se reutiliza tal cual en cada vista de listado: la página 1 (/blog), las paginadas (/blog/pagina/<n>) y los archivos por categoría (/blog/categoria/<cat>) y etiqueta (/blog/tag/<tag>). En todas es la misma CategoryCard con los mismos datos del artículo, así que el blog entero se ve coherente sin escribir layout nuevo por sección.

Piezas cercanas: el archivo de categoría (reusa esta tarjeta filtrada por tema), la paginación (cómo se parte la rejilla de tarjetas) y la card del catálogo (la misma pieza en la vitrina de productos).

Implementación

Cómo está construida

No es una pieza nueva: es CategoryCard (presentacional) alimentada desde BlogListing con los datos de cada artículo y una imagen estable del pool. El componente solo pinta lo que recibe.

El componente vive en CategoryCard.astro y expone una API pequeña a propósito: label, href, image, imageAlt, badge, blurb, subcategories, ctaLabel e index. No sabe nada del blog —no llama a getCollection ni inventa datos—: solo recorre las props y pinta la tarjeta. Esa separación es justo lo que permite reusarla igual en el catálogo y en el blog.

El trabajo de datos vive en BlogListing, no en el componente. Recorre los artículos de la página y, por cada uno, mapea sus campos a las props: el título a label y al H3, /blog/<id> a href, la description a blurb, la categoría legible (CAT_LABEL) al badge y blogImage(id) a la imagen del pool. El index marca la prioridad de carga —las cuatro primeras eager, el resto lazy—.

Astro · uso desde BlogListing (articulos.map → CategoryCard)
---
// USO · BlogListing recorre los artículos de la página y pinta una
// CategoryCard por cada uno. La misma card del catálogo, con los
// datos del artículo: NO hay una "card de blog" aparte.
import CategoryCard from '@components/CategoryCard.astro'
import { blogImage } from '@lib/blogImages'
import { CAT_LABEL } from '@lib/blog'
---

<div class="grid">
  {articulos.map((a, i) => (
    <CategoryCard
      label={a.data.title}                          {/* título → H3 */}
      href={`/blog/${a.id}`}                         {/* título y CTA */}
      image={blogImage(a.id)}                        {/* foto del pool */}
      imageAlt={a.data.title}                        {/* alt descriptivo */}
      badge={CAT_LABEL[a.data.category] ?? a.data.category}
      blurb={a.data.description}                     {/* resumen del .mdx */}
      ctaLabel="Leer artículo"
      index={i}                                      {/* eager si i < 4 */}
    />
  ))}
</div>
TypeScript · la imagen del pool (blogImage)
---
// LA IMAGEN · no se guarda por artículo: se toma de un pool de fotos
// demo (AVIF) de forma estable por id, así cada entrada siempre
// muestra la misma y el listado se ve uniforme sin subir una por post.
import { blogImage } from '@lib/blogImages'

// blogImage(id) → ruta /images/... AVIF, determinista por el id del .mdx.
const src = blogImage('mi-articulo')   // p. ej. /images/articulos/....avif
---

<CategoryCard image={blogImage(a.id)} imageAlt={a.data.title} /* … */ />
TypeScript · la interface Props de CategoryCard
// LA INTERFACE · API de CategoryCard (presentacional: solo pinta lo
// que recibe). La tarjeta del blog es esta misma, con datos del artículo.
export type Sub = { label: string; href: string }

interface Props {
  label: string             // título de la tarjeta (H3)            [requerido]
  href: string              // destino del título y del CTA         [requerido]
  image?: string            // imagen de cabecera (16:10)
  imageAlt?: string         // alt descriptivo (cae a label)
  badge?: string            // etiqueta corta sobre la imagen (categoría)
  blurb?: string            // texto/resumen (1–2 frases)
  subcategories?: readonly Sub[]  // chips de enlaces hijos
  ctaLabel?: string         // texto del botón (default "Ver más")
  index?: number            // posición en la rejilla; index < 4 → imagen eager
  disabled?: boolean        // tarjeta sin enlace (evita 404s)
}

En concreto: la imagen solo se pinta si llega image; si no, la tarjeta omite el bloque visual y el badge pasa al cuerpo (inline, sobre el título), de modo que un artículo sin foto no deja un hueco roto. El alt cae a label cuando no se pasa imageAlt, y los width/height del <img> van fijos para reservar el hueco —cero CLS—.

La prioridad de carga sale del index: eager = index < 4, así las cuatro primeras (las que se ven al entrar) cargan sin lazy y el resto se difiere. El CTA es un <a href> al fondo (margin-top: auto) para que todas las tarjetas de la rejilla cierren a la misma altura. Cero JavaScript: es HTML + CSS estático generado en build.

Buenas prácticas

Qué hacer y qué evitar

La diferencia entre una tarjeta que ayuda y una que estorba cabe en un puñado de hábitos —empezando por reutilizar CategoryCard en vez de inventar una card propia—.

Ninguno de estos hábitos es capricho: salen de mirar dónde tropieza un listado cuando las tarjetas se diseñan a la ligera. Una tarjeta sana reutiliza la pieza del catálogo, conserva sus dimensiones para no provocar saltos, carga las imágenes por prioridad y cierra con un solo CTA. Una que estorba duplica diseño, quita el width/height o llena la card de botones.

La buena noticia es que casi todo se sostiene solo cuando la tarjeta es CategoryCard y los datos salen del frontmatter del artículo. Abajo, lo que conviene y lo que conviene evitar, enfrentados.

Sí conviene

  • Pasa siempre un alt descriptivo (el título del artículo basta) — nunca lo dejes vacío.
  • Conserva width/height para que el navegador reserve el hueco y no haya salto (cero CLS).
  • Deja que las primeras cuatro carguen eager (van en pantalla) y el resto lazy, por su index.
  • Mantén un solo CTA por tarjeta —«Leer artículo»— que repita el destino del título.
  • Reutiliza CategoryCard alimentándola con los datos del artículo, no una card propia.
  • Usa el badge para la categoría legible (CAT_LABEL), no el slug crudo.

Mejor evita

  • No estrenes una «card de blog» nueva: duplica estética y multiplica el mantenimiento.
  • No quites width/height «porque la imagen es responsive»: vuelve el CLS y baja Core Web Vitals.
  • No pongas dos o tres CTAs por tarjeta: un destino claro, no un ramo de botones.
  • No cargues todas las imágenes eager: las de abajo deben ser lazy o pesa la primera carga.
  • No metas el slug crudo en el badge: pásalo por CAT_LABEL para que se lea en humano.
  • No escribas el resumen aparte: usa la description del .mdx para no desincronizar.
¿Necesitas ayuda?