OpenData Vibe Assessment: Zachary Zeff

# NYC Open Data — vibecoding submission

**Question.** The platform for OpenDataNYC hasn't changed much since 2022. What are web (HTML/CSS/JS) features that you would vibecode on top of Open Data's capabilities and datasets? Submit your LLM prompt.

## Part 1 — Answer

I graded NYC Open Data against 16 HCI practices (Nielsen's 10 heuristics, WCAG 2.2, and open-data-portal research) using only what I could verify: a live 311 dataset page, the old and new education sites, the official guides, the 311 dataset description, and a community board's own instruction manual. Eleven practices had evidence; median score 3 / 5, range 2 to 4. The weakest are real-world match, consistency, and recognition over recall: the official guide starts filtering from a column called "Unique Key", the dataset page still links over http to an older site whose tutorial describes a 311 dataset that has since been split, and Manhattan Community Board 3 wrote a six-step manual just to look up 311 requests by address. Socrata documents CORS support for its API, so features 1, 3, 4, 5, 7, 8, and 10 run as static HTML/CSS/JS pages with no backend; 2, 6, and 9 need one small extra piece, noted inline. Each feature targets an evidenced gap (practice numbers in parentheses; full list in Appendix A):

1. Guided dataset page — plain-English summary, freshness badge, filter chips with real values, live match count, export with size shown, copy-API-URL (rows 2, 5, 6, 10).
2. Ask-the-data bar — natural language to SoQL, showing the query it generates so users learn the API; the LLM key needs a small proxy or a user-supplied key (2, 6, 7).
3. "What's near me" page — geolocation plus `within_circle()` across 311, crashes, restaurant grades, trees, and rodent inspections, so people find data by place instead of by column name (6, 14).
4. Building report card — address to BBL via Geosearch, then HPD violations, DOB permits and complaints, 311, evictions, and PLUTO on one page (6, 14).
5. Neighborhood choropleth with time slider — server-side `$group` counts joined to district or ZIP boundaries (14).
6. Saved-query watchers — poll a saved SoQL query while the page is open and notify on new rows; background push needs a small server or a scheduled job (1).
7. Embeddable web components — `<nyc-chart dataset="…">` for community boards and newsrooms (7).
8. Freshness dashboard and SoQL playground — flag datasets behind their update schedule; shareable query permalinks (1, 7).
9. Translate-this-dataset layer — the site menus come in 12 languages but dataset descriptions and dictionaries don't; pre-translate them at build time, ship the JSON, and label it machine-translated (12).
10. Search-first homepage — dataset search with example queries above the fold, popular datasets next, browse and onboarding below; the old homepage leads with program links and reaches data only in its second section (4, 14).

First weekend: features 1, 3, and 4. They share the same filter-and-count code.

## Part 2 — LLM prompt

*Paste as the first message to Claude, ChatGPT, Cursor, or Claude Code. Fill in or delete the [brackets]. Tags keep sections unambiguous for the model; leave them in.*

---

<role>
You are a senior front-end engineer and civic-tech UX designer who has shipped tools on Socrata-based open data portals. You care about usability evidence, not feature lists.
</role>

<goal>
Help me answer "what web (HTML/CSS/JS) features would you vibecode on top of NYC Open Data's capabilities and datasets?" and then build the best one as working code, not a plan. My own answer is in <candidate_features>; validate it against the evidence, improve it, then build the best item. Every feature must fix a measured usability gap (see <known_gaps>), run as a static page, and be verifiable against the live API.
</goal>

<about_me>
[Audience: e.g. residents and community boards / journalists / me]
[Skill and time: e.g. comfortable with JS, one weekend]
[Feature already in mind, if any]
Defaults if blank: audience = residents and community boards, time = one weekend, no feature preselected.
</about_me>

<ground_truth>
Verified September 2026. Treat as fact; verify everything else against the live API.
- Data platform: https://data.cityofnewyork.us (Socrata / Tyler "Data & Insights"). Education site: https://www.nyc.gov/opendata (user guides, classes, gallery, FAQs, reports, contact form for dataset questions and error reports, 12-language menu). The older site https://opendata.cityofnewyork.us is still live with a pre-2020 How To, and dataset pages still link to it over http; its homepage points to the external Scout tool for related datasets.
- Every dataset has a "4x4" ID, e.g. `erm2-nwe9`.
- Classic read API (SODA 2.1, GET, CORS-enabled, works from any static page):
  `https://data.cityofnewyork.us/resource/{ID}.json?$select=…&$where=…&$group=…&$order=…&$limit=…&$offset=…`
  Also `.csv` and `.geojson`. Max 50,000 rows per request; deep paging slows down.
- New API (SODA3): `POST https://data.cityofnewyork.us/api/v3/views/{ID}/query.json`
  Body `{"query": "SELECT … WHERE … GROUP BY … ORDER BY …", "page": {"pageNumber": 1, "pageSize": 1000}}`.
  Requires an `X-App-Token` header, which triggers a CORS preflight from a browser; confirm the endpoint answers it from a static page before relying on it, or use SODA 2.1 with `$$app_token` as a query parameter (no preflight). Returns JSON, CSV, or GeoJSON. Use for long or complex queries.
- App tokens are free (portal profile → Developer Settings). They identify the app for rate limiting, not authenticate it, so shipping one in client-side JS is normal. Send as `X-App-Token` header or `$$app_token` param. Expect HTTP 429; back off.
- Useful SoQL: `count(*)`, `sum()`, `avg()`, `min()`, `max()`, `date_trunc_ym()`, `date_trunc_ymd()`, `date_extract_dow()`, `date_extract_hh()`, `within_circle(point_col, lat, lng, meters)`, `within_box()`, `within_polygon()`, `intersects()`, `distance_in_meters()`, `$q=<full text>`, `case()`, `LIKE`, `starts_with()`. Dates are floating timestamps like `'2026-08-15T00:00:00'`. System fields `:id`, `:created_at`, `:updated_at` (select with `$select=:*,*`).
- Column field names are lowercase snake_case and differ from display names. Fetch them before writing any code:
  `https://data.cityofnewyork.us/api/views/{ID}.json` → `columns[].fieldName`, `dataTypeName`, `description`.
- Catalog search (Discovery API): `https://api.us.socrata.com/api/catalog/v1?domains=data.cityofnewyork.us&q=…` → IDs, descriptions, `updatedAt`.
- Geocoding: `https://geosearch.planninglabs.nyc/v2/search?text=…` (free, no key). Returns lat/lng plus BBL and BIN under `properties.addendum.pad`, the keys that join HPD, DOB, PLUTO, and ACRIS datasets. Not re-tested in this review; run one query before relying on the field names.
- Boundary files (community districts, NTAs, ZIP/MODZCTA) are published on the portal; find the IDs and confirm GeoJSON export via the Discovery API.
- 311 is split: `erm2-nwe9` = 2020 to present (daily); `76ig-c548` = 2010–2019. The old How To (still served) describes the pre-split dataset and starts filters from a column called "Unique Key"; the 311 description still carries an undated Covid-era caveat.
- Legal bar: ADA Title II holds public entities to WCAG 2.1 AA; build to WCAG 2.2 AA. Language access: the nyc.gov/opendata menus come in 12 languages (the Local Law 30 set); dataset pages showed no language switch when fetched.
- Prefer server-side aggregation (`$group` / `GROUP BY`) over downloading raw rows.
</ground_truth>

<known_gaps>
Heuristic review using only verified evidence: the live 311 dataset page (erm2-nwe9), the old site and its How To, the new nyc.gov/opendata site, the 311 dataset description, and Manhattan CB3's 311 lookup manual. Score 1–5, 5 = meets the practice. The old How To and the CB3 manual describe the portal as of about 2020; rows that rest on them are marked "documented ~2020" and were not observed live. Not assessed (no screen test of the grid or chart builder): 8 minimalist design, 9 error recovery, 13 mobile-first, 15 provenance and quality, 16 two-way engagement. Don't claim gaps there without checking first.
| Row | Practice | Score | Evidence |
|---|---|---|---|
| 2 | Real-world match | 2 | Official guide starts filtering from "Unique Key" and uses "primer", "SoQL", "endpoint"; CB3 manual tells users to enter several spellings of one address (documented ~2020; the jargon is confirmed in the guide still served) |
| 4 | Consistency | 2 | Three surfaces (old site, nyc.gov site, Socrata) with different navigation; dataset page nav links to the old site over http |
| 6 | Recognition over recall | 2 | A community board wrote a six-step manual to look up 311 requests by address (manual dates from about 2020) |
| 1 | Status visibility | 3 | Last-updated date shown on dataset pages; 311 description carries an undated Covid-era caveat |
| 3 | User control | 3 | Visualize flow prompts for sign-in (optional) per the guide (documented ~2020) |
| 5 | Error prevention | 3 | Guide tells users to filter large datasets down themselves to avoid trouble (documented ~2020) |
| 10 | Help and docs | 3 | New user guides exist; old How To still served with a pre-split 24M-row tutorial, a broken bullet, and "contains" described as a range filter |
| 11 | Accessibility | 3 | Skip link and mobile viewport on dataset pages; OTI accessibility statement linked from the new site; grid and charts not audited |
| 12 | Language access | 3 | nyc.gov/opendata offers 12 languages; dataset pages showed no language switch |
| 14 | Discoverability | 3 | Browse by agency and category; related-dataset exploration delegated to the external Scout tool on the old site; old homepage leads with program links and reaches data links only in its second section |
| 7 | Flexibility | 4 | Full SoQL API (SODA 2.1 and SODA3); saving views requires an account per the guide (documented ~2020) |
</known_gaps>

<reference_design>
Redesigned dataset page, mobile-first, as a static overlay that reads the API. Use it as a starting point or a source of smaller features:
1. Plain-English summary, publisher chip, and an "updated · on schedule" badge above the fold.
2. "Ask this data" box with example questions; it shows the SoQL it generates.
3. Filter chips with real values (from a `$group` query), an address chip backed by Geosearch, and a live match count.
4. Cards / map / table switch; cards default under 600 px.
5. A few key columns shown by default, "show all columns" one tap away.
6. Export with row count and file size up front; "copy API URL" reproduces the current filters.
7. Collapsed below the fold: data dictionary, related datasets, public question/issue thread.
Homepage, search-first: dataset search with example queries, then popular datasets, then browse by agency or category, then a short new-here strip (guide, class, contact).
</reference_design>

<candidate_features>
My answer to the question. Keep, merge, or replace these; add a new one only if it beats the weakest candidate on the ranking formula. Rows refer to <known_gaps>.
1. Guided dataset page — rows 2, 5, 6, 10 · erm2-nwe9 · pick-lists via `$group`, count via `count(*)`, state in URL · M
2. Ask-the-data bar — rows 2, 6, 7 · any dataset · natural language → SoQL with the query shown; pick-list fallback when no LLM; the key needs a proxy or is user-supplied · M
3. What's near me — rows 6, 14 · erm2-nwe9, h9gi-nx95, 43nn-pn8j, uvpi-gqnh, p937-wjvj · `within_circle(<point column>, lat, lng, 300)`; verify each dataset's point column name · S
4. Building report card — rows 6, 14 · Geosearch → BBL/BIN → wvxf-dwi5, ic3t-wcy2, eabe-havv, 6z8x-wfk4, 64uk-42ks, erm2-nwe9 · `$where=bbl='…'` (verify the BBL field name per dataset) · M
5. Neighborhood choropleth + time slider — row 14 · any dataset with a district or ZIP column, plus boundary GeoJSON · `$select=<area>,count(*)&$group=<area>`, `date_trunc_ym()` · M
6. Saved-query watchers — row 1 · any dataset · poll `created_date > <last check>` or `:updated_at` while open and notify; background push needs a small server or scheduled job · M
7. Embeddable web components — row 7 · any dataset · `<nyc-chart>` custom element wrapping one `$group` query · S
8. Freshness dashboard + SoQL playground — rows 1, 7 · Discovery API `updatedAt` vs `max(<date column>)` per dataset; `#query=` permalinks · S
9. Translate-this-dataset layer — row 12 · `/api/views/{ID}.json` metadata · pre-translate descriptions and dictionary entries into the 12 site languages at build time, ship JSON, label it machine-translated · S
10. Search-first homepage — rows 4, 14 · Discovery API for search and popular lists · reorder to search with example queries, popular, browse, onboarding · S
</candidate_features>

<starter_datasets>
Verify ID and columns before use. Only erm2-nwe9, 76ig-c548, and 43nn-pn8j were confirmed in this review; the rest are from memory.
311 Service Requests 2020–present: erm2-nwe9 · 311 2010–2019: 76ig-c548 · Motor Vehicle Collisions – Crashes: h9gi-nx95 · DOHMH Restaurant Inspection Results: 43nn-pn8j · HPD Housing Maintenance Code Violations: wvxf-dwi5 · DOB Job Application Filings: ic3t-wcy2 · DOB Complaints Received: eabe-havv · Evictions: 6z8x-wfk4 · PLUTO: 64uk-42ks · 2015 Street Tree Census: uvpi-gqnh · Film Permits: tg4x-b46p · Rodent Inspection: p937-wjvj · Open Parking and Camera Violations: nc67-uf89 · NYPD Complaint Data (YTD): 5uac-w243 · Citywide Payroll: k397-673e
</starter_datasets>

<constraints>
Technical
- One `index.html` with inline CSS/JS (three files max). Runs on GitHub Pages. No backend and no framework for the core feature; if a feature needs a secret or a scheduler (LLM call, push alerts, translation), isolate it in one small serverless function or a build-time step and say so.
- CDN libraries only when they earn their place: Leaflet or MapLibre for maps, Chart.js for charts.
- `AbortController` for stale requests, 429 backoff, in-memory cache per session, `localStorage` only for saved queries and preferences.
- Every query the app runs is visible and copyable. The feature should teach the API, not hide it.
- If an LLM call is part of the feature (natural language → SoQL), isolate it in one function with a clear input/output contract and a no-LLM fallback (pick-lists).

Definition of done (one line per practice; the build must satisfy all)
- Status: every fetch shows loading, result count, and last-updated date.
- Real-world match: labels come from the data dictionary; no raw field names on screen; dates read "2 hours ago" with the exact time on tap.
- Control: URL holds all state; back button works; "clear all"; no login prompts.
- Consistency: one type scale, one button style, sentence case.
- Error prevention: row estimate before any export; pulls above 50,000 rows route to an API URL instead.
- Recognition: filter values come from live `$group` queries, never typed from memory.
- Efficiency: copy-query button; `/` focuses search, `Esc` clears.
- Minimalist: one primary action per screen; key columns by default.
- Error recovery: plain-language messages with a next step; never raw JSON.
- Help: inline "?" tips and one example query per control.
- Accessibility (WCAG 2.2 AA): keyboard reachable, visible focus, 4.5:1 contrast, 24×24 px targets, `aria-live` for results, a table alternative for every chart.
- Language: plain language; all UI strings in one object ready for translation; `lang` attribute set.
- Mobile: works at 360 px with no horizontal scroll; filters open as a bottom sheet.
- Discoverability: related datasets or join keys named on screen.
- Provenance: source agency, update frequency, freshness badge, % null for key columns.
- Engagement: a feedback link per dataset.

Task tests (time each on the current portal first as a baseline; the build passes when they work without reading docs)
- T1 · On a phone, a first-time user filters 311 to noise complaints in Bushwick in the last 30 days and sees a count in under 60 seconds.
- T2 · A community board member downloads open requests on one block, seeing row count and file size before the download starts.
- T3 · A journalist copies the API URL for the current view and reproduces the same count in a browser tab.
</constraints>

<process>
0. Clarify: ask at most one question if <about_me> leaves something blocking; otherwise use the defaults.
1. Ideate: start from <candidate_features>. Keep, merge, or replace them, and add a new feature only if it beats the weakest candidate on the ranking formula. For each: scorecard rows it moves with before → after scores, datasets (IDs), the exact SoQL it leans on, effort S/M/L, a 30-second demo, and what must be verified before building. Rank by (rows moved × score gain) ÷ effort. Stop and ask which to build; if I say "go", build the best S or M.
2. Verify: fetch `/api/views/{ID}.json` for each dataset and list the real field names and types you'll use. Run two sample queries (or show exactly what to run) and paste representative rows. Confirm the endpoint you chose works from a static page. If you can't fetch, stop and ask me to paste the metadata JSON and query results before writing any code.
3. Build: complete code in one block, then a README: what it does, how to run, the queries it issues, known limits.
4. Self-review: score the build 1–5 on all 16 practices with one line of evidence each; fix anything at 2 or below before delivering; walk through T1–T3 step by step.
5. Stretch: three upgrades ranked by value ÷ effort.
</process>

<output_format>
Wrap each stage in its tag: <ideas> (table), <verification>, <build> (code), <readme>, <self_review> (table), <stretch>. Finish with <answer>: a 120–180 word answer to the question "what web features would you vibecode on top of NYC Open Data?" grounded in the ranked list, written so I can submit it as-is.
</output_format>

<examples>
Good feature card: "Guided 311 filter. Moves rows 2, 5, 6 from 2 → 4. Dataset erm2-nwe9. Pick-list query: `$select=complaint_type,count(*) AS n&$group=complaint_type&$order=n DESC&$limit=25`. Count query: `$select=count(*)&$where=created_date > '2026-08-15T00:00:00' AND borough='BROOKLYN' AND complaint_type LIKE 'Noise%'`. Effort M. Demo: tap three chips, watch the count change. Verify: `complaint_type`, `borough`, `created_date`, `incident_address`."
Bad feature card: "Filter 311 by `neighborhood`." Don't name a column you haven't seen in `/api/views/{ID}.json`; if it isn't there, say so and use a boundary join instead. If you're not sure a dataset, column, endpoint, or function exists, check. Never invent one.
</examples>

<style>
Terse. Code over commentary. Comment the SoQL, not the CSS. Sentence case in UI strings. Don't call anything "easy" or "seamless"; say what it does.
</style>

## Appendix A — The 16 practices

1 Status visibility (Nielsen 1) · 2 Real-world match (Nielsen 2) · 3 User control (Nielsen 3) · 4 Consistency (Nielsen 4) · 5 Error prevention (Nielsen 5) · 6 Recognition over recall (Nielsen 6) · 7 Flexibility (Nielsen 7) · 8 Minimalist design (Nielsen 8) · 9 Error recovery (Nielsen 9) · 10 Help and docs (Nielsen 10) · 11 Accessibility (WCAG 2.2 AA, ADA Title II) · 12 Language access (NYC Local Law 30) · 13 Mobile-first (WCAG 1.4.10 reflow) · 14 Discoverability (Shneiderman's mantra) · 15 Provenance and quality (W3C Data on the Web Best Practices) · 16 Two-way engagement (open-data-portal usability research)

## Appendix B — Sources checked (September 14, 2026)

- 311 dataset page (nav links, skip link, viewport; body is JavaScript-rendered): https://data.cityofnewyork.us/Social-Services/311-Service-Requests-from-2020-to-Present/erm2-nwe9
- 311 description and the 2010–2019 split: https://data.cityofnewyork.us/Social-Services/311-Service-Requests-from-2010-to-2019/76ig-c548/about_data
- Old site and its How To: https://opendata.cityofnewyork.us/ and https://opendata.cityofnewyork.us/how-to/
- New site: https://www.nyc.gov/opendata/get-started/what-is-open-data- and https://www.nyc.gov/opendata/get-started/user-guides
- Manhattan CB3 311 lookup manual: https://www.nyc.gov/html/mancb3/downloads/cb3docs/instruction-manual-311-srs-on-opendata.pdf
- SODA3 and SoQL: https://dev.socrata.com/docs/endpoints.html, https://dev.socrata.com/docs/queries/, https://support.socrata.com/hc/en-us/articles/34730618169623-SODA3-API
- Heuristics: https://www.nngroup.com/articles/ten-usability-heuristics/ · WCAG 2.2: https://www.w3.org/TR/WCAG22/
- Open-data-portal usability research: https://arxiv.org/abs/2406.08774 and https://www.sciencedirect.com/science/article/abs/pii/S0736585320301982
- Not checked: the nyc.gov/opendata homepage, the Socrata grid, filter panel, chart builder, and export flow.