|
| 1 | +--- |
| 2 | +import Layout from '../layouts/Layout.astro' |
| 3 | +import '@fontsource-variable/jetbrains-mono' |
| 4 | +import { texts } from '../i18n/home' |
| 5 | +import { getRelativeLocaleUrl } from 'astro:i18n' |
| 6 | +
|
| 7 | +interface Props { |
| 8 | + lang: string |
| 9 | +} |
| 10 | +
|
| 11 | +const { lang } = Astro.props |
| 12 | +const t = texts[lang as keyof typeof texts] |
| 13 | +--- |
| 14 | + |
| 15 | +<Layout title="PyConES 2026"> |
| 16 | + <main class="min-h-screen flex flex-col justify-center items-center text-center px-4"> |
| 17 | + <div class="relative z-10 max-w-3xl"> |
| 18 | + <h1 |
| 19 | + class="text-4xl md:text-7xl font-bold text-white font-mono tracking-tighter cursor-default" |
| 20 | + aria-label="PyConES 2026" |
| 21 | + > |
| 22 | + <span aria-hidden="true" data-value="PyConES 2026" id="matrix-text"> PyConES 2026 </span> |
| 23 | + </h1> |
| 24 | + |
| 25 | + <h2 |
| 26 | + id="subtitle-container" |
| 27 | + class="mt-4 text-xl md:text-2xl text-green-400 font-mono h-8 flex justify-center items-center gap-2" |
| 28 | + aria-live="polite" |
| 29 | + aria-busy="true" |
| 30 | + > |
| 31 | + <span aria-hidden="true" class="select-none">></span> |
| 32 | + <span id="subtitle" data-final-text={t['index.subtitle']}>{t['index.initializing']}</span> |
| 33 | + <span class="animate-pulse motion-reduce:animate-none" aria-hidden="true">_</span> |
| 34 | + </h2> |
| 35 | + |
| 36 | + <div |
| 37 | + id="actions" |
| 38 | + class="mt-12 flex flex-col md:flex-row gap-6 justify-center items-center opacity-0 transition-opacity duration-1000 ease-in" |
| 39 | + > |
| 40 | + <a href={getRelativeLocaleUrl(lang, 'sponsors')}> |
| 41 | + <button |
| 42 | + id="sponsor-btn" |
| 43 | + type="button" |
| 44 | + aria-describedby="sponsor-hint" |
| 45 | + class="group relative px-7 py-2 bg-green-500 text-black font-mono font-bold text-lg hover:bg-green-400 transition-all duration-300 shadow-[0_0_15px_rgba(34,197,94,0.5)] hover:shadow-[0_0_25px_rgba(34,197,94,0.8)] cursor-pointer focus:outline-none focus:ring-4 focus:ring-green-300/50 rounded-sm" |
| 46 | + > |
| 47 | + <span |
| 48 | + class="absolute inset-0 w-full h-full bg-white opacity-0 group-hover:opacity-20 transition-opacity pointer-events-none" |
| 49 | + ></span> |
| 50 | + |
| 51 | + <span id="sponsor-text"> |
| 52 | + <span aria-hidden="true">< </span><span lang="en">{t['index.sponsor_btn']}</span><span |
| 53 | + aria-hidden="true" |
| 54 | + > |
| 55 | + /></span |
| 56 | + > |
| 57 | + </span> |
| 58 | + </button> |
| 59 | + </a> |
| 60 | + </div> |
| 61 | + </div> |
| 62 | + |
| 63 | + <div |
| 64 | + aria-hidden="true" |
| 65 | + class="absolute inset-0 bg-[linear-gradient(to_right,#80808012_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-[size:24px_24px] pointer-events-none" |
| 66 | + > |
| 67 | + </div> |
| 68 | + </main> |
| 69 | +</Layout> |
| 70 | + |
| 71 | +<script> |
| 72 | + const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+' |
| 73 | + |
| 74 | + let interval: number | null = null |
| 75 | + |
| 76 | + const runMatrixEffect = (element: HTMLElement) => { |
| 77 | + const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches |
| 78 | + if (prefersReducedMotion) return |
| 79 | + |
| 80 | + let iteration = 0 |
| 81 | + clearInterval(interval as number) |
| 82 | + const originalText = element.dataset.value || '' |
| 83 | + |
| 84 | + interval = window.setInterval(() => { |
| 85 | + element.innerText = originalText |
| 86 | + .split('') |
| 87 | + .map((_, index) => { |
| 88 | + if (index < iteration) { |
| 89 | + return originalText[index] |
| 90 | + } |
| 91 | + return letters[Math.floor(Math.random() * letters.length)] |
| 92 | + }) |
| 93 | + .join('') |
| 94 | + |
| 95 | + if (iteration >= originalText.length) { |
| 96 | + clearInterval(interval as number) |
| 97 | + } |
| 98 | + |
| 99 | + iteration += 1 / 3 |
| 100 | + }, 30) |
| 101 | + } |
| 102 | + |
| 103 | + const init = () => { |
| 104 | + const matrixText = document.getElementById('matrix-text') |
| 105 | + const h1 = document.querySelector('h1') |
| 106 | + const subtitle = document.getElementById('subtitle') |
| 107 | + const actions = document.querySelector('#actions') |
| 108 | + |
| 109 | + if (matrixText && h1) { |
| 110 | + runMatrixEffect(matrixText) |
| 111 | + h1.onmouseover = () => runMatrixEffect(matrixText) |
| 112 | + } |
| 113 | + |
| 114 | + if (subtitle) { |
| 115 | + setTimeout(() => { |
| 116 | + const finalText = subtitle.dataset.finalText |
| 117 | + if (finalText) { |
| 118 | + subtitle.textContent = finalText |
| 119 | + } |
| 120 | + const container = document.getElementById('subtitle-container') |
| 121 | + if (container) { |
| 122 | + container.setAttribute('aria-busy', 'false') |
| 123 | + } |
| 124 | + if (actions) { |
| 125 | + actions.classList.remove('opacity-0') |
| 126 | + } |
| 127 | + }, 1500) |
| 128 | + } |
| 129 | + } |
| 130 | + |
| 131 | + //not needed now |
| 132 | + document.addEventListener('astro:page-load', init) |
| 133 | + init() |
| 134 | +</script> |
0 commit comments