How We Built kafuideyinterviews.com — A Premium Digital Home for Ghana's Most Iconic Journalist
Back to Blog

How We Built kafuideyinterviews.com — A Premium Digital Home for Ghana's Most Iconic Journalist

Celestial Team
June 2, 2026
8 min read

Introduction: When a Legend Needs a Digital Stage

Kafui Dey is not just a journalist — he is a cultural institution. With decades of groundbreaking interviews across politics, entertainment, and business, his work deserves a platform that matches his legacy.

When he approached Celestial Web Solutions to build his official website, the brief was clear: it had to feel premium, it had to perform, and it had to tell a story before a single word was read.

This is the story of how we built kafuideyinterviews.com — the technology behind it, the decisions we made, and what it means for your brand if you are looking for the same level of craftsmanship.

The Goal: More Than a Portfolio Website

Most personal websites are digital brochures — static, forgettable, and rarely visited twice. We wanted kafuideyinterviews.com to be different.

The objectives were:

  • Create an immersive first impression with motion and cinematic visuals
  • Give Kafui full control over his content through a powerful CMS
  • Gate premium content for paying supporters via Patreon integration
  • Allow visitors to read his published books directly on the site
  • Generate downloadable PDFs of interviews and content
  • Rank on Google for searches related to Kafui Dey and his work

The Tech Stack — and Why We Chose It

Every technology choice we make at Celestial Web Solutions is intentional. Here is what powered this build:

TechnologyRoleNext.js 15 (App Router)Frontend frameworkTypeScriptType safety across the entire codebaseTailwind CSSRapid, consistent stylingSanity v3Headless CMS for all contentGSAPCinematic animations and transitionsPatreon OAuthPremium content gatingVercelDeployment and edge hosting

Why Next.js 15?

Next.js gives us the best of both worlds — server-side rendering (SSR) for SEO and client-side interactivity for a smooth user experience. For a public figure like Kafui Dey, Google needs to index every interview, every article, every page. Next.js makes that happen out of the box.

typescript

// Example: Server-side data fetching for interview pages // app/interviews/[slug]/page.tsx import { client } from '@/sanity/lib/client' import { interviewQuery } from '@/sanity/lib/queries' export async function generateMetadata({ params }: { params: { slug: string } }) { const interview = await client.fetch(interviewQuery, { slug: params.slug }) return { title: `${interview.title} | Kafui Dey Interviews`, description: interview.excerpt, openGraph: { images: [interview.coverImage], }, } } export default async function InterviewPage({ params }: { params: { slug: string } }) { const interview = await client.fetch(interviewQuery, { slug: params.slug }) return <InterviewDetail interview={interview} /> }

This means every interview page gets its own unique title, description, and Open Graph image — critical for social sharing and Google rankings.

The Hero Section — First Impressions With GSAP

The hero section was the most important design challenge. Kafui Dey's face and brand needed to command attention immediately.

We built a cinematic hero slideshow powered by GSAP (GreenSock Animation Platform) — the industry standard for high-end web animation used by award-winning agencies worldwide.

typescript

// components/HeroSlideshow.tsx (simplified) import { useEffect, useRef } from 'react' import gsap from 'gsap' export default function HeroSlideshow({ slides }: { slides: Slide[] }) { const containerRef = useRef<HTMLDivElement>(null) const currentIndex = useRef(0) useEffect(() => { const tl = gsap.timeline({ repeat: -1 }) slides.forEach((_, i) => { tl.to(`.slide-${i}`, { opacity: 1, scale: 1, duration: 1.2, ease: 'power2.out', }) .to(`.slide-${i}`, { opacity: 0, duration: 0.8, delay: 4, ease: 'power2.in', }) }) // Headline text reveal on load gsap.from('.hero-headline', { y: 60, opacity: 0, duration: 1, ease: 'power3.out', stagger: 0.15, }) }, []) return ( <section ref={containerRef} className="relative h-screen overflow-hidden"> {slides.map((slide, i) => ( <div key={i} className={`slide-${i} absolute inset-0`}> <img src={slide.image} alt={slide.title} className="object-cover w-full h-full" /> </div> ))} <div className="hero-headline absolute bottom-16 left-8 text-white"> <h1 className="text-6xl font-bold">Kafui Dey</h1> <p className="text-xl">Ghana's Voice of Truth</p> </div> </section> ) }

