Skip to content

Instantly share code, notes, and snippets.

@samkeen
Created March 8, 2026 03:42
Show Gist options
  • Select an option

  • Save samkeen/3957502a7e249a5a87275697e114dc3e to your computer and use it in GitHub Desktop.

Select an option

Save samkeen/3957502a7e249a5a87275697e114dc3e to your computer and use it in GitHub Desktop.
❯ how does this app work
⏺ Bash(date '+%Y-%m-%d %H:%M:%S %z')
⎿  2026-03-07 19:39:07 -0800
⏺ Searched for 2 patterns, read 5 files (ctrl+o to expand)
⏺ Here's how Demo Kit works:
The Big Picture
Demo Kit is a platform for running structured demo nights — think hackathon showcases, team demo days, or community project showcases. It solves the "everyone fumbles with cables and
screenshare" problem by giving demos a permanent, structured home.
Core Concepts
There are three main entities and the relationships between them drive everything:
1. Demos (reusable project assets)
A demo is a structured description of a project: GitHub repo link, premise, problem statement, differentiating features, and an optional screencast. Demos are owned by teams (even solo
creators get a team-of-one). They exist independently from events — one demo can be submitted to many events over time.
Demos have a two-track state system:
- Status: draft → complete (are the required fields filled in?)
- Visibility: private → public (who can see it?)
The completeness check (src/lib/demo-completeness.ts) is a pure function — it just looks at whether title, repo URL, premise, problem, and differentiating features are filled in. It
runs bidirectionally: fill in the last field and you auto-transition to complete; clear one and you drop back to draft.
2. Events (curated demo sessions)
Events are what organizers create to run a demo night. They follow a strict lifecycle state machine (src/lib/event-state.ts):
draft → open_for_submissions → reviewing → live → completed → archived
The transition rules are intentionally restrictive — you can reopen submissions from reviewing, but you can never go backwards from completed. Events with approved submissions can't be
deleted (data protection).
3. Voting (two separate systems)
This is an interesting design choice — there are two independent voting mechanisms:
- Public upvotes: persistent social proof, like GitHub stars. Anyone can upvote a demo anytime.
- Audience votes: event-scoped ranking during a live demo session. Points-by-position for crowd wisdom.
Raw votes are stored individually (not pre-aggregated), so the ranking algorithm can be changed later without a data migration.
Architecture
Route Groups (Next.js App Router)
The app uses three route groups visible in the page structure:
┌──────────────┬────────────────────────────────────────────────────────────────────────┐
│ Group │ Purpose │
├──────────────┼────────────────────────────────────────────────────────────────────────┤
│ (auth)/ │ Login and signup pages │
├──────────────┼────────────────────────────────────────────────────────────────────────┤
│ (dashboard)/ │ Authenticated pages — demo management, event management, guest landing │
├──────────────┼────────────────────────────────────────────────────────────────────────┤
│ (public)/ │ Public-facing — homepage, demo browsing, individual demo pages │
└──────────────┴────────────────────────────────────────────────────────────────────────┘
API Layer (10 routes)
API routes handle mutations: creating/updating demos, validating GitHub repos, managing events and event submissions. Every route checks auth via Supabase and role via getUserRole().
Authorization Model
Authorization is enforced at the database layer via Supabase Row Level Security (RLS), not just in middleware. The middleware (src/lib/supabase/middleware.ts) handles session refresh
and route protection, but the real access control lives in Postgres policies. This means even if someone bypasses the API, the database won't let them read/write unauthorized data.
Role System
Users have roles (at minimum guest and builder). Guests land at /guest, builders get the full /dashboard. The getUserRole() helper in src/lib/auth.ts fetches the role from the users
table.
Theme System
The design system uses a 3-layer token architecture: raw color scales → semantic tokens → theme overrides. Components only reference semantic tokens like --text-heading or
--surface-page, and the theme switch (light/dark/system) just swaps the semantic values. A small inline script in layout.tsx reads the theme from localStorage before React hydrates to
prevent a flash of wrong theme.
★ Insight ─────────────────────────────────────
- The two-track state model (status vs visibility) is a smart pattern — it separates "is this ready?" from "who can see it?" as independent axes, avoiding a combinatorial explosion of
states.
- Storing raw votes instead of aggregated counts is a forward-looking decision: you can retroactively apply different ranking algorithms (Bayesian average, Wilson score, etc.) without
migrating data.
- The pure-function completeness check is decoupled from the database — it can run client-side for instant UI feedback and server-side for validation, with zero duplication of logic.
─────────────────────────────────────────────────
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment