Private Client Work

MAMIKEU

Bilingual institutional website for a Cameroonian NGO, built to win institutional funders. Next.js 16, Payload CMS, WCAG 2.2 AA.

MAMIKEU

About This Project

A bilingual institutional website for the Association des Enfants de Marie Mikeu (MAMIKEU), a rural NGO in Dschang–Fokoué, Cameroon, working with vulnerable families across education, health, agriculture and environment, and entrepreneurship. The site exists to convince institutional funders rather than to collect donations; it takes no payments. It had to answer four questions in the first few seconds: who are you, what do you do, who do you help, and how can I contribute. French and English content sit side by side in the CMS, with no string hardcoded in either language.

At a Glance

74

pre-rendered pages

303

unit + E2E tests

AA

WCAG 2.2, enforced in tokens

33

decision records

Role
Design direction, content model, full build, accessibility, testing and deployment — solo.
Timeline
4 – 16 September 2026 · 52 commits
Status
Feature-complete · awaiting client photography and final copy

The Problem

Late in the build, the client corrected the audience: the site exists to attract NGOs and funders, not to be read by the rural population it serves. Three constraints had been built for a reader on an entry-level phone over 2G. Rather than quietly reversing them, I logged the premise change as its own decision record and relaxed exactly three constraints — animation, typography and the JavaScript budget — each re-measured before and after. Reduced motion, no-JavaScript readability and AA contrast did not move.

Technologies Used

Next.jsReactTypeScriptPayload CMSSQLiteTailwind CSSGSAPBunVitestPlaywright

Framework

Next.js 16.3 (App Router, Cache Components), React 19

CMS

Payload 3.88 mounted in the same app

Read through its local API; 11 collections and 7 globals

Database

SQLite via @payloadcms/db-sqlite

One host, one process, one file to back up

Styling & motion

Tailwind 4, CSS, GSAP 3.15 + ScrollTrigger

GSAP only for scroll-linked effects, only where pages move

Validation

Zod at every trust boundary

Same schema server-side and client-side

Testing

Vitest (84), Playwright (219, desktop + mobile)

Tests encode the design rules, not just behaviour

Key Features

  • •Fully bilingual: every interface string and every content field in French and English
  • •Four programmes presented as full-width numbered chapters instead of generic icon cards
  • •Project archive with filters and story-driven project pages with a reading-position rail
  • •Draggable before/after photo curtain, keyboard-operable, with a no-JavaScript fallback
  • •News, downloadable reports and an impact and transparency page
  • •French-native admin, with an editor's guide written for first-time CMS users
  • •Half-filled CMS produces a shorter page, never a broken one
  • •Consent and permission tracked as fields on photographs and partner logos

Challenges & Solutions

01Serving a licensed typeface without redistributing it

The client chose Palatino Linotype, which can't legally be converted and served. Hand-written @font-face rules resolve local('Palatino Linotype') first, so Windows and macOS visitors download zero bytes; everyone else gets a subset of TeX Gyre Pagella, a metric-compatible twin under a licence that permits it.

02A budget test that reported half the payload

The JavaScript budget test summed compressed sizes inside async handlers and read the total after a fixed wait, so the homepage measured 67 KB while shipping 141 KB. Every response is now awaited, the test fails if no scripts were seen, and router prefetches are blocked so pages aren't charged for their neighbours.

03An entrance animation that cost four seconds of LCP

Scroll-reveal elements start invisible, so the browser recorded no Largest Contentful Paint until JavaScript ran: 6.7 s on throttled mobile. The first row of every list now skips the reveal, bringing LCP to 3.0 s, pinned by a DOM-level regression test.

04The no-JavaScript test that passed on a blank page

The first check read computed opacity, which is 1 even inside a display: none ancestor — so it passed while pages shipped their whole <main> inside React's hidden streaming container. The test now walks up from <main> for hidden ancestors across nine paths, with JavaScript disabled and under reduced motion.

Outcome & Lessons

Largest Contentful Paint lands between 2.55 s and 3.07 s across all six page kinds on throttled mobile, with zero layout shift. The build is feature-complete and runs as a self-contained preview — the build seeds its own SQLite file, so a reviewer gets the full site and admin from a git push, with no third-party services. Nothing was invented to fill the gaps: missing facts hide their section, and placeholders say plainly what's still owed.

  • •A decision that rests on a fact about the audience has to name that fact, so it can fall when the fact does.
  • •A design rule nobody can fail is a suggestion: the tests encode the design system, from icon imports to overflow at every width.
  • •Measurements need testing too: a budget test that under-reports is worse than none, because it signs things off.

Project Links

Source code is private client work