F.L.O.S.S. — Youth & Young Adult Empowerment
A four-week redesign of a youth-development nonprofit's site on Next.js 15 (App Router), React 19, and TypeScript, with Sanity driving age-based pathway content and an interactive age-finder routing visitors to the right stage.

Case Study
F.L.O.S.S. is a New Orleans nonprofit that helps youth and young adults, ages 8 to 30, build toward economic independence. The brief was a full redesign, but the real problem was structural. The organization's work grows with a person across a twenty-two-year age span, and the old site flattened that into one undifferentiated pitch. My first decision was that the age range wasn't a detail to communicate, it was the organizing principle for the whole site.
So the architecture is built around four pathways: 8–12, 13–17, 18–24, and 25–30. I modeled pathways as a dedicated collection rather than hardcoding them into page components, because the same data needed to surface in three places: the homepage cards, the Pathway Explorer on /services, and the age-finder. One source, three consumers. When programming details firm up, the team edits one record and every surface updates.
The age-finder was the piece I most wanted to get right. It's a small interaction, but it's the difference between a visitor self-sorting in one step versus reading four descriptions to find themselves. A visitor enters an age and gets routed to the matching pathway. The part that's easy to skip and shouldn't be is the out-of-range handling: an age below 8 or above 30 gets a specific message rather than nothing, so the interaction never dead-ends. The bounds come straight off each pathway's ageRange tuple in the same Sanity data, so there's no separate config to drift out of sync.
The stack is Next.js 15 on the App Router, React 19, and TypeScript in strict mode, with Sanity for content and Vercel for hosting. This is a combination I reach for on content-driven sites where a non-technical team needs real editorial control, and the reasoning here was concrete rather than habit: F.L.O.S.S. needed to manage pathways, partners, and sponsored projects without me in the loop, and the age-based structure meant the content model had to mirror the program structure exactly. Sanity lets the schema match how the organization actually thinks about its work.
I leaned on the template foundation I've been building out, which carries cookie consent, the accessibility widget, analytics, legal pages, and the dev-password gate, all driven from a single site-config.ts. That's what made a four-week timeline realistic. The infrastructure was solved, so the time went into the pathway architecture, the design system, and the content rather than rebuilding plumbing.
A detail I care about: environment variables are validated at build time with Zod in lib/env.ts. A malformed required value fails the build instead of throwing at runtime in front of a visitor. Paired with that, the contact and dev-login routes run behind a rate limiter, in-memory by default with a documented Upstash Redis path for production-grade distributed limiting. For a nonprofit that won't have a developer on call, failing loudly at build and degrading safely in production matters more than it would on a project with a full-time engineering team.
On the design side, I established a reusable editorial language early: horizontal split headers, editorial numbered lists, alternating left-right sections, and full-width teal CTA bands with decorative arcs. Setting those patterns up front meant each new page was an application of the system rather than a fresh set of decisions, which is the only way the visual consistency holds up across a site this size on this timeline.
If I were doing it again, I'd push the team on real pathway content earlier. The structure is solid and the copy is good, but some of it expands on original highlights rather than final programming details, and I left notes in the data to refine as those firm up. The architecture is ready for it. It's a content question, not a code one.