Halloween Rave 2026 - Scooter @ Ergo Arena, Gdańsk

Promo web pro elektronický festival se Scooter v hlavní roli. Tmavý festivalový aesthetic, lineup sekce, countdown, ticketing. Cílí na polský electronic music market.

Next.jsTypeScriptTailwind CSSMotionVercel

Scooter + 4 DJs

Brief

Halloween Rave 2026 je elektronický festival v Ergo Arena (Gdańsk/Sopot) 30. října 2026. Headliner Scooter, support DJ Antoine, Merlin, Vanesa Hardt a Doom. Promotér chtěl marketingový web cílený výhradně na polský trh - jedna locale, jedna doména, jedno publikum: rave a hard-dance fanoušci 25–40 let, kteří objevují akce přes Instagram, Tikiok a Google "scooter koncert 2026". Prodej běží přes lokálního ticketingového partnera, web je konverzní funnel.

Termín od kickoffu k live byl 9 dní. Promotér měl připravené hero fotky, lineup grafiku a venue plán. Já jsem dodal stack, frontend, integraci s ticketingem a SEO setup.

Proč dark mode + neon

Halloween rave fanoušek se v sobotu večer scrolluje na mobilu v posteli. Bílé světlo z light mode je doslova fyzicky nepříjemné a estetiku rave kultury naprosto vraždí. Šel jsem do near-black background (#0A0A0F), s neon akcenty v jedovatě zelené (#39FF14), purple (#9D00FF) a UV-magenta (#FF00C8). Geist Mono pro counts, Inter pro běžný text, vysoký kontrast pro accessibility (AA pro běžný text, AAA pro CTA).

Paleta není dekorace - je to signál pro publikum. "Tahle akce je pro tebe" musí být patrné v 0.4 s, jinak swipe pryč.

Lineup grid + Motion

Lineup je hrdina stránky. Postavil jsem grid s entrance animací postupně odkrývající 5 artistů přes motion:

import { motion } from 'motion/react';
 
const stagger = { animate: { transition: { staggerChildren: 0.08 } } };
const fadeUp = {
  initial: { opacity: 0, y: 24, filter: 'blur(8px)' },
  animate: { opacity: 1, y: 0, filter: 'blur(0px)', transition: { duration: 0.5 } },
};
 
export function LineupGrid({ artists }: { artists: Artist[] }) {
  return (
    <motion.ul
      variants={stagger}
      initial="initial"
      whileInView="animate"
      viewport={{ once: true, amount: 0.3 }}
      className="grid grid-cols-2 gap-4 md:grid-cols-3"
    >
      {artists.map((a) => (
        <motion.li key={a.slug} variants={fadeUp}>
          <ArtistCard artist={a} />
        </motion.li>
      ))}
    </motion.ul>
  );
}

viewport={{ once: true }} brání retriggrům při scroll up/down. staggerChildren: 0.08 dá 5 kartám 400 ms celkovou animaci - citelnou, ale ne otravnou.

Countdown timer s pause-on-blur

Countdown je hero element. Klasická setInterval implementace na mobilu skáká když se tab uspí (Safari throttluje na 1× za 60 s). Šel jsem do requestAnimationFrame + Page Visibility API:

'use client';
 
import { useEffect, useState } from 'react';
 
const TARGET = new Date('2026-10-30T20:00:00+01:00').getTime();
 
export function Countdown() {
  const [remaining, setRemaining] = useState(() => TARGET - Date.now());
 
  useEffect(() => {
    let raf = 0;
    let lastTick = performance.now();
 
    const tick = (now: number) => {
      if (document.hidden) {
        // tab je schovaný - nepočítáme, šetříme baterku
        raf = requestAnimationFrame(tick);
        return;
      }
      if (now - lastTick >= 1000) {
        lastTick = now;
        setRemaining(TARGET - Date.now());
      }
      raf = requestAnimationFrame(tick);
    };
 
    raf = requestAnimationFrame(tick);
    const onVis = () => {
      // resync hned po návratu, jinak countdown vykazuje stará data
      lastTick = performance.now();
      setRemaining(TARGET - Date.now());
    };
    document.addEventListener('visibilitychange', onVis);
 
    return () => {
      cancelAnimationFrame(raf);
      document.removeEventListener('visibilitychange', onVis);
    };
  }, []);
 
  return <CountdownDigits ms={Math.max(0, remaining)} />;
}

Naměřeno: pause-on-blur ušetřilo na low-end Androidu ~3–5 % baterky/hodinu, což je u festival-promo stránky, kde lidi mají tab otevřený dlouho, znát.

Ticketing CTA polymorfismus

Polský vendor používá vlastní embed/iframe + deeplink scheme. Místo abych psal speciální komponentu, abstraktoval jsem to:

type Vendor = 'eBilet' | 'goingApp' | 'kupBilecik';
 
interface TicketProvider {
  buyUrl(eventId: string, tier?: string): string;
  trackingId: string;
}

CTA komponenta dostane vendor a vykreslí správný href + analytics tracking. Když promotér přidá další ticketingového partnera, je to 5 řádků v provideru, ne refaktor stránky.

SEO play

Polský search market je dominantní Google (~95 %), ale Bing má v některých demografiích 5–7 %. Cílil jsem na long-tail KW:

Klíčové slovoMěs. objemPozice po launchi (T+14)
halloween rave gdańsk1 200#2
scooter koncert 20264 800#4
ergo arena halloween320#1
bilety halloween rave880#3

Strukturovaná data (MusicEvent schema.org), open graph s neon hero, sitemap.xml + robots.txt s explicitním povolením. Hreflang ne - site je polish-only, žádný locale switch.

Asset handling

Hero fotky z laser show jsou krásné, ale 6 MB JPEG. Next/Image dělá zázraky:

FotkaOrigin (JPEG)AVIF servedÚspora
Hero (1920×1080)5.8 MB290 kB95 %
Lineup card (600×600)480 kB32 kB93 %
Galerie thumb (400×400)220 kB18 kB92 %

Galerie pod foldem je lazy-loaded přes loading="lazy" + IntersectionObserver. LCP na mobilu 4G 1.6 s, na desktopu 0.9 s. PageSpeed Insights mobile 94/100.

Lessons

  • Dark mode není feature, je to signál. Pro tuhle audience byla paleta load-bearing dramaturgická volba, ne kosmetika.
  • Motion je dvojsečný meč. Lineup stagger funguje, ale původní verze měla scroll-jacking pro hero - to jsem zahodil. Polish-market uživatelé scrollují rychle a smooth-scroll-hijack je odpuzuje.
  • Vendor button má vlastní A/B test. "Kup vstupenku" vs "Sehnat lístek" vs "Bilety" - Bilety (polsky) konvertoval o 18 % víc než ostatní. Lokalizace včetně CTA copy se nezanedbává.
  • Promotér chtěl carousel, dal jsem grid. Na mobilu carousely zabíjejí konverzi (jen ~30 % uživatelů swipne dál než první slide). Statický grid s celý lineup hned viditelný měl 2× CTR na artist card.