The result? Visitors are greeted with a living, breathing introduction — not a static image. It sets the tone before they scroll a single pixel.

Sanity v3 — Giving Kafui Full Content Control

One of our core promises at Celestial Web Solutions is that your website should not need a developer to update content. This is why we use Sanity CMS — a headless content management system that gives clients a beautiful, intuitive dashboard to manage everything.

For Kafui Dey, we set up schemas for:

  • Interviews — title, guest, date, video embed, transcript, featured image
  • Books — cover, description, chapter files
  • Articles — rich text with embedded media
  • Gallery — categorised photo collections

typescript

// sanity/schemas/interview.ts import { defineType, defineField } from 'sanity' export const interview = defineType({ name: 'interview', title: 'Interview', type: 'document', fields: [ defineField({ name: 'title', type: 'string', title: 'Interview Title' }), defineField({ name: 'slug', type: 'slug', options: { source: 'title' } }), defineField({ name: 'guest', type: 'string', title: 'Guest Name' }), defineField({ name: 'interviewDate', type: 'date', title: 'Date' }), defineField({ name: 'excerpt', type: 'text', title: 'Short Description' }), defineField({ name: 'coverImage', type: 'image', options: { hotspot: true } }), defineField({ name: 'content', type: 'array', title: 'Interview Content', of: [ { type: 'block' }, { type: 'image' }, { type: 'object', name: 'videoEmbed', fields: [{ name: 'url', type: 'url', title: 'Video URL' }], }, ], }), defineField({ name: 'isPremium', type: 'boolean', title: 'Premium Content (Patreon Required)', initialValue: false, }), ], })

Kafui can log into his Sanity Studio, add a new interview in minutes, mark it as premium or free, and it appears live on the website — no developer needed.

The BookReader Component — Reading Online, Beautifully

Kafui Dey is a published author. We wanted visitors to be able to read his books directly on the website — no PDF downloads required, no external platforms.

We built a custom BookReader component that renders book chapters in a clean, distraction-free reading interface, with:

  • Chapter navigation (previous / next)
  • Progress tracking
  • Mobile-optimised layout
  • Smooth page transitions

typescript

// components/BookReader.tsx (simplified) 'use client' import { useState } from 'react' interface Chapter { title: string content: string } export default function BookReader({ chapters }: { chapters: Chapter[] }) { const [currentChapter, setCurrentChapter] = useState(0) const chapter = chapters[currentChapter] return ( <div className="max-w-2xl mx-auto px-6 py-12 font-serif"> {/* Progress bar */} <div className="h-1 bg-gray-200 rounded mb-8"> <div className="h-1 bg-amber-500 rounded transition-all duration-500" style={{ width: `${((currentChapter + 1) / chapters.length) * 100}%` }} /> </div> <h2 className="text-2xl font-bold mb-6">{chapter.title}</h2> <div className="prose prose-lg leading-relaxed" dangerouslySetInnerHTML={{ __html: chapter.content }} /> {/* Navigation */} <div className="flex justify-between mt-12"> <button onClick={() => setCurrentChapter(p => Math.max(0, p - 1))} disabled={currentChapter === 0} className="px-6 py-2 border rounded disabled:opacity-30" > ← Previous </button> <span className="text-sm text-gray-500 self-center"> Chapter {currentChapter + 1} of {chapters.length} </span> <button onClick={() => setCurrentChapter(p => Math.min(chapters.length - 1, p + 1))} disabled={currentChapter === chapters.length - 1} className="px-6 py-2 bg-amber-500 text-white rounded disabled:opacity-30" > Next → </button> </div> </div> ) }

PDF Generation API — Downloadable Interviews on Demand

One unique feature we built was a server-side PDF generation API route. Visitors can download a beautifully formatted PDF of any interview — great for archiving, printing, or sharing professionally.

typescript

