● Estable v1.0.0

Grid de cards

Grilla de tarjetas responsive con imagen, título, descripción y link. Ideal para productos, artículos o portfolios.

Dependencias Ninguna — HTML y CSS puro.
↗ Ver en GitHub

Descripción

El Grid de cards de Kodeval Pages muestra varios elementos en tarjetas prolijas, todas del mismo tamaño. Cada tarjeta puede ser un producto, un artículo de blog, un proyecto de portfolio o un servicio, con imagen, título, descripción y un link. El resultado es una grilla (una cuadrícula ordenada) que se adapta bien tanto en celular como en computadora (responsive).

La grilla usa CSS Grid (un sistema de CSS para ordenar elementos en filas y columnas) con "repeat(3, 1fr)", que crea tres columnas iguales en pantallas grandes. Si agregás más tarjetas en el HTML, se acomodan solas en la grilla, sin tocar el CSS. Para cambiar la cantidad de columnas, solo cambiás ese número.

Cada tarjeta reacciona cuando pasás el mouse por encima (hover): se levanta un poco (con "transform: translateY(-4px)") y su borde cambia al color principal del proyecto. Este pequeño movimiento le avisa al usuario que la tarjeta se puede clickear, sin usar JavaScript.

La imagen de cada tarjeta usa "aspect-ratio: 16/9", una propiedad CSS que fija la proporción entre ancho y alto. Así todas las imágenes se ven con la misma forma, sin importar el tamaño original. Junto con "object-fit: cover", la imagen siempre llena su espacio sin estirarse ni dejar huecos.

Cuando pasás el mouse sobre una tarjeta, la imagen también se agranda un poquito (con "transform: scale(1.04)"). Es un efecto de acercamiento (zoom) suave que hace la tarjeta más viva, sin distraer del contenido.

El interior de cada tarjeta usa flexbox (un sistema de CSS para acomodar elementos en columna) con "flex: 1", así todas las tarjetas de una misma fila quedan con la misma altura. El link "Ver más" usa "margin-top: auto" para quedar siempre pegado abajo de la tarjeta, sin importar si el título o la descripción son más largos o más cortos.

Cada tarjeta tiene una etiqueta chiquita (.card-tag) arriba del título, con fondo de color semitransparente (se deja ver un poco lo de atrás). Sirve para marcar una categoría, un tipo de contenido o cualquier clasificación que necesites, sin tapar el resto del diseño.

Arriba de toda la grilla hay un encabezado (.cards-header) con el título y la descripción general de la sección. El título cambia de tamaño según la pantalla gracias a clamp() (una función CSS que ajusta un valor entre un mínimo y un máximo), y la descripción usa un color más apagado (muted) para diferenciarse del título. Este encabezado es opcional: si no lo necesitás, borrá ese bloque del HTML.

Todos los colores están guardados en variables CSS (valores reutilizables) dentro de .cards-section: el fondo de la sección, el fondo de las tarjetas, el borde, el texto, el color apagado y el color principal. Cambiando esas variables, cambiás el estilo de toda la grilla desde un solo lugar. También hay variables aparte para la etiqueta de categoría.

En pantallas de hasta 900px de ancho, la grilla pasa de tres a dos columnas. En pantallas de hasta 560px (celulares), pasa a una sola columna, y el espacio interno de la sección se reduce para aprovechar mejor la pantalla chica.

El componente no necesita JavaScript ni ninguna librería. Usa la etiqueta HTML article para cada tarjeta, dentro de una section. Usar estas etiquetas (en vez de divs genéricos) ayuda a que los buscadores y los lectores de pantalla entiendan mejor el contenido.

Este grid de tarjetas sirve para muchos casos: tiendas online, blogs, portfolios, directorios de servicios, galerías de proyectos o presentación de un equipo. Como los colores están en variables CSS, es fácil adaptarlo a cualquier proyecto cambiando solo esos valores.

Para usarlo, pegá el HTML donde quieras mostrar la grilla de tarjetas, y el CSS en tu archivo de estilos (o en un <style> dentro del head). No necesita nada más.

