11---
22import Layout from ' ../layouts/Layout.astro'
33import ' @fontsource-variable/jetbrains-mono'
4+ import { SPONSORS_EMAIL } from ' ./constants'
45---
56
67<Layout title =" PyConES 2026" >
78 <main class =" min-h-screen flex flex-col justify-center items-center text-center px-4" >
89 <div class =" relative z-10 max-w-3xl" >
910 <h1
1011 class =" text-4xl md:text-7xl font-bold text-white font-mono tracking-tighter cursor-default"
11- data-value = " PYCON ES 2026"
12+ aria-label = " PyCon ES 2026"
1213 >
13- PYCON ES 2026
14+ < span aria-hidden = " true " data-value = " PYCON ES 2026" id = " matrix-text " > PYCON ES 2026 </ span >
1415 </h1 >
1516
16- <h2 class =" mt-4 text-xl md:text-2xl text-green-400 font-mono h-8" >
17- > <span id =" subtitle" >Initialising system...</span ><span class =" animate-pulse" >_</span >
17+ <h2
18+ id =" subtitle-container"
19+ class =" mt-4 text-xl md:text-2xl text-green-400 font-mono h-8 flex justify-center items-center gap-2"
20+ aria-live =" polite"
21+ aria-busy =" true"
22+ >
23+ <span aria-hidden =" true" class =" select-none" >> </span >
24+ <span id =" subtitle" >Initialising system...</span >
25+ <span class =" animate-pulse motion-reduce:animate-none" aria-hidden =" true" >_</span >
1826 </h2 >
1927
2028 <div
@@ -23,17 +31,34 @@ import '@fontsource-variable/jetbrains-mono'
2331 >
2432 <button
2533 id =" sponsor-btn"
26- 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"
34+ type =" button"
35+ data-email ={ SPONSORS_EMAIL }
36+ aria-describedby =" sponsor-hint"
37+ 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"
2738 >
2839 <span
2940 class =" absolute inset-0 w-full h-full bg-white opacity-0 group-hover:opacity-20 transition-opacity pointer-events-none"
3041 ></span >
31- <span id =" sponsor-text" >< BECOME A SPONSOR /> </span >
42+
43+ <span id =" sponsor-text" >
44+ <span aria-hidden =" true" >< </span lang='en'>BECOME A SPONSOR<span aria-hidden =" true" > /> </span >
45+ </span >
46+ <span id =" sponsor-hint" class =" sr-only" >Copia el email de contacto para patrocinadores</span >
3247 </button >
48+
49+ <div
50+ id =" copy-confirmation"
51+ role =" status"
52+ tabindex =" -1"
53+ class =" hidden px-7 py-2 text-green-400 font-mono font-bold text-lg rounded-sm"
54+ >
55+ <span aria-hidden =" true" > < !-- </span lang='en'>EMAIL COPIED!<span aria-hidden =" true" > --> </span >
56+ </div >
3357 </div >
3458 </div >
3559
3660 <div
61+ aria-hidden =" true"
3762 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"
3863 >
3964 </div >
@@ -46,10 +71,11 @@ import '@fontsource-variable/jetbrains-mono'
4671 let interval: number | null = null
4772
4873 const runMatrixEffect = (element: HTMLElement) => {
49- let iteration = 0
74+ const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches
75+ if (prefersReducedMotion) return
5076
77+ let iteration = 0
5178 clearInterval(interval as number)
52-
5379 const originalText = element.dataset.value || ''
5480
5581 interval = window.setInterval(() => {
@@ -72,44 +98,58 @@ import '@fontsource-variable/jetbrains-mono'
7298 }
7399
74100 const init = () => {
101+ const matrixText = document.getElementById('matrix-text')
75102 const h1 = document.querySelector('h1')
76103 const subtitle = document.querySelector('#subtitle')
77104 const actions = document.querySelector('#actions')
78105
79- if (h1) {
80- runMatrixEffect(h1 )
81- h1.onmouseover = () => runMatrixEffect(h1 )
106+ if (matrixText && h1) {
107+ runMatrixEffect(matrixText )
108+ h1.onmouseover = () => runMatrixEffect(matrixText )
82109 }
83110
84111 if (subtitle) {
85112 setTimeout(() => {
86113 subtitle.textContent = 'Sede UB Barcelona | 6-8 Nov 2026'
114+ const container = document.getElementById('subtitle-container')
115+ if (container) {
116+ container.setAttribute('aria-busy', 'false')
117+ }
87118 if (actions) {
88119 actions.classList.remove('opacity-0')
89120 }
90121 }, 1500)
91122 }
92123
93- //COPY MAIL
124+ // COPY MAIL LOGIC
94125 const sponsorBtn = document.getElementById('sponsor-btn')
95- const sponsorText = document.getElementById('sponsor-text')
126+ const copyConfirmation = document.getElementById('copy-confirmation')
127+ const email = sponsorBtn?.dataset.email as string
96128
97- if (sponsorBtn && sponsorText ) {
129+ if (sponsorBtn && copyConfirmation && email ) {
98130 sponsorBtn.onclick = async () => {
99131 try {
100- await navigator.clipboard.writeText('sponsors@2026.es.pycon.org')
101- const originalText = sponsorText.innerText
102- sponsorText.innerText = '[ EMAIL COPIED! ]'
132+ await navigator.clipboard.writeText(email)
133+
134+ // Hide button, show confirmation
135+ sponsorBtn.classList.add('hidden')
136+ copyConfirmation.classList.remove('hidden')
137+ copyConfirmation.focus()
138+
103139 setTimeout(() => {
104- sponsorText.innerText = originalText
140+ // Hide confirmation, show button and restore focus
141+ copyConfirmation.classList.add('hidden')
142+ sponsorBtn.classList.remove('hidden')
143+ sponsorBtn.focus()
105144 }, 2000)
106145 } catch (err) {
107146 console.error('Failed to copy', err)
108- window.location.href = ' mailto:sponsors@2026.es.pycon.org'
147+ window.location.href = ` mailto:${email}`
109148 }
110149 }
111150 }
112151 }
152+
113153 //not needed now
114154 document.addEventListener('astro:page-load', init)
115155 init()
0 commit comments