Ir al contenido
Evaluar mi proyecto
Componentes · TarjetasDisponible

Tarjeta de perfil con disponibilidad

Perfil con iniciales, especialidades y disponibilidad explícita; enlace de contacto configurable.

Disponible

Alex Rivera

Product designer

  • Interfaces
  • Sistemas
  • Prototipos
Hablemos

Código completo

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

tarjeta-perfil.tsx87 líneas
Descargar
"use client";

import { type CSSProperties } from "react";

/** Tarjeta de perfil con disponibilidad. React + TypeScript, sin dependencias. Personaliza con variables --gcxr-*. */
export type TarjetaPerfilProps = {
  name?: string;
  role?: string;
  initials?: string;
  available?: boolean;
  tags?: string[];
  href?: string;
  className?: string;
  style?: CSSProperties;
};

const CSS = `
.gcxr-root { box-sizing: border-box; min-width: 0; max-width: 100%; color: var(--gcxr-fg, #202421); font: 400 15px/1.5 ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
.gcxr-root *, .gcxr-root *::before, .gcxr-root *::after { box-sizing: border-box; }
.gcxr-root button, .gcxr-root input, .gcxr-root textarea, .gcxr-root select { font: inherit; }
.gcxr-root button, .gcxr-root a { -webkit-tap-highlight-color: transparent; }
.gcxr-root :focus-visible { outline: 2px solid var(--gcxr-accent, #25724b); outline-offset: 4px; }
.gcxr-root ::selection { background: var(--gcxr-accent, #25724b); color: #fff; }
.gcxr-root button:disabled { opacity: .5; cursor: not-allowed; }
.gcxr-root svg { flex-shrink: 0; }
@media (prefers-reduced-motion: reduce) { .gcxr-root *, .gcxr-root *::before, .gcxr-root *::after { animation: none !important; transition-duration: .01ms !important; scroll-behavior: auto !important; } }
.gcxr-root { width: 320px; padding: 28px; border: 1px solid #d8d2c6; border-radius: 14px; background: var(--gcxr-bg,#fbf9f3); }
.gcxr-top { display:flex; justify-content:space-between; gap:12px; align-items:center; }
.gcxr-avatar { display:grid; place-items:center; width:60px; height:60px; background:#e6d6bd; color:#61432d; border-radius:50%; font-size:21px; font-weight:600; }
.gcxr-state { display:flex; gap:6px; align-items:center; font-size:10px; color:#5d594e; }
.gcxr-state i { width:6px; height:6px; border-radius:50%; background:#8b887e; }
.gcxr-state[data-available="true"] i { background:#26734d; }
.gcxr-root h3 { font-size:26px; letter-spacing:-.02em; margin:26px 0 4px; }
.gcxr-root p { color:#716657; font-size:13px; margin:0; }
.gcxr-root ul { list-style:none; display:flex; flex-wrap:wrap; gap:7px; padding:0; margin:24px 0; }
.gcxr-root li { font-size:11px; color:#665643; background:#eee7dc; border-radius:4px; padding:5px 8px; }
.gcxr-root a { display:flex; justify-content:space-between; align-items:center; padding:12px 0 0; border-top:1px solid #ded7ca; min-height:44px; color:#563e2b; font-weight:600; text-decoration:none; }
.gcxr-root a svg { transition:transform .25s; }
.gcxr-root a:hover svg { transform:translateX(4px); }`;

export function TarjetaPerfil({
  name = "Alex Rivera",
  role = "Product designer",
  initials = "AR",
  available = true,
  tags = ["Interfaces", "Sistemas", "Prototipos"],
  href = "mailto:alex@example.com",
  className = "",
  style,
}: TarjetaPerfilProps) {
  return (
    <article className={`gcxr-root ${className}`} style={style}>
      <style>{CSS}</style>
      <div className="gcxr-top">
        <span className="gcxr-avatar" aria-hidden="true">
          {initials}
        </span>
        <span className="gcxr-state" data-available={available}>
          <i aria-hidden="true" />
          {available ? "Disponible" : "Sin disponibilidad"}
        </span>
      </div>
      <h3>{name}</h3>
      <p>{role}</p>
      <ul>
        {tags.map((tag) => (
          <li key={tag}>{tag}</li>
        ))}
      </ul>
      <a href={href}>
        Hablemos
        <svg
          width="18"
          height="18"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          strokeWidth="1.5"
          aria-hidden="true"
        >
          <path d="M5 12h14m-6-6 6 6-6 6" />
        </svg>
      </a>
    </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-perfil

Ejemplo de uso de Tarjeta de perfil con disponibilidad

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

ejemplo.tsx5 líneas
import { TarjetaPerfil } from "./tarjeta-perfil";

export default function Ejemplo() {
  return <TarjetaPerfil role="Ejemplo" available />;
}

Propiedades de TarjetaPerfil

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

PropiedadTipoDescripción
name?stringOpcional.
role?stringOpcional.
initials?stringOpcional.
available?booleanOpcional.
tags?string[]Opcional.
href?stringOpcional.
className?stringOpcional.
style?CSSPropertiesOpcional.

Personaliza Tarjeta de perfil con disponibilidad 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
--gcxr-fg#202421
--gcxr-accent#25724b
--gcxr-bg#fbf9f3

Accesibilidad y rendimiento

El archivo tiene 87 líneas (4 KB) y no necesita @keyframes. Esto es lo que hace Tarjeta de perfil con disponibilidad 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)