Skip to content

Add 404, mobile CTA, social cards, icon set, loading states and analytics hook - #87

Merged
scrimshawlife-ctrl merged 6 commits into
mainfrom
claude/amazing-hypatia-z2534v
Nov 11, 2026
Merged

scrimshawlife-ctrl merged 6 commits into
mainfrom
claude/amazing-hypatia-z2534v

Conversation

@scrimshawlife-ctrl

Copy link
Copy Markdown
Contributor

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.

# Item Before Now
1 Custom 404 Missing New 404.html with 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.io
2 CTA above the fold Desktop only; nothing at 390x844 In home.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 unchanged
3 Unique titles Present Now checked by npm test
4 Unique descriptions About, philosophy and terms were thin Those pages now use their own heading and lede copy
5 Open Graph and Twitter tags 2 of 30 pages All 30 pages. og-image.png is confirmed 1200x630
6 Favicon set Partial favicon.ico (16/32/48), 192/512 icons and site.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, and zero-state-mark.svg is a different mark that would need brand review
7 robots.txt Present Unchanged
8 sitemap.xml Present lastmod dates refreshed; all 30 pages listed
9 Alt text Present Now checked for every <img> in pages, inline scripts and JS templates
10 Loading states Partial Ask: an "Answering…" status line for screen readers and aria-busy while 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 readers
11 Privacy page Present and linked from every page The new 404 links it too, and the link is now checked
12 Analytics Missing site.js loads 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 match

The validator (scripts/validate-site.js) only gained checks; none were relaxed. 404.html is now a required page. workers/ask/knowledge.md was 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."
  • The new validator checks were tested against a scratch copy broken in nine different ways, and each break was caught.
  • Playwright confirmed:
    • the call to action is visible at 390x844 and 1440x900;
    • the 404 page renders at the root and on nested missing paths, on both hosts;
    • the beacon sends nothing with the empty token and adds the correct tag with a test token;
    • the Ask pending state appears, using a mocked Worker.
  • The live site isn't reachable from the sandbox, so live status codes weren't checked.

For the owner

  1. Paste the analytics token. In Cloudflare, go to Web Analytics → Add a site → zer0state.com, then paste the token into CF_WEB_ANALYTICS_TOKEN in site.js. Until then, the policy describes analytics that aren't running.
  2. Decide on the policy date. §14 promises a new effective date on revision. This PR follows the August 11 precedent: it changes "Last updated" to November 8 and leaves the effective date at August 7.
  3. The policy doesn't mention the Ask assistant. Questions go to the zs-ask Worker. This gap was there before this PR.
  4. Redeploy zs-ask. It serves knowledge.md, so it keeps the old privacy text until it's redeployed.
  5. Unlisted pages. games/, play/, roost.html, chat/ and concepts/ 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

  • Added or updated tests
  • Updated documentation if needed
  • Added the appropriate labels
  • Checked the linked GitHub Project item
  • Requested review from the Partner Agents team when appropriate

Generated by Claude Code

… 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
@scrimshawlife-ctrl
scrimshawlife-ctrl requested a review from a team as a code owner November 8, 2026 08:51
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 8, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-08T08:53:40.628716Z 3c57a5e PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

github-actions Bot commented Nov 8, 2026 •

Copy link
Copy Markdown

Pawl check: PASS

Gate definition: origin/main:pawl.toml

Suite Failures (baseline) Collected (floor) Orders
site-validation 0 (0) 1 (1) default
  • Note: gate definition from origin/main; stricter of base and head baselines

Pawl guard: PASS (base origin/main (b8c0178))

Gate definition: origin/main:pawl.toml

No tampering found.

  • Note: gate definition from origin/main

… 14 promises

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 prabu-openclaw left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@scrimshawlife-ctrl
scrimshawlife-ctrl merged commit 907d2fe into main Nov 11, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants