Command Search
Search for a page
/ Components / Dark Social Platform Grid

Dark Social Platform Grid

Social

Dark, glowing card grid of social platform links with per-platform gradients, a mouse-follow spotlight, and a gradient CTA button.

socialgriddarkglowanimation
Open preview
Live Preview
AI Generation Prompt

Create a Svelte 5 (runes) dark-themed social-links showcase. Full-bleed dark gradient section (slate-950 via slate-900) with layered decorative background: two large radial-gradient blobs (top-right / bottom-left), a subtle grid-line pattern masked to fade at the edges, and two blurred pulsing 'orb' blobs. A centered header with a small gradient pill badge, a large gradient-clip heading, and gray subtext, all fading/sliding in on mount via a CSS class toggled after an `$effect` sets `isLoaded = true`. Below, a 2/3-column grid of glassy cards (`backdrop-blur`, translucent slate background, subtle border), each staggered-fade-in on mount with a per-index transition delay. Each card: a per-platform gradient (looked up from a `platform` key, e.g. linkedin=blue, github=gray, instagram=purple/pink/orange), an icon badge that rotates+scales on hover, platform name + description, a 'Connect' label with an arrow that translates on hover, plus a hover-triggered radial glow tinted to the platform's color and a diagonal shimmer sweep. A mouse-tracked soft radial light also follows the cursor across the whole section (via `fixed` positioning updated from `mousemove`). Bottom CTA: a gradient pill button with an arrow icon and its own shimmer sweep. Data-driven via `content: { badge, heading, subtext, cta_label, cta_href, platforms: {name, platform, link, description}[] }`.

<script lang="ts">
	import { cn } from '$lib/utils';

	type Platform = { name: string; platform: string; link: string; description: string };
	type Props = {
		content: {
			badge: string;
			heading: string;
			subtext: string;
			cta_label: string;
			cta_href: string;
			platforms: Platform[];
		};
	};
	let { content }: Props = $props();

	const platformStyles: Record<string, { gradient: string; shadowColor: string }> = {
		linkedin:  { gradient: 'from-blue-600 to-blue-400',                    shadowColor: 'rgba(59,130,246,0.5)' },
		github:    { gradient: 'from-gray-700 to-gray-500',                    shadowColor: 'rgba(75,85,99,0.5)' },
		twitter:   { gradient: 'from-slate-800 to-slate-600',                  shadowColor: 'rgba(51,65,85,0.5)' },
		x:         { gradient: 'from-slate-800 to-slate-600',                  shadowColor: 'rgba(51,65,85,0.5)' },
		instagram: { gradient: 'from-purple-600 via-pink-600 to-orange-500',   shadowColor: 'rgba(219,39,119,0.5)' },
		discord:   { gradient: 'from-indigo-600 to-purple-600',                shadowColor: 'rgba(99,102,241,0.5)' },
		youtube:   { gradient: 'from-red-600 to-red-400',                      shadowColor: 'rgba(239,68,68,0.5)' },
		facebook:  { gradient: 'from-blue-700 to-blue-500',                    shadowColor: 'rgba(37,99,235,0.5)' },
	};

	// Inline SVG paths — Lucide removed all brand icons
	const socialPaths: Record<string, string> = {
		linkedin:  `<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/>`,
		github:    `<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>`,
		twitter:   `<path d="M8.29 20.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0022 5.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-6.993 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.072 4.072 0 012.8 9.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 012 18.407a11.616 11.616 0 006.29 1.84"/>`,
		x:         `<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>`,
		instagram: `<path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z"/>`,
		discord:   `<path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419-.0189 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1568 2.4189Z"/>`,
		youtube:   `<path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/>`,
		facebook:  `<path fill-rule="evenodd" d="M22 12c0-5.523-4.477-10-10-10S2 6.477 2 12c0 4.991 3.657 9.128 8.438 9.878v-6.987h-2.54V12h2.54V9.797c0-2.506 1.492-3.89 3.777-3.89 1.094 0 2.238.195 2.238.195v2.46h-1.26c-1.243 0-1.63.771-1.63 1.562V12h2.773l-.443 2.89h-2.33v6.988C18.343 21.128 22 16.991 22 12z" clip-rule="evenodd"/>`,
	};

	let isLoaded = $state(false);
	let mouseX = $state(0);
	let mouseY = $state(0);

	$effect(() => {
		isLoaded = true;
		const onMove = (e: MouseEvent) => { mouseX = e.clientX; mouseY = e.clientY; };
		window.addEventListener('mousemove', onMove);
		return () => window.removeEventListener('mousemove', onMove);
	});
</script>

