'use client'; import { useEffect } from 'react'; /** * CRITICAL COMPONENT: Loads Tailwind v4 CDN on client-side only * ⚠️ DO NOT REMOVE OR MODIFY WITHOUT CAREFUL TESTING ⚠️ * * This component prevents hydration mismatches by loading Tailwind CDN * only after React hydration is complete. It's essential for CodeSandbox * environments where we use Tailwind v4 CDN instead of compiled CSS. * * Without this component, you'll get hydration errors because server * and client would render different HTML/styles. * * NEVER load CDN styles in layout.tsx with conditional logic based on * process.env as this causes hydration mismatches. */ export function TailwindCDNClient() { useEffect(() => { // Show content immediately on Vercel (CSS is already compiled) if (process.env.NEXT_PUBLIC_VERCEL) { document.body.classList.add('css-loaded'); return; } // Only load CDN if not on Vercel if (!process.env.NEXT_PUBLIC_VERCEL) { // Check if already loaded const existingScript = document.querySelector('script[src*="@tailwindcss/browser"]'); const existingStyle = document.querySelector('style[type="text/tailwindcss"]'); if (!existingScript && !existingStyle) { // Add Tailwind config const style = document.createElement('style'); style.setAttribute('type', 'text/tailwindcss'); style.textContent = ` @theme { --radius: 0.625rem; --color-neutral-50: rgb(250 250 250); --color-neutral-100: rgb(245 245 245); --color-neutral-200: rgb(229 229 229); --color-neutral-300: rgb(212 212 212); --color-neutral-400: rgb(163 163 163); --color-neutral-500: rgb(115 115 115); --color-neutral-600: rgb(82 82 82); --color-neutral-700: rgb(64 64 64); --color-neutral-800: rgb(38 38 38); --color-neutral-900: rgb(23 23 23); --color-primary: #020022; --color-muted: rgb(82 82 82); --color-muted-dark: rgb(212 212 212); --animate-scroll: scroll var(--animation-duration, 40s) var(--animation-direction, forwards) linear infinite; --animate-marquee: marquee var(--marquee-duration) linear infinite; --animate-fade-in: fade-in 0.5s linear forwards; } @keyframes scroll { to { transform: translate(calc(-50% - 0.5rem)); } } @keyframes marquee { 100% { transform: translateY(-50%); } } @keyframes fade-in { from { opacity: 0; } to { opacity: 1; } } .shadow-derek { box-shadow: 0px 0px 0px 1px rgb(0 0 0 / 0.06), 0px 1px 1px -0.5px rgb(0 0 0 / 0.06), 0px 3px 3px -1.5px rgb(0 0 0 / 0.06), 0px 6px 6px -3px rgb(0 0 0 / 0.06), 0px 12px 12px -6px rgb(0 0 0 / 0.06), 0px 24px 24px -12px rgb(0 0 0 / 0.06); } .shadow-aceternity { box-shadow: 0px 2px 3px -1px rgba(0,0,0,0.1), 0px 1px 0px 0px rgba(25,28,33,0.02), 0px 0px 0px 1px rgba(25,28,33,0.08); } .bg-gradient-radial { background-image: radial-gradient(var(--tw-gradient-stops)); } .bg-gradient-conic { background-image: conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops)); } `; document.head.appendChild(style); // Add Tailwind script const script = document.createElement('script'); script.src = 'https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4'; script.onload = () => { // Show content when CDN is loaded and processed setTimeout(() => { document.body.classList.add('css-loaded'); }, 50); // Small delay for CDN to process styles }; document.head.appendChild(script); } } }, []); return null; }