Code for my personal website

feat: just some fun stuff lol

+115
+2
package.json
··· 22 22 "@fontsource/lora": "^5.1.0", 23 23 "@tailwindcss/typography": "^0.5.15", 24 24 "astro": "^4.15.6", 25 + "canvas-confetti": "^1.9.3", 25 26 "clsx": "^2.1.1", 26 27 "sharp": "^0.33.5", 27 28 "tailwind-merge": "^2.5.2", ··· 29 30 "typescript": "^5.6.2" 30 31 }, 31 32 "devDependencies": { 33 + "@types/canvas-confetti": "^1.6.4", 32 34 "@typescript-eslint/eslint-plugin": "^8.5.0", 33 35 "@typescript-eslint/parser": "^8.5.0", 34 36 "eslint": "^9.10.0",
+14
pnpm-lock.yaml
··· 32 32 astro: 33 33 specifier: ^4.15.6 34 34 version: 4.15.6(typescript@5.6.2) 35 + canvas-confetti: 36 + specifier: ^1.9.3 37 + version: 1.9.3 35 38 clsx: 36 39 specifier: ^2.1.1 37 40 version: 2.1.1 ··· 49 52 version: 5.6.2 50 53 51 54 devDependencies: 55 + '@types/canvas-confetti': 56 + specifier: ^1.6.4 57 + version: 1.6.4 52 58 '@typescript-eslint/eslint-plugin': 53 59 specifier: ^8.5.0 54 60 version: 8.5.0(@typescript-eslint/parser@8.5.0)(eslint@9.10.0)(typescript@5.6.2) ··· 1302 1308 '@babel/types': 7.25.6 1303 1309 dev: false 1304 1310 1311 + /@types/canvas-confetti@1.6.4: 1312 + resolution: {integrity: sha512-fNyZ/Fdw/Y92X0vv7B+BD6ysHL4xVU5dJcgzgxLdGbn8O3PezZNIJpml44lKM0nsGur+o/6+NZbZeNTt00U1uA==} 1313 + dev: true 1314 + 1305 1315 /@types/cookie@0.6.0: 1306 1316 resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} 1307 1317 dev: false ··· 1973 1983 1974 1984 /caniuse-lite@1.0.30001660: 1975 1985 resolution: {integrity: sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg==} 1986 + dev: false 1987 + 1988 + /canvas-confetti@1.9.3: 1989 + resolution: {integrity: sha512-rFfTURMvmVEX1gyXFgn5QMn81bYk70qa0HLzcIOSVEyl57n6o9ItHeBtUSWdvKAPY0xlvBHno4/v3QPrT83q9g==} 1976 1990 dev: false 1977 1991 1978 1992 /ccount@2.0.1:
+97
src/components/Fun/Confetti.astro
··· 1 + --- 2 + 3 + --- 4 + 5 + <script> 6 + import confetti from 'canvas-confetti'; 7 + 8 + let typedKeys = ''; 9 + const confettiWord = 'confetti'; 10 + const trixaWord = 'trixa'; 11 + 12 + // Create a div for the Trixa message 13 + const trixaMessageDiv = document.createElement('div'); 14 + trixaMessageDiv.style.position = 'fixed'; 15 + trixaMessageDiv.style.top = '50%'; 16 + trixaMessageDiv.style.left = '50%'; 17 + trixaMessageDiv.style.transform = 'translate(-50%, -50%)'; 18 + trixaMessageDiv.style.fontSize = '4rem'; 19 + trixaMessageDiv.style.fontWeight = 'bold'; 20 + trixaMessageDiv.style.color = '#FF1493'; 21 + trixaMessageDiv.style.textAlign = 'center'; 22 + trixaMessageDiv.style.zIndex = '9999'; 23 + trixaMessageDiv.style.display = 'none'; 24 + document.body.appendChild(trixaMessageDiv); 25 + 26 + document.addEventListener('keydown', (event) => { 27 + if ( 28 + (event.target as Element)?.tagName.toLowerCase() === 'input' || 29 + (event.target as Element)?.tagName.toLowerCase() === 'textarea' 30 + ) { 31 + return; 32 + } 33 + 34 + typedKeys += event.key.toLowerCase(); 35 + 36 + // Keep only the last 8 characters (length of the longer word 'confetti') 37 + if (typedKeys.length > confettiWord.length) { 38 + typedKeys = typedKeys.slice(-confettiWord.length); 39 + } 40 + 41 + if (typedKeys.endsWith(confettiWord)) { 42 + confetti({ 43 + particleCount: 100, 44 + spread: 70, 45 + origin: { y: 0.6 } 46 + }); 47 + typedKeys = ''; 48 + } else if (typedKeys.endsWith(trixaWord)) { 49 + showTrixaMessage(); 50 + confetti({ 51 + particleCount: 100, 52 + spread: 70, 53 + origin: { y: 0.6 } 54 + }); 55 + typedKeys = ''; 56 + } 57 + }); 58 + 59 + function showTrixaMessage() { 60 + trixaMessageDiv.textContent = 'TRIXA IS THE BEST!'; 61 + trixaMessageDiv.style.display = 'block'; 62 + trixaMessageDiv.style.backgroundColor = 'rgba(255, 20, 147, 0.1)'; 63 + 64 + // Animate the message 65 + trixaMessageDiv.animate( 66 + [ 67 + { transform: 'translate(-50%, -50%) scale(0)', opacity: 0 }, 68 + { 69 + transform: 'translate(-50%, -50%) scale(1.2)', 70 + opacity: 1, 71 + offset: 0.8 72 + }, 73 + { transform: 'translate(-50%, -50%) scale(1)', opacity: 1 } 74 + ], 75 + { 76 + duration: 500, 77 + easing: 'ease-out' 78 + } 79 + ); 80 + 81 + // Hide the message after 3 seconds with a smooth exit animation 82 + setTimeout(() => { 83 + trixaMessageDiv.animate( 84 + [ 85 + { transform: 'translate(-50%, -50%) scale(1)', opacity: 1 }, 86 + { transform: 'translate(-50%, -50%) scale(0)', opacity: 0 } 87 + ], 88 + { 89 + duration: 500, 90 + easing: 'ease-in' 91 + } 92 + ).onfinish = () => { 93 + trixaMessageDiv.style.display = 'none'; 94 + }; 95 + }, 2000); 96 + } 97 + </script>
+2
src/layouts/Layout.astro
··· 1 1 --- 2 + import Confetti from '@/components/Fun/Confetti.astro'; 2 3 import Head from '@/components/Head.astro'; 3 4 import Header from '@/components/Header.astro'; 4 5 import Footer from '@/components/Footer.astro'; ··· 22 23 <main> 23 24 <slot /> 24 25 </main> 26 + <Confetti /> 25 27 <Footer /> 26 28 </body> 27 29 </html>