vccs
Composable charting components for Vue 3, ported from Recharts
Find it. Understand it. Check where it came from. Milwaukee's public data from Data You Can Use and the City of Milwaukee, explained.
DataTypeScript · HTMLGitHub @tmoody1973OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Find, understand, and download Milwaukee's public data. Unofficial; built on Data You Can Use's public data.
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
A guide to Milwaukee's public data. Find it. Understand it. Check where it came from.
An unofficial, phone-first guide to two public catalogs: Data You Can Use's neighborhood data and the City of Milwaukee's open data. Not affiliated with either. Built by Tarik Moody.
Live: https://www.creamcityalmanac.app · New here? Start here · How it works, a real dataset sheet annotated in plain English.Read the full README ↗Look up your Milwaukee neighborhood. Every number says where it came from.
An unofficial, phone-first guide to Milwaukee's public data: Data You Can Use's neighborhood data, the City of Milwaukee's open data, Health Compass Milwaukee's ZIP code measures, and the U.S. Census Bureau's survey. Not affiliated with any of them. Built by Tarik Moody.
Live: https://www.creamcityalmanac.app · New here? What's in the Almanac · Start here · How it works, a real dataset sheet annotated in plain English.
Milwaukee publishes a lot of data, in two places that don't talk to each other. Data You Can Use (DYCU) publishes what neighborhoods are like (income, housing, health) on an ArcGIS Hub: 382 items with thin descriptions and cryptic column names. The City publishes what happens in them (crimes, fire and EMS calls, 311 requests, permits, property sales): 186 datasets on its CKAN portal and map server. Cream City Almanac puts both in one list a reporter can use on deadline, on a phone:
F02, so the 2022, 2023 and 2024 versions of one measure, or a City dataset's Current and Historical files, live together. City rows carry a CITY tag; daily feeds carry a LIVE mark./n to see your neighborhood, alder and aldermanic district, voting ward, and polling place (with its accessible entrance). The address goes to the Census Bureau's geocoder and is never stored.Status: live and in public use. Decisions, with what each one cost, are in docs/decisions/.
A weekly job (a Convex cron, a function that runs on a schedule, Mondays 09:00 UTC) rebuilds the catalog:
Unchanged items are skipped, so a typical weekly run costs cents. A failed run never replaces the live catalog.
When someone asks a question, Ask's AI picks a tested tool: search the catalog, read a dataset sheet, count City records, look up an address, rank or compare neighborhoods, describe a ZIP code, or read stored history. Counts run as live, read-only queries against the City's own database, built only from the columns and values in that dataset's profile, so the AI never writes the query. Maps are built the same way: the City returns quarter-mile cell counts, never coordinates, and only the cells reach the browser.
| Layer | Technology |
|---|---|
| Web app | Next.js 16.4 (App Router), React 19.3, CSS Modules |
| Backend, database, search, cron | Convex 1.46 (full-text and vector indexes) |
| Sign-in | Clerk 7 (needed for Ask only; browsing is open) |
| Ask chat | CopilotKit 1.77 and the AI SDK, Claude Sonnet 5.5 through the Vercel AI Gateway |
| Maps | MapLibre GL JS 6.11 with OpenFreeMap streets (free, no key); City layers from the City's ArcGIS map server |
| Rate limiting | @convex-dev/rate-limiter: search embeddings, Ask, City counts, tract reads, address lookups, the public map |
| AI | Claude Sonnet 5.5 for dataset cards and Ask; OpenAI text-embedding-3-small for search; all through the Vercel AI Gateway |
| PDF reading | Firecrawl v2 |
| Source data | DYCU ArcGIS Hub (DCAT feed and FeatureServer API); City of Milwaukee CKAN (datastore_search_sql) and ArcGIS REST; U.S. Census Bureau ACS 5-year API and geocoder; Health Compass Milwaukee |
| Tests | Vitest 5 + convex-test, Playwright 1.63 + axe (phone and desktop), an Ask report card graded against the real AI |
| Design | Impeccable comp-led build; system recorded in DESIGN.md |
git clone https://github.com/tmoody1973/cream-city-almanac.git
cd cream-city-almanac
npm install
npx convex dev --once # creates your Convex project and writes .env.local
Add the browser's copy of your Convex URL to .env.local (same value as CONVEX_URL):
echo "NEXT_PUBLIC_CONVEX_URL=$(grep '^CONVEX_URL=' .env.local | cut -d= -f2-)" >> .env.local
Set the build's keys in Convex. Type them in your own terminal; never commit them:
npx convex env set AI_GATEWAY_API_KEY <your-key>
npx convex env set FIRECRAWL_API_KEY <your-key>
For Ask, add your Clerk keys to .env.local (NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY, CLERK_SECRET_KEY), set CLERK_FRONTEND_API_URL in Convex, and set the same random ASK_METER_SECRET in both .env.local and Convex (npx convex env set ASK_METER_SECRET <random-string>).
Build the catalog once (about 25 minutes on Firecrawl's free plan, which reads report PDFs 7 seconds apart; roughly $1 of AI on the first run), then start the site:
npx convex run build:start
npm run dev # http://localhost:3000
| Variable | Where | Purpose | Required |
|---|---|---|---|
CONVEX_DEPLOYMENT | .env.local | Which Convex deployment the CLI talks to (written by npx convex dev) | Yes |
CONVEX_URL | .env.local | Convex URL for server-side calls (written by npx convex dev) | Yes |
NEXT_PUBLIC_CONVEX_URL | .env.local, Vercel | Convex URL for the browser | Yes |
AI_GATEWAY_API_KEY | Convex env (and .env.local for Ask locally) | Card writing, search embeddings, and Ask's model. On Vercel, Ask uses the project's built-in OIDC token (a sign-in Vercel issues automatically) instead | For builds, meaning search and Ask |
FIRECRAWL_API_KEY | Convex env | Reading report PDFs | For builds |
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY, CLERK_SECRET_KEY | .env.local, Vercel | Sign-in | For Ask |
CLERK_FRONTEND_API_URL | Convex env | Lets Convex check Clerk's sign-in tokens (CLERK_PREVIEW_FRONTEND_API_URL for preview deploys) | For Ask |
ASK_METER_SECRET | .env.local, Vercel, Convex env | Lets the Ask route record each answer's AI spend | For Ask |
CENSUS_API_KEY | Convex env | The Census Bureau's survey: City and tract figures for fact sheets, ZIP profiles, tract populations. Never written to code, files or logs | For census figures |
E2E_CLERK_USER_EMAIL | .env.local | A test user for the signed-in browser tests | For e2e |
TEST_ACCOUNTS | Convex env, dev only | Comma-separated verified emails that get 10x the per-account City and tract limits, so back-to-back test runs don't drain them. Never set it on production | For e2e |
Without AI_GATEWAY_API_KEY, search still works on keywords only and says so. Ask's limits live in the Convex settings table: 30 questions a day per person (200 for newsroom email domains) and a $10 site-wide daily AI cap, after which Ask pauses until tomorrow.
| Command | What it does |
|---|---|
npm run dev | Next.js dev server |
npm run build / npm start | Production build / serve it |
npm test | Unit and Convex tests (Vitest) |
npm run typecheck | TypeScript check for the app (npx tsc --noEmit -p convex checks the backend) |
npm run e2e | Playwright on an iPhone 13 and a 1440px desktop, including axe accessibility checks; starts the dev server itself. Ask tests use a scripted model, never the real AI |
npm run capture | Design screenshots for Impeccable's comp comparison |
npm run fixtures | Re-pins the Hub catalog and inventory test fixtures |
npm run ask:card | Asks the real AI a set of reporter questions and grades the answers (costs a few cents per question; --only=N runs one) |
npx tsx scripts/city-sql-smoke.ts | Checks that the City's live database still accepts every kind of query Ask builds |
npx convex run evals:searchReportCard | Grades search on 26 reporter-style questions (target: 80%+ in the top 3) |
app/ Next.js routes: front door (/), search (/search), dataset sheets (/d/[code]), neighborhood fact sheets
(/n, /n/[slug]), Ask (/ask, /ask/guide), shared cards (/s/[id]), What's in the Almanac (/about),
Start here, How it works, and the Ask API route (api/copilotkit)
ui/components/ Rundown rows, dataset sheets, Ask panel and cards, the map, grease-pencil marks
ui/lib/ Pure UI logic: formatting, marks, address parameters, map cells and layers
lib/ask/ Ask's prompt, tools and model wiring
convex/ Catalog build, search, Ask, City counts, the map, cron, rate limits
convex/lib/ Pure backend logic: Hub and CKAN parsing, grouping, City SQL, neighborhoods, the map grid
tests/ Vitest unit tests and pinned fixtures
e2e/ Playwright tests (phone and desktop) and design captures
scripts/ The Ask report card, the City query smoke test, fixture tools
public/plates/ Grease-pencil images as served (originals with provenance in assets/plates/)
public/maplibre/ MapLibre's worker files (a test fails if they drift from the installed version)
docs/decisions/ Decision records: what was decided, why, and what it cost
docs/superpowers/ Design specs and implementation plans
DESIGN.md The "Rundown" design system
PRODUCT.md Product brief: users, purpose, principles
AI on screen. Numbers shown on a sheet come from the data, never from the AI.text-embedding-3-small. Public search is capped at 600 per hour (bursts up to 100); past the cap it falls back to keywords with a notice.settings.buildCapUsd).DESIGN.md: the Rundown design system (a radio show's rundown sheet: ruled rows, permanent codes, red used only as grease-pencil marks, including the line drawn around the area a map count covers).docs/decisions/: 55 numbered decision records, each with the options considered and the honest downside.MIT © 2026 Tarik Moody. Data © Data You Can Use, the City of Milwaukee, and their sources; see each dataset's source page for terms.
/about, which explains the whole product with its own working parts.