// app/api/generate-pdf/route.ts import { NextRequest, NextResponse } from 'next/server' import puppeteer from 'puppeteer' export async function POST(req: NextRequest) { const { interviewSlug } = await req.json() const browser = await puppeteer.launch({ headless: true }) const page = await browser.newPage() // Load the interview page for PDF rendering await page.goto(`${process.env.NEXT_PUBLIC_BASE_URL}/interviews/${interviewSlug}?pdf=true`, { waitUntil: 'networkidle0', }) const pdf = await page.pdf({ format: 'A4', printBackground: true, margin: { top: '20mm', bottom: '20mm', left: '15mm', right: '15mm' }, }) await browser.close() return new NextResponse(pdf, { headers: { 'Content-Type': 'application/pdf', 'Content-Disposition': `attachment; filename="${interviewSlug}.pdf"`, }, }) }

This means every interview is instantly downloadable as a polished PDF — no extra design work required.

Patreon OAuth Gating — Rewarding Loyal Supporters

Kafui Dey has a loyal community. We built a Patreon integration that allows him to gate premium interviews and content exclusively for his paying Patreon supporters.

The flow works like this:

  1. Visitor tries to access a premium interview
  2. They are prompted to Connect with Patreon
  3. After OAuth authentication, we verify their pledge tier
  4. If they are an active supporter, the content unlocks instantly

typescript

// app/api/auth/patreon/route.ts (simplified) import { NextRequest, NextResponse } from 'next/server' export async function GET(req: NextRequest) { const { searchParams } = new URL(req.url) const code = searchParams.get('code') // Exchange code for access token const tokenRes = await fetch('https://www.patreon.com/api/oauth2/token', { method: 'POST', body: new URLSearchParams({ code: code!, grant_type: 'authorization_code', client_id: process.env.PATREON_CLIENT_ID!, client_secret: process.env.PATREON_CLIENT_SECRET!, redirect_uri: process.env.PATREON_REDIRECT_URI!, }), }) const { access_token } = await tokenRes.json() // Fetch user's pledge status const userRes = await fetch( 'https://www.patreon.com/api/oauth2/v2/identity?include=memberships&fields[member]=patron_status', { headers: { Authorization: `Bearer ${access_token}` } } ) const userData = await userRes.json() const isActiveMember = userData.included?.some( (m: any) => m.attributes?.patron_status === 'active_patron' ) // Set session cookie and redirect const response = NextResponse.redirect(new URL('/interviews', req.url)) response.cookies.set('patreon_verified', isActiveMember ? 'true' : 'false', { httpOnly: true, secure: true, maxAge: 60 * 60 * 24, // 24 hours }) return response }

Although the Patreon gate launched as inactive (a strategic decision by the client), the entire infrastructure is built and ready to activate — a future-proof investment.

SEO Architecture — Built to Rank

A beautiful website that no one finds is a wasted investment. We built kafuideyinterviews.com with SEO as a first-class concern:

  • Dynamic metadata per interview, article, and page
  • JSON-LD structured data for rich Google results
  • Open Graph tags for rich social media previews
  • Sitemap auto-generation from Sanity content
  • Canonical URLs to prevent duplicate content

typescript

// Structured data for interview pages const jsonLd = { '@context': 'https://schema.org', '@type': 'NewsArticle', headline: interview.title, author: { '@type': 'Person', name: 'Kafui Dey', }, datePublished: interview.interviewDate, image: interview.coverImage, publisher: { '@type': 'Organization', name: 'Kafui Dey Interviews', logo: 'https://kafuideyinterviews.com/logo.png', }, }

The Result

kafuideyinterviews.com is more than a website — it is a living digital archive of one of Ghana's most important journalistic voices, built to last, built to grow, and built to be found.

What was delivered:

  1. Cinematic GSAP hero slideshow
  2. Full Sanity v3 CMS with interview, book, article, and gallery schemas
  3. Custom BookReader component for in-browser reading
  4. Server-side PDF generation API
  5. Patreon OAuth gating (built and ready to activate)
  6. Full SEO architecture with JSON-LD and dynamic metadata
  7. Mobile-first, responsive design
  8. Deployed on Vercel with global edge CDN

What This Means for You

Whether you are a journalist, author, public speaker, pastor, politician, or business owner in Ghana — your story deserves this level of platform.

At Celestial Web Solutions, we build custom websites that are:

  • Fast, secure, and SEO-optimised
  • Connected to a CMS you can actually use
  • Designed to make your brand unforgettable online
  • Built with the same technology stack used by global companies

Ready to talk about your project? Contact us at celestialwebsolutions.net