HTML
<section class="cards-section">
  <div class="cards-inner">

    <div class="cards-header">
      <h2>Nuestros proyectos</h2>
      <p>Una selección de lo que estamos construyendo.</p>
    </div>

    <div class="cards-grid">

      <article class="card">
        <div class="card-img-wrap">
          <img src="imagen.jpg" alt="Nombre del proyecto">
        </div>
        <div class="card-body">
          <span class="card-tag">Diseño</span>
          <h3 class="card-title">Nombre del proyecto</h3>
          <p class="card-desc">Descripción breve de qué es este proyecto y para qué sirve.</p>
          <a href="#" class="card-link">Ver más →</a>
        </div>
      </article>

      <article class="card">
        <div class="card-img-wrap">
          <img src="imagen.jpg" alt="Otro proyecto">
        </div>
        <div class="card-body">
          <span class="card-tag">Desarrollo</span>
          <h3 class="card-title">Otro proyecto</h3>
          <p class="card-desc">Descripción breve de qué es este proyecto y para qué sirve.</p>
          <a href="#" class="card-link">Ver más →</a>
        </div>
      </article>

      <article class="card">
        <div class="card-img-wrap">
          <img src="imagen.jpg" alt="Tercer proyecto">
        </div>
        <div class="card-body">
          <span class="card-tag">Producto</span>
          <h3 class="card-title">Tercer proyecto</h3>
          <p class="card-desc">Descripción breve de qué es este proyecto y para qué sirve.</p>
          <a href="#" class="card-link">Ver más →</a>
        </div>
      </article>

    </div>
  </div>
</section>
CSS
/*
|--------------------------------------------------------------------------
| Grid de Cards — Kodeval Pages
|--------------------------------------------------------------------------
| Grilla de tarjetas responsive con imagen, tag, título, descripción y link.
| Sin dependencias externas. Funciona con cualquier número de cards.
|--------------------------------------------------------------------------
*/

/* =====================
   VARIABLES
   ===================== */
.cards-section {
  --cards-bg:      #f8fafc;
  --card-bg:       #ffffff;
  --card-border:   #e2e8f0;
  --card-text:     #0f172a;
  --card-muted:    #64748b;
  --card-accent:   #5865f2;
  --card-tag-bg:   rgba(88,101,242,0.08);
  --card-tag-text: #5865f2;
}

/* =====================
   SECCIÓN
   ===================== */
.cards-section {
  background: var(--cards-bg);
  padding: 5rem 2rem;
}

.cards-inner {
  max-width: 1200px;
  margin: 0 auto;
}

/* =====================
   ENCABEZADO DE LA SECCIÓN
   ===================== */
.cards-header {
  margin-bottom: 3rem;
}

.cards-header h2 {
  font-size: clamp(1.6rem, 3vw, 2.2rem);
  font-weight: 700;
  color: var(--card-text);
  margin-bottom: 0.5rem;
  letter-spacing: -0.02em;
}

.cards-header p {
  font-size: 1rem;
  color: var(--card-muted);
  line-height: 1.6;
}

/* =====================
   GRILLA
   repeat(3, 1fr) → 3 columnas iguales en desktop.
   Se adapta automáticamente en pantallas más chicas.
   Cambiá el 3 por 2 o 4 según cuántas columnas necesites.
   ===================== */
.cards-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.5rem;
}

/* =====================
   TARJETA
   overflow:hidden recorta la imagen al border-radius.
   ===================== */
.card {
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: 16px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: transform 0.2s, border-color 0.2s;
}

.card:hover {
  transform: translateY(-4px);
  border-color: var(--card-accent);
}

/* =====================
   IMAGEN DE LA CARD
   aspect-ratio:16/9 mantiene proporciones consistentes
   sin importar el tamaño de la imagen original.
   ===================== */
.card-img-wrap {
  width: 100%;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: #e2e8f0;
}

.card-img-wrap img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 0.3s ease;
}

.card:hover .card-img-wrap img { transform: scale(1.04); }

/* =====================
   CUERPO DE LA CARD
   flex:1 hace que el cuerpo ocupe el espacio restante,
   lo que alinea los links al fondo en grillas de filas.
   ===================== */
.card-body {
  padding: 1.25rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  flex: 1;
}

/* =====================
   ETIQUETA DE CATEGORÍA
   ===================== */
.card-tag {
  display: inline-block;
  background: var(--card-tag-bg);
  color: var(--card-tag-text);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.25rem 0.6rem;
  border-radius: 6px;
  width: fit-content;
}

/* =====================
   TÍTULO Y DESCRIPCIÓN
   ===================== */
.card-title {
  font-size: 1rem;
  font-weight: 600;
  color: var(--card-text);
  line-height: 1.3;
  margin: 0.25rem 0;
}

.card-desc {
  font-size: 0.88rem;
  color: var(--card-muted);
  line-height: 1.6;
  flex: 1;
}

/* =====================
   LINK
   margin-top:auto empuja el link al fondo de la card.
   ===================== */
.card-link {
  color: var(--card-accent);
  text-decoration: none;
  font-size: 0.85rem;
  font-weight: 600;
  margin-top: auto;
  padding-top: 0.75rem;
  transition: opacity 0.15s;
  display: inline-block;
}

.card-link:hover { opacity: 0.7; }

/* =====================
   RESPONSIVE
   ===================== */
@media (max-width: 900px) {
  .cards-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 560px) {
  .cards-grid { grid-template-columns: 1fr; }
  .cards-section { padding: 3rem 1.5rem; }
}