Ir al contenido
Evaluar mi proyecto
Componentes · TarjetasDisponible

Tarjeta de proyecto expandible

Tarjeta con portada tipográfica, entregables desplegables y enlace al proyecto; disclosure nativo y accesible.

Identidad de producto

Un sistema visual que crece contigo.

Ver entregables
  • Dirección de arte
  • Sistema de componentes
  • Guía de implementación
Abrir proyecto

Código completo

Es exactamente el archivo que se descarga. Cópialo a tu proyecto y úsalo.

tarjeta-proyecto.tsx93 líneas
Descargar
"use client";

import { type CSSProperties } from "react";

/** Tarjeta de proyecto expandible. React + TypeScript, sin dependencias. Personaliza con variables --gcxj-*. */
export type TarjetaProyectoProps = {
  title?: string;
  description?: string;
  tasks?: string[];
  href?: string;
  className?: string;
  style?: CSSProperties;
};

const CSS = `
.gcxj-root { box-sizing: border-box; min-width: 0; max-width: 100%; color: var(--gcxj-fg, #202421); font: 400 15px/1.5 ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
.gcxj-root *, .gcxj-root *::before, .gcxj-root *::after { box-sizing: border-box; }
.gcxj-root button, .gcxj-root input, .gcxj-root textarea, .gcxj-root select { font: inherit; }
.gcxj-root button, .gcxj-root a { -webkit-tap-highlight-color: transparent; }
.gcxj-root :focus-visible { outline: 2px solid var(--gcxj-accent, #25724b); outline-offset: 4px; }
.gcxj-root ::selection { background: var(--gcxj-accent, #25724b); color: #fff; }
.gcxj-root button:disabled { opacity: .5; cursor: not-allowed; }
.gcxj-root svg { flex-shrink: 0; }
@media (prefers-reduced-motion: reduce) { .gcxj-root *, .gcxj-root *::before, .gcxj-root *::after { animation: none !important; transition-duration: .01ms !important; scroll-behavior: auto !important; } }
.gcxj-root { width: 330px; border-radius: 14px; overflow: hidden; background: var(--gcxj-bg, #fff); box-shadow: 0 10px 30px #23322212; }
.gcxj-art { position: relative; height: 150px; overflow: hidden; background: #c5d4a7; display: grid; place-items: center; }
.gcxj-art span { font-size: 80px; line-height:1; font-weight: 600; letter-spacing: -.04em; position: relative; z-index: 1; color: #29472a; }
.gcxj-art i,.gcxj-art b { position: absolute; width: 140px; height: 140px; border:1px solid #6f865c; border-radius: 50%; left: -30px; top: 50px; transition: transform .6s cubic-bezier(.16,1,.3,1); }
.gcxj-art b { left:auto; right:-40px; top:-60px; width:200px; height:200px; }
.gcxj-root:hover .gcxj-art i { transform: translate(10px,-10px); }
.gcxj-content { padding: 24px; }
.gcxj-root h3 { font-size: 22px; letter-spacing:-.02em; margin:0 0 7px; font-weight:600; }
.gcxj-root p { color:#5b6557; font-size:13px; margin:0 0 20px; }
.gcxj-root summary { cursor:pointer; display:flex; align-items:center; justify-content:space-between; min-height:44px; border-top:1px solid #e2e6df; font-size:13px; }
.gcxj-root summary::-webkit-details-marker { display:none; }
.gcxj-root details[open] summary svg { transform:rotate(180deg); }
.gcxj-root ul { padding-left:18px; margin:4px 0 16px; font-size:13px; color:#4b5945; }
.gcxj-root li { margin:7px 0; }
.gcxj-root a { display:flex; align-items:center; justify-content:space-between; min-height:44px; color:#29472a; font-size:13px; font-weight:600; text-underline-offset:4px; }`;

export function TarjetaProyecto({
  title = "Identidad de producto",
  description = "Un sistema visual que crece contigo.",
  tasks = [
    "Dirección de arte",
    "Sistema de componentes",
    "Guía de implementación",
  ],
  href = "#proyecto",
  className = "",
  style,
}: TarjetaProyectoProps) {
  return (
    <article className={`gcxj-root ${className}`} style={style}>
      <style>{CSS}</style>
      <div className="gcxj-art" aria-hidden="true">
        <span>Aa</span>
        <i />
        <b />
      </div>
      <div className="gcxj-content">
        <h3>{title}</h3>
        <p>{description}</p>
        <details>
          <summary>
            Ver entregables
            <svg
              width="18"
              height="18"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              strokeWidth="1.5"
              aria-hidden="true"
            >
              <path d="m6 9 6 6 6-6" />
            </svg>
          </summary>
          <ul>
            {tasks.map((task) => (
              <li key={task}>{task}</li>
            ))}
          </ul>
        </details>
        <a href={href}>
          Abrir proyecto
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" aria-hidden="true"><path d="M5 19 19 5M5 5h14v14"/></svg>
        </a>
      </div>
    </article>
  );
}

Úsalo en tu proyecto

Instálalo con la CLI de shadcn.

terminal.sh1 líneas
npx shadcn@latest add https://www.gassierconsulting.com/r/tarjeta-proyecto

Ejemplo de uso de Tarjeta de proyecto expandible

Después de copiar tarjeta-proyecto.tsx a tu proyecto, impórtalo y colócalo donde lo necesites.

ejemplo.tsx5 líneas
import { TarjetaProyecto } from "./tarjeta-proyecto";

export default function Ejemplo() {
  return <TarjetaProyecto title="Ejemplo" />;
}

Propiedades de TarjetaProyecto

TarjetaProyecto acepta 6 propiedades: todas son opcionales y tienen un valor por defecto, así que funciona sin configurar nada.

PropiedadTipoDescripción
title?stringOpcional.
description?stringOpcional.
tasks?string[]Opcional.
href?stringOpcional.
className?stringOpcional.
style?CSSPropertiesOpcional.

Personaliza Tarjeta de proyecto expandible con variables CSS

Define estas variables en el contenedor o en tu hoja de estilos para cambiar los colores sin tocar el componente. Si no las defines, se usa el valor por defecto.

VariableValor por defecto
--gcxj-fg#202421
--gcxj-accent#25724b
--gcxj-bg#fff

Accesibilidad y rendimiento

El archivo tiene 93 líneas (4 KB) y no necesita @keyframes. Esto es lo que hace Tarjeta de proyecto expandible por quien lo usa:

  • Detecta prefers-reduced-motion y baja o quita el movimiento cuando la persona lo pide.
  • Muestra un anillo de foco visible al navegar con teclado (:focus-visible).
  • Expone su estado a tecnologías de apoyo con atributos aria-*.
  • Es un componente de cliente de Next.js: incluye la directiva "use client".
Licencia
MIT
Dependencias
React >=18
Compatibilidad
React 18 y 19 · Next.js (componente de cliente)