szamowski.dev
  • Home
  • Selected Works
  • Blog
  • Contact

Designed & Developed
by NA SERIO Maciej Szamowski

From Poland with
EU VAT: PL5222864821

© 2026. All rights reserved.

Navigation

  • Home
  • Selected Works
  • Blog
  • Contact

Connect

  • LinkedIn
  • GitHub
  • WhatsApp
From One-Pager to Powerhouse - Rebuilding Copa City's Web Presence
February 22, 2026· Maciej Szamowski

From One-Pager to Powerhouse - Rebuilding Copa City's Web Presence

Next.jsStoryblokTailwindCSSPerformancei18nVercel

The Starting Point

The problems were structural. The blog section existed, technically, but it was buried - no pagination, no tags, no archives, no way for anyone to actually discover content organically. Mobile experience was an afterthought. The CMS structure had grown organically to the point where the marketing team needed a developer (me) for every content change. With nine language versions and a game approaching new milestones, it was time to rebuild from scratch.

And I do mean scratch.

The Plan

I knew going in that this wasn't a reskin. The client wanted a modern gaming website that would appeal to both football fans and gamers - dark, immersive, with beautiful gradients and real atmosphere. They were also developing new key visuals, so the design language needed room to evolve.

The tech stack decision was straightforward:

  • Next.js 15 with React 19 - App Router, Server Components, Turbopack for dev builds

  • TailwindCSS v4 - the latest, with PostCSS plugin integration

  • Storyblok - headless CMS, but restructured properly this time

  • next-intl - for routing and translations across all nine languages from day one

  • Framer Motion - for scroll-triggered animations (though I'd later learn to be selective about where)

  • Vercel - deployed to Frankfurt for EU-optimized performance

The workflow was iterative by design: build a section, test it with Playwright, get client feedback, refine. Claude Code served as a capable pair-programming partner throughout - handling boilerplate, catching edge cases, and accelerating the structural work that would have taken days manually. But creative direction, architecture decisions, and design iterations were mine.

Nine languages from the ground up - English, German, Spanish, French, Polish, Portuguese (BR), Chinese, Russian, and Turkish. Not bolted on afterward. Baked into routing, CMS structure, and every component from the first commit.

Phase 1: The Home Page

The home page is fifteen sections deep. Each one maps to a Storyblok blok - hero, wishlist CTA, platform covers, trailer, stadiums, clubs, features, game modes, and more. In total, I built 48 components following Atomic Design: 11 atoms, 8 molecules, 14 organisms, and 15 Storyblok bloks that tie everything to the CMS.

The Color Journey

This is where design iteration really mattered. The first version leaned into Copa City's bright blue brand palette - vivid, energetic, unapologetically colorful.

The client liked the structure but wanted something darker, more atmospheric. So we pivoted. V2 introduced deeper tones. V3 went full dark navy with blue accents - and that's when it clicked.

Three rounds of color iteration to get there. Lesson learned: don't fall in love with your first palette.

The Atmosphere

A gaming website needs to feel alive. Two canvas-based effects carry the visual weight:

Confetti Canvas - a custom WebGL implementation with vertex and fragment shaders. Forty to one hundred particles depending on viewport, each with 3D tumble physics, multi-layer sway, and a flutter effect that mimics paper catching air. Brand colors only - teal, blue, deep blue, white. Motion trails on desktop, disabled on mobile.

Smoke Background - multi-layer radial gradients composited in "lighter" blend mode. Three overlapping gradients per particle for irregular, organic shapes. Lazy-loaded via IntersectionObserver so it doesn't touch performance until it's near the viewport.

Phase 2: Blog, Contact & CMS

The Blog System

The blog was the biggest gap in the original site. I built it with four distinct routes:

  • /blog - paginated listing with five posts per page

  • /blog/[slug] - individual posts with rich text, related articles, and social sharing

  • /blog/tag/[tag] - tag-filtered views

  • /blog/archive/[year]/[month] - monthly archives for SEO crawlability

Each post lives entirely in Storyblok: title, rich text body, tags, featured image, author, publication date, related posts, and full SEO metadata. The sidebar surfaces tags and archive months with post counts. Reading time is calculated at 200 words per minute from the rich text content.

The Contact Form

Three contact categories routing to three different email addresses - general inquiries, business development, and press - each with full Zod validation:

  • Name: 2–100 characters

  • Email: validated format

  • Subject: 3–200 characters

  • Message: 10–5,000 characters

  • Honeypot field for bot protection

  • Rate limiting: 3 requests per 15 minutes per IP

The backend uses Resend for delivery, with branded HTML templates matching Copa City's color scheme. React Hook Form handles the frontend with Framer Motion for the modal. Success auto-closes after 2.5 seconds.

Cookie Consent

I replaced the third-party Cookiebot dependency with a custom implementation. Three categories - necessary (always on), analytics, and marketing - persisted to localStorage. Context-based state management, a settings modal for granular control, and conditional script loading based on consent. Lighter, faster, and no external dependency.

CMS Architecture

This is the part I'm proudest of. Every section of the site maps to a Storyblok blok. The marketing team can rearrange sections, update copy in any of nine languages, swap images, toggle visibility - all without touching code. Dynamic imports keep the JavaScript lean: only the hero and page wrapper load statically. The other eleven bloks are lazy-loaded.

Storyblok's webhook triggers a revalidation endpoint, so content changes go live within sixty seconds via ISR.

The Performance Story

This is where things got humbling.

The first Lighthouse audit after adding all the visual effects came back around 30 on mobile. Confetti, smoke, Framer Motion animations - they made the site beautiful and the performance score ugly.

Getting from 30 to 95+ was a multi-week effort:

  1. Deferred WebGL canvas - moved ConfettiCanvas initialization to requestIdleCallback() so it doesn't block Largest Contentful Paint. Device pixel ratio capped at 2x.

  2. CSS animations over Framer Motion for the hero - Framer Motion is powerful but heavy. The hero section's entrance animations were rewritten as pure CSS @keyframes. Framer Motion was kept for scroll-triggered reveals deeper in the page, dynamically imported to keep it out of the initial bundle.

  3. Typekit font loading - switched to media="print" with an onload swap to "all". Prevents the font stylesheet from blocking First Contentful Paint while still loading the brand typeface (Avant Garde) quickly.

  4. AVIF images - Next.js image optimization configured to serve AVIF with WebP fallback. Image cache TTL set to one year on Vercel's edge.

  5. Lazy YouTube iframes - trailer section only loads the embed when it enters the viewport.

  6. Code splitting - eleven Storyblok bloks loaded via next/dynamic, reducing initial JavaScript by roughly 40%.

  7. Smoke lazy start - IntersectionObserver with 200px root margin. The smoke canvas doesn't start rendering until the user scrolls near it.

The accessibility score hit 100. Performance stabilized at 95+ on desktop, 88+ on mobile (the WebGL effects still cost a few points on slower mobile devices, but the tradeoff is worth it for the atmosphere).

Lessons Learned

Design iteration is non-negotiable

Three color rounds. Three. The first version was technically sound but visually wrong for the brand's direction. If I'd committed to V1 and moved on, we'd have shipped something "fine." The dark base in V3 transformed the whole feel.

Performance and animation are constantly in tension

Every visual effect has a cost. The confetti canvas is gorgeous on a 2024 MacBook Pro. On a budget Android phone, it needs to be lighter, or deferred, or both. Building the toggle early - disable trails on mobile, reduce particle count, defer initialization - saved me from a painful retrofit later.

CMS architecture deserves as much planning as code architecture

The old site's CMS was a mess of loosely connected fields. The new structure - fourteen clearly defined bloks, each with explicit props and rich text support - means I haven't gotten a single "can you change this text for me" request since launch. That's the real measure of success.

Internationalization is a first-class concern or it's a nightmare

Nine languages can't be an afterthought. Every component, every CMS field, every route, every meta tag, every sitemap entry needs to know about locales from day one. The next-intl library handles this beautifully, but only if you structure around it from the start.

The Result

The Copa City website went from a single-page marketing site to a full-featured, multilingual gaming platform:

  • 48 components following Atomic Design principles

  • 9 languages with SEO-optimized hreflang tags and locale-prefixed routes

  • ISR with 60-second revalidation - content changes go live in under a minute

  • Lighthouse 95+ desktop / 88+ mobile performance with full WebGL effects

  • Accessibility 100 - semantic HTML, ARIA labels, keyboard navigation

  • Blog with four route types - paginated, tagged, archived, individual posts

  • Custom cookie consent - no third-party dependencies

  • Contact form with category routing, validation, rate limiting, and honeypot protection

All deployed on Vercel's Frankfurt edge, all managed through Storyblok by a marketing team that hasn't needed a developer for content changes.

The tools are there. The frameworks are mature. And yes, AI-assisted development accelerates the process considerably. But the architecture decisions, the design direction, the performance tradeoffs - those still need a human with opinions. The code gets written faster. The thinking doesn't.

Share:

Related Posts

Anything is Possible: How I Built This Site in Just 3 Days
February 20, 2026· Maciej Szamowski

Anything is Possible: How I Built This Site in Just 3 Days

I’ve always been interested in technology (and gaming, too). I remember making my first attempts at building websites when I was in my teens—starting with Microsoft FrontPage and somehow ending up in MS Word. Then HTML5 and CSS came along; I tinkered with them, saw some results, but eventually set it all aside for years...

LLMClaudeNext.jsVercelTailwindCSSMCP