Meteors
Pluie de meteores decorative avec des trainees qui tombent en arriere-plan. Installez Meteors depuis le registre VLLNT UI avec la CLI shadcn.
Pluie de meteores decorative avec des trainees qui tombent en arriere-plan. Fait partie de la famille utility dans VLLNT UI, il est publie comme entree de registre lisible par machine — copiez la source directement dans votre application avec la CLI shadcn et possedez-la, sans dependance d'execution a une bibliotheque de composants.
Apercu
Basculez entre clair et sombre pour inspecter l'apercu Storybook integre.
Installation
Ajoutez Meteors a votre projet avec la CLI shadcn. La source arrive dans votre code, prete a etre adaptee :
pnpm dlx shadcn@latest add https://ui.vllnt.ai/r/meteors.jsonSource
"use client";
import * as React from "react";
import { cn } from "../../lib/utils";
/** Props for {@link Meteors}. */
export type MeteorsProps = React.ComponentPropsWithoutRef<"div"> & {
/** Count of meteors streaking across the field. Defaults to `12`. */
count?: number;
};
type Meteor = {
delay: number;
duration: number;
left: number;
top: number;
};
function createMeteors(count: number): Meteor[] {
return Array.from({ length: count }, () => ({
delay: Math.random(),
duration: 2 + Math.random() * 4,
left: Math.random() * 100,
top: -(Math.random() * 10),
}));
}
type MeteorStyle = React.CSSProperties & {
"--vllnt-meteor-angle"?: string;
};
function meteorStyle(meteor: Meteor): MeteorStyle {
return {
"--vllnt-meteor-angle": "215deg",
animation: `vllnt-meteor ${meteor.duration}s linear infinite`,
animationDelay: `${meteor.delay}s`,
left: `${meteor.left}%`,
top: `${meteor.top}%`,
};
}
/**
* Decorative shower of meteors falling diagonally across the field.
*
* Respects `prefers-reduced-motion`: the meteors stay parked.
*
* @example
* ```tsx
* <Meteors count={20} />
* ```
*/
export const Meteors = ({
className,
count = 12,
ref,
...props
}: MeteorsProps & { ref?: React.Ref<HTMLDivElement> }) => {
const [meteors] = React.useState(() => createMeteors(count));
return (
<div
aria-hidden="true"
className={cn(
"pointer-events-none absolute inset-0 overflow-hidden",
className,
)}
ref={ref}
{...props}
>
{meteors.map((meteor, index) => (
<span
className="absolute h-0.5 w-0.5 rounded-full bg-muted-foreground motion-reduce:animate-none"
key={index}
style={meteorStyle(meteor)}
>
<span
className="absolute right-0 top-1/2 h-px w-12 -translate-y-1/2"
style={{
background:
"linear-gradient(90deg, oklch(var(--muted-foreground)), transparent)",
}}
/>
</span>
))}
</div>
);
};
Meteors.displayName = "Meteors";
Stories
Explorez chaque variante et etat dans le Storybook interactif :
Apercu
Basculez entre clair et sombre pour inspecter l'apercu Storybook integre.