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.
"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.
npx shadcn@latest add https://www.gassierconsulting.com/r/tarjeta-climaEjemplo 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.
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.
| Propiedad | Tipo | Descripción |
|---|---|---|
city? | string | Opcional. |
temp? | number | Opcional. |
condition? | string | Opcional. |
hours? | { time: string; temp: number; kind: "sol" | "nube" | "lluvia" }[] | Opcional. |
className? | string | Opcional. |
style? | CSSProperties | Opcional. |
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".