How we turned the site into a Next.js playground: every feature has a reason
When you're building a site about technology, the site itself should be proof you know what you're talking about. That's why we made airepublic.cz more than a blog — it's a playground where every Next.js feature has a place.
Here's what we added, why, and how it works under the hood.
Particle hero animation
What: The text "airepublic.cz" assembles itself out of thousands of coloured dots in the colours of the Czech flag. On desktop there's mouse repulsion — hover the cursor and the dots scatter.
Why: First impression. A visitor lands on the site and instantly sees this isn't a generic template. Plus it's a good performance test — a canvas with 3,000+ particles has to hit 60fps.
How: A Client Component with useEffect. An offscreen canvas renders the text in a system font, getImageData samples the pixels, and each pixel becomes a particle with a target position. The animation loop uses requestAnimationFrame, easing, and mouse-repulsion physics.
// Česká vlajka — barva podle pozice pixelu v textu
function getCzFlagColor(rx: number, ry: number): string {
// Modrý klín vlevo
if (rx < 0.5 && ry >= rx && ry <= 1 - rx) return '#11457E'
// Horní polovina = šedá, dolní = červená
return ry < 0.5 ? '#B0B0B0' : '#D7141A'
}
On mobile the canvas doesn't work reliably (Safari and offscreen canvas are sworn enemies), so the mobile version only shows the mascot.
Next.js feature: Client Component boundary — 'use client' only on the canvas component, the rest of the homepage is a Server Component.
Tamagotchi mascot
What: A pixel-art character with 21 behaviours — waving, typing on a laptop, sleeping, dancing, thinking, spawning agents, chatting with them, debugging bugs, launching rockets.
Why: Personality. Every site has a logo, but how many sites have a living mascot? Inspiration: Claude.ai has its own pixel mascot with animations. We took it a level further — interactions, multi-agent scenes, achievements.
How: An SVG pixel grid defined as a 2D array. Each behaviour is a sequence of frame grids with timing definitions. A state machine in React cycles through the behaviours.
const IDLE: Grid = [
[_,_,_,_,B,_,_,_,B,_,_,_,_], // uši
[_,_,_,B,B,B,B,B,B,B,_,_,_], // hlava
[_,_,B,B,D,B,B,B,D,B,B,_,_], // oči
// ... 9 řádků, 13 sloupců
]
The multi-agent scenes (spawn, chat, handoff) render two SVG grids side by side. The buddy is flipped via a mirror() helper. The spawn animation gradually reveals the buddy's pixels in a random order.
Next.js feature: A scalable client component with props — scale={12} on mobile (hero), scale={6} on desktop, scale={4} as the floating guide.
Floating guide
What: A small mascot in the bottom-right corner on every page (except the homepage). It says contextual lines, occasionally peeks from the side, and you can drag it anywhere.
Why: A site should feel alive. A floating mascot adds personality without getting in the way. Contextual bubbles ("Nice article!" on the blog, "Claude is number one!" on the tools section) show the site reacts to where you are.
How: usePathname() from Next.js detects the current route → picks a pool of lines. A speech bubble shows every 12-20s and disappears after 3.5s. Peek-a-boo effect via CSS transitions. Drag via pointer events with capture.
const BUBBLES_BY_PATH: Record<string, string[]> = {
'/blog': ['Hezký článek!', 'Scrolluj dál...'],
'/nastroje': ['Claude je jednička! 💜', 'Testoval jsem všechny.'],
// ...
}
Next.js feature: usePathname() hook for contextual behaviour, component in layout.tsx = persists across navigations.
Achievement system
What: You click the mascot → a counter in localStorage. Milestones: 10× = "Friend", 50× = "Best Friends", 100× = "Addict", 200× = "Legend". Toast notifications on each milestone.
Why: Gamification. People love clicking and love getting rewards. Plus it motivates them to interact with the mascot.
How: localStorage for persistence, React state for toasts. Zero backend, zero cookies.
Dark mode
What: A moon/sun icon in the navigation. Click to switch the whole site between light and dark mode.
Why: Because everyone wants dark mode. And because it shows how CSS custom properties work — one classList.toggle('dark') and the whole site flips.
How: The .dark CSS class on the <html> element overrides the theme variables in @theme. Persists in localStorage.
.dark {
--color-background: #0a0a0a;
--color-foreground: #f8fafc;
--color-card: #141414;
/* ... */
}
Next.js feature: Tailwind v4 @theme with CSS custom properties plus class-based dark mode.
Konami code
What: Type ↑↑↓↓←→←→BA on the keyboard → a pixel firework explodes across the whole screen.
Why: Easter egg. Developers love them. And it's proof the site has a soul.
How: A useEffect listener on keydown, a sliding-window sequence. On match, 40 CSS particles are rendered with custom properties --tx and --ty controlling flight direction.
@keyframes konami-particle {
0% { transform: translate(0, 0) scale(1); opacity: 1; }
100% { transform: translate(var(--tx), var(--ty)) scale(0); opacity: 0; }
}
Custom 404 page
What: The mascot is looking for the lost page. "Nothing here… but the mascot is!"
Why: The default 404 is boring. A custom 404 shows personality and keeps the user on the site.
Next.js feature: not-found.tsx in the app directory — automatically shown for non-existent routes.
Custom error page
What: The mascot with a "Something went wrong" message and a retry button.
Why: Errors happen. Instead of a white screen of death we show a friendly face.
Next.js feature: error.tsx with 'use client' — the Error Boundary pattern, reset() function for retry.
Skeleton loading
What: On the Blog and Tools pages, animated skeleton placeholders appear while content loads instead of a blank page.
Why: Perceived performance. The user sees the page structure immediately, the data fills in.
Next.js feature: loading.tsx in route segments — an automatic Suspense boundary.
What's under the hood overall
| Next.js feature | Where it's used |
|----------------|----------------|
| App Router | The whole site structure |
| Server Components | All pages (default) |
| Client Components | Mascot, navigation, canvas, easter eggs |
| loading.tsx (Suspense) | Blog, Tools |
| error.tsx (Error Boundary) | Global error handling |
| not-found.tsx | Custom 404 |
| generateStaticParams | Blog and tool detail pages |
| generateMetadata | Dynamic SEO for every page |
| Metadata API | Root layout, OG images |
| usePathname | Navigation, floating mascot context |
| Route Handlers | OG image generator |
| MDX (next-mdx-remote) | All articles and reviews |
| next/font (Geist) | Typography |
| Tailwind v4 + @theme | Whole design system |
Takeaways
- Your site is your CV. If you write about Next.js, your site should be the best showcase.
- Details make the difference. The Konami code won't boost traffic. But it turns the site into an experience.
- Progressive enhancement. Canvas particle animation on desktop, a simple mascot on mobile. Works everywhere.
- AI + vibe coding = speed. I built everything above with Claude Code over a single weekend. On my own it would have taken weeks.
Now — try that Konami code. ↑↑↓↓←→←→BA. 🎮