cleanyourtv-app
Clean Your TV를 위한 커뮤니티 제작 모바일 앱
저장소 정보는 아직 수집하지 않았습니다.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
NZ Election 2026 — Compare the Parties: side-by-side policy comparison, ELI15 explanations, live news + manifesto watch bot, anonymous poll, electorate lookup.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Live: https://nz-election-2026-compare.vercel.app
A comparison of the 7 main parties contesting the 2026 New Zealand general election across 27 policy domains — each policy explained simply (ELI15 analogies), plus a bot-updated live news feed, real published opinion polls with a trend chart, campaign social-media moments decoded, a personalised "Find Your Match" tool, an anonymous leaning poll, and an electorate candidate lookup.
Editorial design system (Fraunces/Inter type, warm sunset-orange accent) over a real NZ landscape photo and real leader photos/party logos — all properly licensed, see Photo & logo credits below.
Pure HTML/CSS/JS on the frontend — no framework, no build step. The only "backend" is a GitHub Action (content bot) and two small Vercel serverless functions (poll + storage).
index.html Main comparison page (includes embedded Find Your Match)
find-your-match.html Standalone deep-link version of the same tool
constituency.html Electorate candidate lookup page
css/styles.css All styling (editorial theme, responsive)
js/data.js Parties, 27 domains, policies+analogies, leaders, profiles
js/app.js Renders the matrix, polls, decoder, manifesto watch, leaning poll, form
js/match.js Renders the Find Your Match profile/concern picker (used on both pages)
js/constituency.js Renders the electorate lookup page + interactive map
api/poll.js Vercel serverless function: anonymous poll (GET/POST)
api/feedback.js Vercel serverless function: suggestion form storage (POST/GET)
data/electorates.json Candidate data for all 64 general + 7 Māori electorates
data/news.json Bot-generated — latest filtered election news (RSS)
data/polls.json Manually-refreshed real published opinion polls
data/manifesto-watch.json Bot-generated — per-party change-detection status
data/manifesto-hashes.json Bot-internal — content hashes used to detect changes
scripts/fetch-updates.mjs The news/manifesto bot (runs on a schedule)
scripts/make-og-image.py Regenerates assets/og-image.png if you change the branding
scripts/build-electorate-map.mjs Builds assets/nz-electorates.svg from Stats NZ boundary data
.github/workflows/update-content.yml Cron job: runs the bot every 2h, commits changes
assets/hero-photo*.jpg Hero background (real photo, see credits)
assets/leaders/*.jpg Leader photos (real photos, see credits)
assets/logos/* Party logos (official party assets)
assets/nz-electorates.svg Interactive electorate map (built, see below)
assets/favicon.svg, og-image.png Tab icon + social preview
robots.txt / sitemap.xml SEO
netlify.toml / vercel.json Deploy configs (+ security headers)
package.json @vercel/blob dependency + local preview script
npm install
npm start
or just open index.html in a browser (the poll and some live-data
fetches won't work without a real deploy, since they need /api and
the GitHub Action's output).
npm i -g vercel
vercel --prod
Already linked to ravis-projects-053aad42/nz-election-2026-compare.
vercel.json handles routing/headers; the /api/poll.js function and
the connected Blob store (nz-election-votes) are what make the poll
work with zero extra signup — both live under the same Vercel
account already used to deploy.
Everything except the /api/poll and /api/feedback endpoints works fine as a plain static
deploy (drag-and-drop to Netlify, or GitHub Pages). The poll will show
"unavailable in this deployment" there, since those hosts don't run
Vercel's serverless functions — the rest of the site (comparison
matrix, news, manifesto watch, electorate lookup) is unaffected.
<script defer src="/_vercel/insights/script.js">
in index.html). Turn it on with one click: Vercel dashboard →
this project → Analytics tab → Enable. Free on the Hobby
plan, no separate account, no cookie banner needed.api/feedback.js, which stores submissions in the same Vercel Blob
store as the poll (not email, not a third-party form service). To
read submissions back: curl "https://<your-domain>/api/feedback?key=<FEEDBACK_READ_KEY>"
— the key is set as an env var on the Vercel project
(vercel env ls to confirm it exists; the value itself is stored as
a secret, so keep your own copy somewhere safe when you first set it
with vercel env add FEEDBACK_READ_KEY production).nz-election-votes,
created and connected via vercel storage create / vercel storage connect) — nothing further to do.api/poll.js is a tiny serverless function: GET returns current vote
totals, POST { party } casts/changes a vote. One browser = one vote,
enforced with a random id in a cookie (nzc_voter) — no name, email,
or IP is stored. Votes live in a single JSON file in Vercel Blob
(poll/votes.json), read-modify-written on each vote. This is a casual
engagement feature, not a scientific survey, and the UI says so.
Caveats: it's not abuse-proof (clearing cookies lets someone vote again), and read-modify-write isn't atomic (fine at low traffic, could drop a vote under heavy concurrent load). If this ever needs to be rigorous, swap the storage for a proper KV/DB with atomic increments.
constituency.html)All 64 general electorates and all 7 Māori electorates have
verified candidate data in data/electorates.json, sourced from
Wikipedia's "Candidates in the 2026 New Zealand general election by
electorate" page (fetched section-by-section via the MediaWiki API —
action=parse§ion=N — since the page is too long to fetch in one
shot without truncation). Candidate lists can change until nominations
close, so treat this as a strong starting point, not the final word —
the page links to the official vote.nz
lookup throughout.
To refresh: get the section index list from
https://en.wikipedia.org/w/api.php?action=parse&page=Candidates_in_the_2026_New_Zealand_general_election_by_electorate&prop=sections&format=json,
then fetch each electorate's section individually and update the
electorates / maoriElectorates arrays in data/electorates.json.
The electorate map (assets/nz-electorates.svg) is built from Stats
NZ's official "General Electorates 2025" boundaries — the actual 64
electorates used for the 2026 election — via their public ArcGIS
FeatureServer, simplified with mapshaper
(0.7% Visvalingam simplification; full-resolution boundaries are
~11MB, this is ~65KB) and projected to SVG with a small equirectangular
projection (scripts/build-electorate-map.mjs — no mapping library
needed at that scale). Electorate names in the SVG's data-name
attributes match data/electorates.json exactly; constituency.js
keeps the map and the dropdown in sync in both directions.
To rebuild the map (e.g. after a boundary change): re-run the ArcGIS
query in that script's comment, then
node scripts/build-electorate-map.mjs <input.geojson> assets/nz-electorates.svg.
Chatham Islands and other outliers are intentionally outside the fixed
mainland viewBox and simply don't render — pick them from the dropdown.
There is no free, reliable way to pull live tweets from X/Twitter
anymore, and scraping it violates its terms of service — so this bot
does not attempt that. The site's "Live: X/Twitter search" link is
still the right tool for real-time social content. What the bot does
automate, from scripts/fetch-updates.mjs (dependency-free Node,
fetch + crypto only):
data/news.json.data/manifesto-hashes.json). Flags "changed" with a
timestamp in data/manifesto-watch.json — it tracks that
something changed, never what, so you always go read the source..github/workflows/update-content.yml runs every 2 hours (cron
0 */2 * * *) automatically once on the default branch.node scripts/fetch-updates.mjs
Overwrites the three data/*.json bot files in place.
Edit NEWS_FEEDS, KEYWORDS, and MANIFESTO_SOURCES in
scripts/fetch-updates.mjs. Party sites get restructured during
campaigns — a "error" status in Manifesto Watch usually means a URL
moved. (Watch for look-alike domains: NZ First's real site is
nzfirst.nz, not nzfirst.org.nz, which is an unrelated parked
domain.)
js/data.js → POLICIES is
hand-written and simplified for comparison — not official party
positions, not auto-synced. The Manifesto Watch section links to
each party's real policy page.OTHER_PARTIES) is cross-checked against the
Electoral Commission's official Register of Political Parties and
Logos (as at 5 August 2026) — 17 parties total, 7 compared in detail
NEWS_ITEMS snapshot in js/data.js if the fetch fails.index.html and constituency.html both carry a descriptive
<title>/meta description, canonical URL, Open Graph + Twitter Card
tags (image at assets/og-image.png, regenerate with
python scripts/make-og-image.py if you change the branding), a
WebSite JSON-LD block, and a <noscript> fallback summary of the 7
parties for non-JS crawlers. robots.txt and sitemap.xml sit at the
project root. If you redeploy to a different domain, update the
canonical/OG URLs in both HTML files, robots.txt, and sitemap.xml.
Hero photo (assets/hero-photo*.jpg): Aoraki/Mt Cook sunrise from
Mueller Hut, by Michal Klajban,
CC BY-SA 4.0 — cropped/resized, credited in the hero itself.
Leader photos (assets/leaders/*.jpg), all from Wikimedia Commons,
cropped to square:
Party logos (assets/logos/*): each party's own official logo,
pulled from their current website (or Wikimedia Commons for ACT/Green,
both effectively public domain as simple geometric marks), used here
for editorial identification only — no endorsement implied. National,
NZ First and Te Pāti Māori's logos are white-on-transparent in their
official form, so they're composited onto a rounded tile in that
party's brand colour for legibility on light backgrounds — if a party
changes its logo again, re-fetch the source and recomposite the same
way (trim to bounding box, centre on a rounded rect filled with that
party's color from js/data.js).
Hand-written content (parties, domains, policies, analogies, minor
parties) lives in js/data.js. Bot-generated content lives in
data/*.json and is overwritten on each bot run. No build step either
way — edit and reload.