Ir al contenido
Evaluar mi proyecto
Componentes · TarjetasDisponible

Tarjeta de clima de vidrio

Glassmorfismo sobre cielo degradado: sol con rayos que giran, nube que flota y pronóstico por hora.

Guadalajara

Parcialmente soleado

24°

  • Ahora24°
  • 15h26°
  • 16h25°
  • 17h22°
  • 18h20°

Código completo

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

tarjeta-clima.tsx92 líneas
Descargar
"use client";

import type { CSSProperties } from "react";

/** Tarjeta de clima en glassmorfismo: vidrio esmerilado con sol dibujado en SVG, pronóstico por hora y nubes que flotan. Datos de muestra. React + TypeScript, sin dependencias. */
export type TarjetaClimaProps = {
  city?: string;
  temp?: number;
  condition?: string;
  hours?: { time: string; temp: number; kind: "sol" | "nube" | "lluvia" }[];
  className?: string;
  style?: CSSProperties;
};

const CSS = `
.gctcl-root { box-sizing: border-box; display: block; width: 100%; max-width: 340px; color: #fff; font: 500 15px/1.3 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; }
.gctcl-root *, .gctcl-root *::before, .gctcl-root *::after { box-sizing: border-box; }
@media (prefers-reduced-motion: reduce) { .gctcl-root *, .gctcl-root *::before, .gctcl-root *::after { animation: none !important; } }
.gctcl-card { position: relative; overflow: hidden; padding: 24px; border-radius: 28px; background: rgba(255, 255, 255, .16); -webkit-backdrop-filter: blur(20px) saturate(1.6); backdrop-filter: blur(20px) saturate(1.6); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .35), inset 0 1px 0 rgba(255, 255, 255, .55), 0 24px 40px -18px rgba(20, 40, 100, .6); text-shadow: 0 1px 2px rgba(0, 30, 80, .25); }
.gctcl-top { display: flex; justify-content: space-between; align-items: flex-start; }
.gctcl-city { margin: 0; font-size: 20px; font-weight: 700; }
.gctcl-cond { margin: 2px 0 0; font-size: 14px; opacity: .9; }
.gctcl-temp { margin: 14px 0 0; font: 200 84px/.9 ui-sans-serif, system-ui, -apple-system, sans-serif; letter-spacing: -.05em; }
.gctcl-temp sup { font-size: 34px; font-weight: 300; vertical-align: top; position: relative; top: 8px; margin-left: 2px; }
.gctcl-sun { position: absolute; right: -18px; top: -18px; width: 130px; height: 130px; animation: gctcl-spin 40s linear infinite; }
.gctcl-cloud { position: absolute; right: 26px; top: 62px; width: 92px; height: 38px; border-radius: 999px; background: rgba(255, 255, 255, .85); box-shadow: -18px -12px 0 -2px rgba(255, 255, 255, .85), 14px -16px 0 2px rgba(255, 255, 255, .85); animation: gctcl-drift 6s ease-in-out infinite; }
.gctcl-hours { display: grid; grid-template-columns: repeat(5, 1fr); gap: 6px; margin: 22px 0 0; padding: 14px 0 0; list-style: none; border-top: 1px solid rgba(255, 255, 255, .3); text-align: center; font-size: 13px; }
.gctcl-hours li { display: grid; gap: 8px; justify-items: center; }
.gctcl-hours b { font-weight: 600; font-variant-numeric: tabular-nums; }
@keyframes gctcl-spin { to { transform: rotate(360deg); } }
@keyframes gctcl-drift { 50% { transform: translateX(-12px); } }`;

const Icon = ({ kind }: { kind: "sol" | "nube" | "lluvia" }) => (
  <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
    {kind === "sol" ? (
      <>
        <circle cx="12" cy="12" r="4" />
        <path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4" />
      </>
    ) : (
      <>
        <path d="M7 17a4 4 0 0 1-.6-7.96A5.5 5.5 0 0 1 17 8.5a3.8 3.8 0 0 1 .5 7.5H7z" />
        {kind === "lluvia" ? <path d="M9 20l-1 2M13 20l-1 2M17 20l-1 2" /> : null}
      </>
    )}
  </svg>
);

const DEFAULT_HOURS: TarjetaClimaProps["hours"] = [
  { time: "Ahora", temp: 24, kind: "sol" },
  { time: "15h", temp: 26, kind: "sol" },
  { time: "16h", temp: 25, kind: "nube" },
  { time: "17h", temp: 22, kind: "lluvia" },
  { time: "18h", temp: 20, kind: "nube" },
];

export function TarjetaClima({ city = "Guadalajara", temp = 24, condition = "Parcialmente soleado", hours = DEFAULT_HOURS, className = "", style }: TarjetaClimaProps) {
  return (
    <div className={`gctcl-root ${className}`} style={style}>
      <style>{CSS}</style>
      <article className="gctcl-card" aria-label={`Clima en ${city}`}>
        <svg className="gctcl-sun" viewBox="0 0 130 130" aria-hidden="true">
          <circle cx="65" cy="65" r="30" fill="#ffd45e" opacity=".95" />
          {Array.from({ length: 12 }, (_, index) => (
            <rect key={index} x="62" y="6" width="6" height="18" rx="3" fill="#ffd45e" opacity=".7" transform={`rotate(${index * 30} 65 65)`} />
          ))}
        </svg>
        <span className="gctcl-cloud" aria-hidden="true" />
        <div className="gctcl-top">
          <div>
            <h3 className="gctcl-city">{city}</h3>
            <p className="gctcl-cond">{condition}</p>
          </div>
        </div>
        <p className="gctcl-temp">
          {temp}
          <sup>°</sup>
        </p>
        <ul className="gctcl-hours">
          {hours?.map((hour) => (
            <li key={hour.time}>
              <span>{hour.time}</span>
              <Icon kind={hour.kind} />
              <b>{hour.temp}°</b>
            </li>
          ))}
        </ul>
      </article>
    </div>
  );
}

Ú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-clima

Ejemplo de uso de Tarjeta de clima de vidrio

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

ejemplo.tsx5 líneas
import { TarjetaClima } from "./tarjeta-clima";

export default function Ejemplo() {
  return <TarjetaClima city="Ejemplo" />;
}

Propiedades de TarjetaClima

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

PropiedadTipoDescripción
city?stringOpcional.
temp?numberOpcional.
condition?stringOpcional.
hours?{ time: string; temp: number; kind: "sol" | "nube" | "lluvia" }[]Opcional.
className?stringOpcional.
style?CSSPropertiesOpcional.

Accesibilidad y rendimiento

El archivo tiene 92 líneas (5 KB) y define 2 animaciones con @keyframes. Esto es lo que hace Tarjeta de clima de vidrio por quien lo usa:

  • Detecta prefers-reduced-motion y baja o quita el movimiento cuando la persona lo pide.
  • Expone su estado a tecnologías de apoyo con atributos aria-*.
  • Usa backdrop-filter para el efecto de vidrio; en navegadores sin soporte el panel se ve sin desenfoque.
  • 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)