A Next.js application that helps users make informed voting decisions through personalized policy alignment matching. Users complete a civic values assessment to build their "Civic Blueprint," which is then used to provide personalized ballot recommendations.
- Authentication: Google OAuth and anonymous guest mode via better-auth
- Civic Blueprint Assessment: Slider-based questionnaire across 17 policy axes grouped into 5 domains (Economic, Healthcare, Housing, Justice, Climate)
- Schwartz Values Assessment: Personal values assessment based on the Schwartz theory of basic human values, with booster vignettes for deeper profiling
- Ballot Explorer: Browse ballot items with personalized recommendations based on your civic profile
- Vote Tracking: Persistent ballot vote selections stored locally via Zustand
- Demographic Impact: See how ballot measures affect different demographic groups
- Fine-Tuning: Adjust your blueprint axes after completing the assessment
- Archetype Classification: Get classified into one of 8 civic archetypes based on your profile
- Blueprint Insights: Personalized insights generated from your civic profile
- Voting Squad: Form a small accountability group with friends and family — members can see binary status signals (registered, prepped, voted) but never see anyone's answers, values, or actual votes. Invites flow through a dedicated
/joinonboarding. - Analytics & Feedback: Vercel Web Analytics + custom event tracking and user feedback collection backed by a Neon Postgres database, with feedback mirrored to Google Sheets
- Framework: Next.js 16 (App Router)
- Language: TypeScript 5.9
- UI: React 19, Tailwind CSS v4, Framer Motion
- Auth: better-auth (Google OAuth + anonymous sessions)
- State Management: Zustand with localStorage persistence
- Database: Neon Postgres (via Prisma 7 ORM)
- Analytics: Vercel Web Analytics
- Testing: Vitest (unit/integration) + Playwright (E2E)
- CI/CD: GitHub Actions (build, lint, security audit)
- Icons: Lucide React
- Deployment: Vercel
- Node.js 20+
- npm
# Clone the repository
git clone git@github.com:Humanity-Unleashed/Ballot-Builder-Prototype.git
cd Ballot-Builder-Prototype
# Install dependencies
npm install
# Pull environment variables from Vercel (see below)
# Start development server
npm run devOpen http://localhost:3000 to view the app.
The project uses Vercel-managed environment variables for database connections and secrets. To pull them to your local machine:
-
Install the Vercel CLI (if you haven't already):
npm i -g vercel
-
Link your local project to the Vercel project (first time only):
vercel link
Select the Humanity-Unleashed team and the ballot-builder-prototype project when prompted.
-
Pull environment variables to a local
.env.localfile:vercel env pull .env.local
This will create a .env.local file with all the required variables including DATABASE_URL and DIRECT_URL for the Neon Postgres database.
Note: Never commit
.env.localto git — it contains secrets. See.env.examplefor a reference of all available variables.
The project uses Neon Postgres as the database (connected via Prisma ORM). It stores analytics events and user feedback.
View the database: Team members with access can browse tables, run queries, and inspect analytics/feedback data via the Neon Console. The direct project link lives in the internal team wiki.
View feedback: Feedback entries are automatically mirrored to a Google Sheet (restricted to humun.org accounts) for easy browsing. The direct sheet link lives in the internal team wiki.
Database commands:
npm run db:generate # Regenerate Prisma client
npm run db:push # Push schema changes to the database
npm run db:migrate:dev # Create and apply a new migration
npm run db:migrate:deploy # Apply pending migrations (production)
npm run db:studio # Open Prisma Studio (visual DB browser)# Development
npm run dev # Start development server
npm run build # Build for production
npm run start # Start production server
npm run lint # Run ESLint
npm run lint:fix # Auto-fix lint issues
# Testing
npm run test # Run unit tests in watch mode
npm run test:run # Run unit tests once
npm run test:coverage # Run tests with coverage report
npm run test:e2e # Run Playwright E2E tests
npm run test:e2e:ui # Run E2E tests with interactive UI
# Database (see above)
npm run db:generate
npm run db:push
npm run db:migrate:dev
npm run db:studiosrc/
├── app/ # Next.js App Router
│ ├── (app)/ # Main application routes
│ │ ├── ballot/ # Ballot explorer
│ │ └── blueprint/ # Blueprint assessment & viewer
│ ├── (auth)/ # Authentication routes
│ │ ├── login/
│ │ └── register/
│ ├── api/ # API routes (53 endpoints)
│ │ ├── analytics/ # Analytics event tracking
│ │ ├── assessment/ # Assessment session management
│ │ ├── auth/ # better-auth catch-all handler
│ │ ├── ballot/ # Ballot data endpoints
│ │ ├── blueprint/ # Blueprint endpoints
│ │ ├── candidates/ # Candidate information
│ │ ├── civic-axes/ # Civic axes specification & scoring
│ │ ├── contests/ # Contest endpoints
│ │ ├── feedback/ # User feedback collection
│ │ ├── fine-tuning/ # Fine-tuning session management
│ │ ├── measures/ # Ballot measure endpoints
│ │ ├── personas/ # Test persona endpoints
│ │ └── schwartz-values/ # Schwartz values assessment
│ ├── globals.css
│ ├── layout.tsx
│ └── page.tsx
├── components/ # React components
│ ├── analytics/ # Analytics provider
│ ├── ballot/ # Ballot browsing & voting
│ ├── blueprint/ # Assessment & blueprint views
│ ├── demographics/ # Demographic impact screens
│ ├── feedback/ # Feedback collection button
│ ├── layout/ # Layout components (TopNav)
│ ├── schwartz/ # Schwartz assessment & boosters
│ └── ui/ # Reusable UI primitives
├── context/ # React Context providers
├── data/ # Static data files
├── hooks/ # Custom hooks (analytics, etc.)
├── lib/ # Client-side utilities & auth config
├── server/ # Server-side code
│ ├── data/ # Data sources (ballot, civic axes, etc.)
│ ├── services/ # Business logic & DB services
│ ├── types/ # Server-side type definitions
│ └── utils/ # Server utilities
├── services/ # API client (Axios)
├── stores/ # Zustand stores (user, schwartz, ballot, etc.)
└── types/ # TypeScript type definitions
prisma/
├── schema.prisma # Database schema
└── migrations/ # Database migrations
e2e/ # Playwright E2E tests
docs/ # Project documentation
A user's political profile consisting of:
- 5 Domains: Economic, Healthcare, Housing, Justice, Climate
- 17 Axes: Policy spectrums within each domain — Economic (4: safety net, investment, school choice, tax structure), Healthcare (3), Housing (3), Justice (4: policing, sentencing, firearms, reproductive), Climate (3)
- Axis Values: 0-10 scale representing stance between two policy poles
- Confidence Scores: Based on number of assessment items answered
Personal values assessment using the Portrait Values Questionnaire:
- 10 Value Types: Power, Achievement, Hedonism, Stimulation, Self-Direction, Universalism, Benevolence, Tradition, Conformity, Security
- 4 Higher-Order Dimensions: Self-Enhancement vs Self-Transcendence, Openness vs Conservation
- Ipsatized Scoring: Scores relative to individual's mean rating
- Booster Vignettes: Follow-up scenarios for deeper value profiling
Users are classified into one of 8 archetypes based on three meta-dimensions derived from their civic axes:
- Responsibility Orientation: Community-led ↔ Individual-led
- Change Tempo: Change-seeking ↔ Stability-seeking
- Governance Style: Rules & standards ↔ Flexibility & choice
See the docs folder for detailed documentation:
- Architecture Overview - System design and layer descriptions
- Assessment Pipeline - Scoring algorithms and archetype classification
- API Routes Reference - All 53 API endpoints
- Getting Started Guide - Setup and development workflow
- Production Roadmap - Implementation brief for production features
- Team Access & Credentials - External services, consoles, and onboarding
Private - All rights reserved