What it is
Personal portfolio built to be a technical artifact, not just a landing page. Every feature uses the full stack: Redis for real-time counters and leaderboards, Contentlayer for type-safe MDX content, qrcode.react for a downloadable contact card, canvas + react-swipeable for a mobile-playable Snake game, and a 79-entry certification dataset across 9 authorities rendered in a navigable carousel.
Live: developer.ericgitangu.com
Source: github.com/ericgitangu/deveric
Projects — Contentlayer + Redis view counting
Projects are MDX files processed by Contentlayer at build time into typed Project objects — no CMS, no database reads for content. Each project page increments a view counter in Upstash Redis using an atomic INCR on page load (server action), giving real-time counts without polling. The projects grid is statically generated; only the counter fetch is dynamic.
Certifications — 79 certs, 9 authorities, 10 domains
app/certifications/data.ts (803 lines) is the source of truth: 79 certifications with id, name, url, authority, domain, date, licenseNumber, and featured flag. Authorities: Meta (20), LinkedIn Learning (39), Coursera (12), Duke University (Rust specialization), DeepLearning.AI, HackerRank, Udemy, SoloLearn, HackerX. Domains span Backend, Frontend, Mobile, Data Science, Blockchain, Security, DevOps, Languages, AI/ML, and Fundamentals.
CertificationCarousel renders them in a swipeable carousel grouped by domain. CertificationCard links directly to the issuing authority. CarouselDots tracks position. All filtered client-side — no server round-trip for domain switching.
Virtual business card — vCard/ICS download + QR code
BusinessCardPanel.tsx (373 lines) is a full contact-card implementation:
- VCF file served at
/eric-gitangu.vcf— standard vCard 3.0 format, importable by iOS Contacts, Android, Outlook, macOS Contacts - QR code generated client-side via
qrcode.reactpointing to the VCF URL — scan-to-import from any phone - WhatsApp deep-link QR for direct chat
- Dual phone numbers: US (+1-978-710-9475) and Kenya (+254-708-078-997)
- LinkedIn, GitHub, and portfolio links in a single panel
- UI: fixed left-edge tab on desktop (vertical text), floating bottom-left pill on mobile — no layout shift
- Panel reveals with Framer Motion spring animation
Snake game — canvas, swipe, haptic feedback, Redis leaderboard
SnakeGame.tsx (383 lines):
- Canvas rendering with a 20px grid. Responsive canvas: 300px mobile / 350px tablet / 400px desktop, recalculated on resize via
ResizeObserver - Keyboard (arrow keys) +
react-swipeablefor mobile swipe detection — same game, both input surfaces - Speed curve: starts at 200ms tick interval, decreases by 5ms per food eaten, floor at 50ms
- Collision detection: wall bounds + self-intersection check on every move
useHapticscustom hook fires the Vibration API on food eat and game over — progressive enhancement, no-op on desktopGameOverModalprompts for a display name, then writes{ name, score, timestamp }to Upstash Redis sorted setsnake:leaderboardwithZADD(score as sort key)- Leaderboard fetched from Redis on game start — top 10 by score, displayed alongside the game
HapticSnackbarshows score milestones mid-game without interrupting play
Fun zone
app/fun/page.tsx composes: SnakeGame + TipFetcher + Particles background (canvas-based particle system, no library dependency).
TipFetcher calls app/api/tips/route.ts — a Next.js route handler that returns a random development tip from a static array. Auto-refreshes every 30s client-side. The API route is edge-compatible.
PWA
PWAInstallPrompt.tsx hooks the beforeinstallprompt browser event and surfaces a custom install prompt rather than relying on the browser default. manifest.json is generated at build time with the correct theme color and icon set. The site is fully installable on iOS (Add to Home Screen) and Android (PWA install).
SEO and structured data
app/lib/structured-data.ts (166 lines) generates Schema.org JSON-LD for every page type: Person, WebSite, ProfilePage, BreadcrumbList, ItemList (projects), and AboutPage. Injected via next/head script tags — no third-party SEO library. <meta> og/twitter tags are per-page via Next.js 13 generateMetadata.
Stack
Next.js 13 App Router · TypeScript · Tailwind CSS · Contentlayer · Upstash Redis · Framer Motion · qrcode.react · react-swipeable · @vercel/analytics · Vercel · Rome formatter · Rehype + Remark plugins · Inter + CalSans fonts