Glowing Stars

Card background stars that animate on hover and animate anyway

Svelte

The power of full-stack to the frontend. Read the release notes.

Installation

Install Dependencies

npm i svelte-motion clsx tailwind-merge

Add util file

src/lib/utils/cn.ts
import { type ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
        
export function cn(...inputs: ClassValue[]) {
    return twMerge(clsx(inputs));
}

Copy the source code

src/lib/components/ui/GlowingStars/Glow.svelte
<script lang="ts">
	import { Motion } from 'svelte-motion';
	export let delay: number;
</script>

<Motion
	let:motion
	initial={{
		opacity: 0
	}}
	animate={{
		opacity: 1
	}}
	transition={{
		duration: 2,
		ease: 'easeInOut',
		delay: delay
	}}
	exit={{
		opacity: 0
	}}
>
	<div
		use:motion
		class="absolute left-1/2 z-10 h-[4px] w-[4px] -translate-x-1/2 rounded-full bg-blue-500 shadow-2xl shadow-blue-400 blur-[1px]"
	/>
</Motion>
src/lib/components/ui/GlowingStars/GlowingStarsBackgroundCard.svelte
<script lang="ts">
	import { cn } from '@/utils';
	import Illustration from './Illustration.svelte';

	export let className: string | undefined = undefined;

	let mouseEnter = false;
</script>

<div
	on:mouseenter={() => (mouseEnter = true)}
	on:mouseleave={() => (mouseEnter = false)}
	class={cn(
		'h-full max-h-[20rem] w-full max-w-md rounded-xl border border-[#eaeaea] bg-[linear-gradient(110deg,#333_0.6%,#222)] p-4 dark:border-neutral-600',
		className
	)}
>
	<div class="flex items-center justify-center">
		<Illustration {mouseEnter} />
	</div>
	<div class="px-2 pb-6"><slot /></div>
</div>
src/lib/components/ui/GlowingStars/GlowingStarsDescription.svelte
<script lang="ts">
	import { cn } from '@/utils';

	export let className: string | undefined = undefined;
</script>

<p class={cn('max-w-[16rem] text-base text-white', className)}>
	<slot />
</p>
src/lib/components/ui/GlowingStars/GlowingStarsTitle.svelte
<script lang="ts">
	import { cn } from '@/utils';

	export let className: string | undefined = undefined;
</script>

<h2 class={cn('text-2xl font-bold text-[#eaeaea]', className)}>
	<slot />
</h2>
src/lib/components/ui/GlowingStars/Illustration.svelte
<script lang="ts">
	import { cn } from '@/utils';
	import Glow from './Glow.svelte';
	import Star from './Star.svelte';
	import { AnimatePresence } from 'svelte-motion';
	import { onDestroy, onMount } from 'svelte';
	import { writable } from 'svelte/store';

	export let mouseEnter: boolean;

	const stars = 108;
	const columns = 18;
	let glowingStars: number[] = [];

	// const highlightedStars = useRef<number[]>([]);
	const highlightedStars = writable<number[]>([]);

	onMount(() => {
		const interval = setInterval(() => {
			$highlightedStars = Array.from({ length: 5 }, () => Math.floor(Math.random() * stars));
			glowingStars = [...$highlightedStars];
		}, 3000);

		return () => clearInterval(interval);
	});
</script>

<div
	class="h-48 w-full p-1"
	style={`display: grid; grid-template-columns: repeat(${columns}, 1fr); gap: 1px;`}
>
	{#each [...Array(stars)] as star, starIdx (`matrix-col-${starIdx}}`)}
		{@const isGlowing = glowingStars.includes(starIdx)}
		{@const delay = (starIdx % 10) * 0.1}
		{@const staticDelay = starIdx * 0.01}

		<div class="relative flex items-center justify-center">
			<Star isGlowing={mouseEnter ? true : isGlowing} delay={mouseEnter ? staticDelay : delay} />
			{#if mouseEnter}
				<Glow delay={staticDelay} />
			{/if}
			<AnimatePresence show={true}>
				{#if isGlowing}
					<Glow {delay} />
				{/if}
			</AnimatePresence>
		</div>
	{/each}
</div>
src/lib/components/ui/GlowingStars/Star.svelte
<script lang="ts">
	import { cn } from '@/utils';
	import { Motion } from 'svelte-motion';

	export let isGlowing: boolean;
	export let delay: number;
</script>

<Motion
	let:motion
	initial={{
		scale: 1
	}}
	animate={{
		scale: isGlowing ? [1, 1.2, 2.5, 2.2, 1.5] : 1,
		background: isGlowing ? '#fff' : '#666'
	}}
	transition={{
		duration: 2,
		ease: 'easeInOut',
		delay: delay
	}}
>
	<div use:motion class={cn('relative z-20 h-[1px] w-[1px] rounded-full bg-[#666]')}></div>
</Motion>
src/lib/components/ui/GlowingStars/index.ts
import Glow from './Glow.svelte';
import GlowingStarsBackgroundCard from './GlowingStarsBackgroundCard.svelte';
import GlowingStarsDescription from './GlowingStarsDescription.svelte';
import GlowingStarsTitle from './GlowingStarsTitle.svelte';
import Illustration from './Illustration.svelte';
import Star from './Star.svelte';

export {
	Glow,
	GlowingStarsDescription,
	GlowingStarsBackgroundCard,
	GlowingStarsTitle,
	Illustration,
	Star
};

Props

Glow
Prop Type Description
delay number Delay between glowing.
GlowingStarsBackgroundCard
Prop Type Description
className string | undefined The class applied to the background.
GlowingStarsDescription
Prop Type Description
className string | undefined The class applied to the description.
GlowingStarsTitle
Prop Type Description
className string | undefined The class applied to the title.
Illustration
Prop Type Description
mouseEnter boolean Trigger true when the mouse enters.
Star
Prop Type Description
isGlowing boolean Set to true if the star should glow.
delay number The transition delay.