Chicknz
AI / Multi-tenant SaaS

Outcomes
CSS custom properties + CVA variants drive little/kid/tween/teen tap targets, density, and reading level. No per-age fork.
Every Prisma query and AI call scoped by familyId in extension code. A missed scope is a type error, not a security incident.
Overview
Chicknz is a multi-tenant family management app built around an age-adaptive UI: one codebase that serves four distinct complexity tiers (little, kid, tween, teen) so a four-year-old and a fourteen-year-old get experiences tuned to their reading level, motor skills, and motivation patterns.
The stack is Next.js 15, Prisma + PostgreSQL, Better Auth for parent/kid auth (OAuth + PIN), Vercel AI SDK with Anthropic Claude for background intelligence (chore rotation, summaries, NL task creation), Ably for realtime, and a Vercel Blob layer for media.
Challenge
Family-management apps either ship a single generic UI that doesn't fit any age group, or shard into multiple apps and lose the shared family state. The goal was to keep one shared multi-tenant data model (every query scoped by `familyId`) while presenting four parallel UI tiers driven by CSS custom properties and CVA variants.
Tech stack
Architecture
Key features
Age-Adaptive UI
Four UI tiers (little / kid / tween / teen) driven by CSS custom properties and CVA variants. Tap targets, icon size, density, and reading level all flex per age group from a single component library.
AI Background Intelligence
Anthropic Claude (Haiku for fast paths, Sonnet for generation) powers natural-language chore creation, fair-rotation scheduling, and weekly family summaries. Embedded in the workflow, not bolted on as a chatbot.
Multi-Tenant by familyId
Every database query and AI call is scoped to a familyId. Better Auth handles parent OAuth and kid PIN flows; Ably broadcasts realtime updates inside a family without crossing tenant boundaries.
Lessons & tradeoffs
A four-year-old and a fourteen-year-old can't share one UI, and I refused to fork the app to give each their own. So I pushed the whole age gap into CSS tokens and component variants: one library, four reading levels. The right abstraction turns a product decision into a styling decision, so a whole age tier becomes something you configure instead of rebuild.
The challenge
Four age groups need genuinely different interfaces, but forking into four apps would shatter the shared family state and kill velocity.
What I decided
I drove every age difference (tap target, icon size, density, reading level) through CSS custom properties and CVA variants on a single component library.
What I took from it
Age-adaptive UI is engineering, not just design. Get the tokens right and shipping a new tier costs a config, not a fork.
The challenge
In multi-tenant data, one missed scope leaks one family's kids to another, and middleware checks are easy to forget on the next new query.
What I decided
I scoped every query by familyId inside the Prisma extension layer, not in the route handlers.
What I took from it
Push tenancy down to the data layer and a missed scope becomes a type error at compile time instead of a security incident in production.
The challenge
Parents wanted the app to feel intelligent, but I will not put a chat surface in front of a child.
What I decided
I wired Claude into the workflow (chore rotation, fair scheduling, weekly summaries) as background intelligence, never as a chatbot.
What I took from it
AI as orchestrator beats AI as chat for most products. The intelligence should show up in the outcome, not in a text box.