<section class="relative w-full overflow-hidden bg-gradient-to-br from-slate-950 via-slate-900 to-slate-950">
	<!-- Animated background layers -->
	<div class="absolute inset-0 pointer-events-none">
		<div class="absolute inset-0 bg-[radial-gradient(ellipse_at_top_right,_var(--tw-gradient-stops))] from-blue-900/20 via-transparent to-transparent"></div>
		<div class="absolute inset-0 bg-[radial-gradient(ellipse_at_bottom_left,_var(--tw-gradient-stops))] from-purple-900/20 via-transparent to-transparent"></div>
		<!-- Grid lines -->
		<div class="absolute inset-0 bg-[linear-gradient(to_right,#1e293b_1px,transparent_1px),linear-gradient(to_bottom,#1e293b_1px,transparent_1px)] bg-[size:4rem_4rem] [mask-image:radial-gradient(ellipse_60%_50%_at_50%_0%,#000_70%,transparent_100%)]"></div>
		<!-- Floating orbs -->
		<div class="absolute top-20 left-20 w-72 h-72 bg-blue-600/30 rounded-full blur-[128px] animate-pulse"></div>
		<div class="absolute bottom-20 right-20 w-96 h-96 bg-purple-600/20 rounded-full blur-[128px] animate-pulse [animation-delay:700ms]"></div>
	</div>

	<!-- Content -->
	<div class="relative flex flex-col items-center py-24 px-4 lg:px-6">

		<!-- Header -->
		<div class={cn(
			'text-center mb-16 transition-all duration-1000',
			isLoaded ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-10'
		)}>
			<div class="inline-block mb-4 px-4 py-1.5 bg-gradient-to-r from-blue-500/10 to-purple-500/10 rounded-full border border-blue-500/20">
				<span class="text-sm font-medium bg-gradient-to-r from-blue-400 to-purple-400 bg-clip-text text-transparent">
					{content.badge}
				</span>
			</div>
			<h2 class="text-5xl md:text-6xl font-bold mb-6 tracking-tight bg-gradient-to-br from-white via-white to-gray-400 bg-clip-text text-transparent">
				{content.heading}
			</h2>
			<p class="text-lg md:text-xl text-gray-400 max-w-2xl mx-auto leading-relaxed">
				{content.subtext}
			</p>
		</div>

		<!-- Platform cards -->
		<div class="grid grid-cols-2 md:grid-cols-3 gap-6 w-full max-w-5xl mx-auto">
			{#each content.platforms as platform, index}
				{@const style = platformStyles[platform.platform] ?? { gradient: 'from-slate-700 to-slate-500', shadowColor: 'rgba(100,100,100,0.4)' }}
				{@const path = socialPaths[platform.platform]}
				<a
					href={platform.link}
					target="_blank"
					rel="noopener noreferrer"
					class={cn(
						'group relative transition-all duration-700',
						isLoaded ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-10'
					)}
					style="transition-delay: {index * 100}ms"
				>
					<!-- Card -->
					<div class="relative bg-gradient-to-br from-slate-800/50 to-slate-900/50 backdrop-blur-xl rounded-2xl p-8 border border-slate-700/50 overflow-hidden transition-all duration-500 hover:scale-105 hover:border-slate-600/50">
						<!-- Hover gradient tint -->
						<div class="absolute inset-0 bg-gradient-to-br {style.gradient} opacity-0 group-hover:opacity-10 transition-opacity duration-500"></div>

						<!-- Glow -->
						<div
							class="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-500 blur-[40px]"
							style="background: radial-gradient(circle at 50% 50%, {style.shadowColor}, transparent 70%)"
						></div>

						<!-- Content -->
						<div class="relative z-10">
							<!-- Icon badge -->
							<div class="mb-4 inline-flex p-3 rounded-xl bg-gradient-to-br {style.gradient} text-white transition-transform duration-500 group-hover:rotate-6 group-hover:scale-110">
								{#if path}
									<!-- eslint-disable-next-line svelte/no-at-html-tags -->
									<svg viewBox="0 0 24 24" fill="currentColor" class="w-7 h-7" aria-hidden="true">{@html path}</svg>
								{/if}
							</div>

							<h3 class="text-white font-semibold text-lg mb-1">{platform.name}</h3>
							<p class="text-gray-500 text-sm group-hover:text-gray-400 transition-colors duration-300">{platform.description}</p>

							<!-- Connect arrow -->
							<div class="mt-4 flex items-center text-gray-600 group-hover:text-white transition-colors duration-300">
								<span class="text-sm font-medium">Connect</span>
								<svg class="w-4 h-4 ml-2 transition-transform duration-300 group-hover:translate-x-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
									<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8l4 4m0 0l-4 4m4-4H3" />
								</svg>
							</div>
						</div>

						<!-- Shimmer sweep -->
						<div class="absolute inset-0 -translate-x-full group-hover:translate-x-full transition-transform duration-1000 bg-gradient-to-r from-transparent via-white/5 to-transparent"></div>
					</div>
				</a>
			{/each}
		</div>

		<!-- CTA -->
		<div class={cn(
			'mt-16 text-center transition-all duration-1000 delay-700',
			isLoaded ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-10'
		)}>
			<a
				href={content.cta_href}
				class="group relative inline-flex items-center gap-2 px-8 py-3 bg-gradient-to-r from-blue-600 to-purple-600 rounded-full text-white font-medium overflow-hidden transition-all duration-300 hover:scale-105 hover:shadow-lg hover:shadow-purple-500/25"
			>
				<span class="relative z-10">{content.cta_label}</span>
				<svg class="w-4 h-4 relative z-10 transition-transform duration-300 group-hover:translate-x-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
					<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7l5 5m0 0l-5 5m5-5H6" />
				</svg>
				<!-- Button shimmer -->
				<div class="absolute inset-0 -translate-x-full group-hover:translate-x-full transition-transform duration-700 bg-gradient-to-r from-transparent via-white/20 to-transparent"></div>
			</a>
		</div>
	</div>

	<!-- Mouse-follow light -->
	<div
		class="pointer-events-none fixed size-96 rounded-full opacity-20 blur-[100px] transition-all duration-200 ease-out z-0"
		style="background: radial-gradient(circle, rgba(147,51,234,0.3), transparent); left: {mouseX - 192}px; top: {mouseY - 192}px;"
	></div>
</section>