WeatherSprite
Escena chica para cada estado del clima del producto: soleado, parcial, nublado, tormenta y niebla.
Los umbrales son los del producto: promedio desde 4,2 soleado, desde 3,5 parcial, desde 2,8 nublado y por debajo tormenta. Con menos de 8 pulsos en los últimos 7 días el estado es niebla. weatherFor(promedio, pulsos) hace esa cuenta.
Instalación
npm
npm i @moodinary/spritesshadcn
npx shadcn@latest add https://design.moodinary.com/r/sprites-weather.jsonimport
import { WeatherSprite } from "@moodinary/sprites/react";
import { weatherFor } from "@moodinary/sprites";Ejemplos
Los cinco estados
Código
weather-states.tsx
import { WEATHER_LABEL, type Weather } from "@moodinary/sprites";
import { WeatherSprite } from "@moodinary/sprites/react";
const states: Weather[] = [
"soleado",
"parcial",
"nublado",
"tormenta",
"niebla",
];
export default function WeatherStates() {
return (
<div className="grid w-full grid-cols-2 gap-3 sm:grid-cols-5">
{states.map((w) => (
<figure className="m-0" key={w}>
<div className="h-20 overflow-hidden rounded-[8px] border border-line">
<WeatherSprite weather={w} />
</div>
<figcaption className="mt-1.5 text-[12px] text-ink-3">
{WEATHER_LABEL[w]}
</figcaption>
</figure>
))}
</div>
);
}De promedio a clima
Mové los controles. Con pocos pulsos manda la niebla, sin importar el promedio.
Parcial
Código
weather-for.tsx
import { WEATHER_LABEL, weatherFor } from "@moodinary/sprites";
import { WeatherSprite } from "@moodinary/sprites/react";
import { useState } from "react";
export default function WeatherFor() {
const [avg, setAvg] = useState(3.7);
const [n, setN] = useState(14);
const w = weatherFor(avg, n);
return (
<div className="flex w-full flex-wrap items-center gap-6">
<div className="h-24 w-40 overflow-hidden rounded-[8px] border border-line">
<WeatherSprite weather={w} />
</div>
<div className="grid gap-3 text-[13px] text-ink-2">
<label className="flex items-center gap-3">
Promedio{" "}
<input
aria-label="Promedio"
max={5}
min={1}
onChange={(e) => setAvg(Number(e.target.value))}
step={0.1}
type="range"
value={avg}
/>{" "}
<code className="font-mono text-ink">
{avg.toFixed(1).replace(".", ",")}
</code>
</label>
<label className="flex items-center gap-3">
Pulsos en 7 días{" "}
<input
aria-label="Pulsos en 7 días"
max={30}
min={0}
onChange={(e) => setN(Number(e.target.value))}
type="range"
value={n}
/>{" "}
<code className="font-mono text-ink">{n}</code>
</label>
<div className="font-medium text-ink">{WEATHER_LABEL[w]}</div>
</div>
</div>
);
}Props
WeatherSprite
| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| weather | "soleado" | "parcial" | "nublado" | "tormenta" | "niebla" | - | Estado a dibujar. |
| pixel | number | 3 | Píxeles CSS por píxel del dibujo. Más alto, más grueso. |
| still | boolean | false | Dibuja un solo cuadro. Pasa solo con prefers-reduced-motion. |
| className / style | - | Van al canvas. El canvas ocupa el 100% de su contenedor; dale alto al contenedor. |
weatherFor
| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| avg | number | - | Promedio de 1 a 5. |
| weekPulses | number | - | Pulsos de los últimos 7 días. Sin este dato no hay niebla. |
Cuándo sí y cuándo no
Hacé
- Acompañá siempre la escena con la palabra del estado: Soleado, Parcial, Nublado, Tormenta o Niebla.
- Usá
weatherForen lugar de copiar los umbrales.
Evitá
- No uses niebla para decir ánimo bajo. Niebla significa pocos datos.
- No inventes estados nuevos.
Accesibilidad
- Con
prefers-reduced-motion: reducedibuja un cuadro quieto y no anima. - Fuera de pantalla se pausa (IntersectionObserver), así no gasta batería.
- El canvas es
aria-hidden: es decoración. Si transmite algo, decilo también en texto.