Repository navigation
Add 404, mobile CTA, social cards, icon set, loading states and analytics hook - #87
Conversation
… loading states, analytics
Owner request, 2026-11-08 ("Make sure all of our websites have all of this."):
- 404.html: branded not-found page in the shared chrome, noindex, primary
button home. A script-set <base> keeps its links working at any depth.
- Home: the "Begin here" pill shows from 24rem with one spacing step
tighter, so a primary CTA is above the fold at 390x844; the nav mark
keeps its 20 px. Desktop is unchanged.
- Every official page: Open Graph + Twitter Card meta with the shared
1200x630 image, favicon.ico (16/32/48) and site.webmanifest (192/512),
all resized from the existing mark 3 art. About, philosophy and terms
get descriptions taken from their own page copy.
- Ask widget: "Answering..." status line and aria-busy while a reply is
pending; Enter no longer aborts it. Chat preview log is a live region.
- Cloudflare Web Analytics loads from site.js only once
CF_WEB_ANALYTICS_TOKEN is pasted (empty now: nothing loads).
privacy.html section 2.3 discloses it; Last updated 2026-11-08.
- npm test asserts all of the above, plus alt on every <img> in pages
and JS templates. Ask knowledge regenerated for the privacy text.
- Intent records the owner's analytics decision and that the move to
Cloudflare hosting was deferred; the site stays on GitHub Pages.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0163byfa8ygGgJ2UjSDtgqBR
npm test regenerates sitemap.xml from each page's last commit date; every official page changed in the previous commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0163byfa8ygGgJ2UjSDtgqBR
…disclosure Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0163byfa8ygGgJ2UjSDtgqBR
Codex Review Summary: ✅ CompletedThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Pawl check: PASSGate definition:
Pawl guard: PASS (base origin/main (b8c0178))Gate definition: No tampering found.
|
… 14 promises Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0163byfa8ygGgJ2UjSDtgqBR
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0163byfa8ygGgJ2UjSDtgqBR
…Cloudflare's docs Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0163byfa8ygGgJ2UjSDtgqBR
prabu-openclaw
left a comment
There was a problem hiding this comment.
Approved. The validator additions are the strongest part of this: scripts/validate-site.js only gained assertions, 404.html became a required page, and the og:image dimension check (pngSize(...) !== '1200x630') means the 1200×630 claim is machine-enforced rather than asserted in prose. Testing nine deliberately-broken scratch copies to confirm each new check actually catches its failure is the right way to land validator changes. CI green on build and pawl.
Gating the Cloudflare beacon on both a non-empty token and location.hostname === 'zer0state.com' is correct — previews and the github.io host won't pollute the numbers. Worth recording explicitly so nobody trips over it later: a Cloudflare Web Analytics site token is not a secret. It is a client-side beacon identifier, visible in the DOM of every page that loads it, and committing it to a public repo is the intended deployment. When the token gets pasted in, that is not a leak and should not be treated as one.
Four notes, none blocking:
1. The 404 page's navigation is JavaScript-dependent, and nothing else on the site is. The <base> element is injected by script, so with JS off no base is set and every relative href on 404.html resolves against the missing path instead of the root — a 404 at /products/nonexistent resolves index.html to /products/index.html, which 404s too. So the one page whose entire job is recovering a lost visitor is the one page that can't recover them without JS. I understand why the hack exists: root-absolute hrefs would break the github.io/<repo>/ project URL, and that is a real constraint. But the cleaner resolution is a static <base href="/"> in the markup plus the script only overriding it on .github.io, which degrades to working links on the custom domain (the host that actually matters) and keeps the project URL working when JS runs.
2. Edge case in the base computation. location.pathname.split('/')[1] yields '' when pathname is exactly /, making base.href // — a protocol-relative URL with an empty host. That path isn't reachable for a project page (an org's github.io root serves the org Pages repo, not this one), so this is theoretical today, but it is a one-line guard if the hosting arrangement ever changes.
3. The PASS line undercounts and sent me chasing a phantom inconsistency. The summary prints requiredPages.length → PASS: 28 pages, while the body says 30 pages and 2 of 30. I fetched sitemap.xml at this PR's head to reconcile it: 30 <loc> entries, and per line 456 the metadata/social-card loop covers every manifest page plus every required page, not just the 28. So the numbers are consistent — they count different sets — but the printed line reads like a total. Suggest either printing the union count or labelling it 28 required pages so the next reader doesn't repeat the diff I just did.
4. The host gate is exact, so www is uncounted. location.hostname === 'zer0state.com' won't match www.zer0state.com. Fine if www 301s to apex before any JS runs; worth confirming, because a silent half-count is worse than no analytics.
On your owner item #1 — disclosing the analytics in privacy.html §2.3 before the beacon is live is the correct order of operations, not a problem to fix. Over-disclosure is the safe direction; shipping the beacon ahead of the policy would be the error. Leave it.
Flagging to Aaron rather than resolving here: the analytics token paste, the effective-date decision in §14, the zs-ask redeploy (it serves knowledge.md, so it keeps serving the old privacy text until redeployed), and the gap you identified where the policy never mentions the Ask assistant even though questions reach the zs-ask Worker. That last one is a pre-existing gap rather than something this PR introduced, but it is the one with actual substance — the policy should say where typed questions go.
Summary
This brings zer0state.com up to a 12-item launch checklist the owner asked for on 2026-11-08. The site stays on GitHub Pages; the owner put the move to Cloudflare on hold that day. The intent file is
intent/2026-11-08-real-website-checklist.md.404.htmlwith the shared header and footer, noindex, a button home and a portfolio link. A small<base>script keeps links working on missing paths at any depth, on both zer0state.com and github.iohome.css, the "Begin here" pill now shows from 24rem up. The nav gap and padding are one step tighter between 24rem and 40rem so the mark stays 20px. Desktop is unchangednpm testog-image.pngis confirmed 1200x630favicon.ico(16/32/48), 192/512 icons andsite.webmanifest, linked on every page. All are resized from the existing mark 3 art. No SVG icon: that mark only exists as a raster image, andzero-state-mark.svgis a different mark that would need brand reviewlastmoddates refreshed; all 30 pages listed<img>in pages, inline scripts and JS templatesaria-busywhile a reply is pending. Pressing Enter no longer stops a pending answer (only the Stop button does). Chat: the message log is announced to screen readerssite.jsloads the cookieless Cloudflare Web Analytics beacon from one constant,CF_WEB_ANALYTICS_TOKEN. It is empty, so nothing loads yet.privacy.html§2.3 discloses the analytics, and §12 (Do Not Track) is updated to matchThe validator (
scripts/validate-site.js) only gained checks; none were relaxed.404.htmlis now a required page.workers/ask/knowledge.mdwas regenerated after the privacy edit.Related issue or project item
None. The owner asked for this directly on 2026-11-08.
Testing performed
npm test: "PASS: 28 pages, sitemap/robots, product heroes, dark home, social cards, icons, 404, alt text, internal links, metadata, and product-source posture validated."For the owner
CF_WEB_ANALYTICS_TOKENinsite.js. Until then, the policy describes analytics that aren't running.knowledge.md, so it keeps the old privacy text until it's redeployed.games/,play/,roost.html,chat/andconcepts/are publicly served but not in the sitemap, and some have no noindex or description. Those builds come from their own source projects, so fixes belong upstream.Collaboration checklist
Generated by Claude Code