Skip to content

Fix chart distortion, 404 cache poisoning, and invisible prose links - #6

Merged
VeteranXYZ merged 1 commit into
mainfrom
review-fixes
Sep 2, 2026
Merged

Fix chart distortion, 404 cache poisoning, and invisible prose links#6
VeteranXYZ merged 1 commit into
mainfrom
review-fixes

Conversation

@VeteranXYZ

Copy link
Copy Markdown
Owner

Four defects, then the waste and drift they sat next to.

Bugs:

  • The Worker chose Cache-Control by path without checking status, so a 404 under /_astro/* or /grain.png was served immutable for a year. A half-rolled deploy or stale HTML pointing at a departed hashed asset would pin that error in every cache downstream. Errors now get 60s must-revalidate. The styled 404 also covers every method now, with a body only on GET, so HEAD and POST stop leaking the asset layer's own 404 payload.

  • Frame 2 pinned a 1100x480 viewBox to a fluid width with preserveAspectRatio="none". At a 375px viewport that is 0.45 horizontal distortion: cursor dots rendered as 3x6.7 ellipses and the event labels collapsed into overlapping mush. The viewBox now tracks the element's own CSS box, so both axes scale by exactly 1. Narrow layouts drop the event labels and thin the year axis rather than overprinting them. Measured on mount and on resize rather than with a ResizeObserver, which could not be shown to fire reliably.

  • The global anchor reset strips colour and underline from every link, and nothing restored them inside running text. Three prose links — the privacy policy, the contact address, the wallet-security guidance — were pixel-identical to the paragraph around them. Gold cannot carry this alone (1.48:1 against body text), so the fix is an underline.

  • The mobile menu is a role="dialog" aria-modal="true" panel, but focus never left on open, which left the Tab trap inert: it only engages once focus is already on the first or last item. Focus now moves into the panel, and the skip link and nav links join main and footer in going inert. The Menu button stays live as the dialog's own control.

Waste (total build output down 25.2%, 1,228,836 -> 919,412 bytes):

  • Fontsource emits a legacy .woff beside every .woff2. A Vite transform strips the fallback before the CSS is read, so 259,868 bytes of font that no browser capable of running this site would request are never emitted at all.

  • The resource filter was a 3.1KB inline block in the shared layout: seventeen pages paid for it so three could use it. It now ships with ResourceFilter.astro. Its two vestigial window globals are gone.

  • The Network Clock polled /pulse.json every five minutes, but that file is a build artifact and cannot change between deploys — roughly 288 requests a day per open tab for a guaranteed identical body. Removed, along with the flash affordance the poll drove.

  • fetch-pulse fetched, stored, and typed a bitnodes node count that nothing rendered. generate-grain used Math.random(), rewriting a 64KB PNG on every refresh; it is seeded now and byte-identical across runs.

  • Ten unused design tokens, plus four aliases that were exact duplicates of an existing size or tracking value and implied a granularity the design did not have. No visual change.

Copy and data:

  • The primer promised five steps and listed six nouns; objections promised nine and enumerated eight, omitting Better Technology. Both counts are derived now rather than asserted.

  • The paths page kept its route questions in a hand-written map keyed by dataset id. A renamed path would have rendered an empty

    . They live in rabbit-holes.ts, where README already says they belong.

  • Frame 2's method note omitted the chart's largest assumption: the dollar line is an index and the bitcoin line is a lump-sum position entered at the July 2010 anchor. It says so now.

  • The archive note claimed nothing had been changed directly beside "Translated from Chinese". Two sources of truth for site freshness had already drifted a month apart; both derive from routes.json now.

Security:

  • script-src names the sha256 of every inline script the build emits, so 'unsafe-inline' survives only as a fallback for browsers predating hash support. Verified in a browser against the real Worker: all nine scripts execute, JSON-LD parses, islands hydrate, no violations.

The audit gained checks for CSP hash freshness, shipped .woff, the prose link affordance, and filter-script scope; each was confirmed to fail when its subject is broken. Its ROOT no longer uses a percent-encoded URL.pathname, which broke on any checkout path containing a space.

Four defects, then the waste and drift they sat next to.

Bugs:

- The Worker chose Cache-Control by path without checking status, so a
  404 under /_astro/* or /grain.png was served immutable for a year. A
  half-rolled deploy or stale HTML pointing at a departed hashed asset
  would pin that error in every cache downstream. Errors now get 60s
  must-revalidate. The styled 404 also covers every method now, with a
  body only on GET, so HEAD and POST stop leaking the asset layer's own
  404 payload.

- Frame 2 pinned a 1100x480 viewBox to a fluid width with
  preserveAspectRatio="none". At a 375px viewport that is 0.45 horizontal
  distortion: cursor dots rendered as 3x6.7 ellipses and the event labels
  collapsed into overlapping mush. The viewBox now tracks the element's
  own CSS box, so both axes scale by exactly 1. Narrow layouts drop the
  event labels and thin the year axis rather than overprinting them.
  Measured on mount and on resize rather than with a ResizeObserver,
  which could not be shown to fire reliably.

- The global anchor reset strips colour and underline from every link,
  and nothing restored them inside running text. Three prose links — the
  privacy policy, the contact address, the wallet-security guidance —
  were pixel-identical to the paragraph around them. Gold cannot carry
  this alone (1.48:1 against body text), so the fix is an underline.

- The mobile menu is a role="dialog" aria-modal="true" panel, but focus
  never left <body> on open, which left the Tab trap inert: it only
  engages once focus is already on the first or last item. Focus now
  moves into the panel, and the skip link and nav links join main and
  footer in going inert. The Menu button stays live as the dialog's own
  control.

Waste (total build output down 25.2%, 1,228,836 -> 919,412 bytes):

- Fontsource emits a legacy .woff beside every .woff2. A Vite transform
  strips the fallback before the CSS is read, so 259,868 bytes of font
  that no browser capable of running this site would request are never
  emitted at all.

- The resource filter was a 3.1KB inline block in the shared layout:
  seventeen pages paid for it so three could use it. It now ships with
  ResourceFilter.astro. Its two vestigial window globals are gone.

- The Network Clock polled /pulse.json every five minutes, but that file
  is a build artifact and cannot change between deploys — roughly 288
  requests a day per open tab for a guaranteed identical body. Removed,
  along with the flash affordance the poll drove.

- fetch-pulse fetched, stored, and typed a bitnodes node count that
  nothing rendered. generate-grain used Math.random(), rewriting a 64KB
  PNG on every refresh; it is seeded now and byte-identical across runs.

- Ten unused design tokens, plus four aliases that were exact duplicates
  of an existing size or tracking value and implied a granularity the
  design did not have. No visual change.

Copy and data:

- The primer promised five steps and listed six nouns; objections
  promised nine and enumerated eight, omitting Better Technology. Both
  counts are derived now rather than asserted.

- The paths page kept its route questions in a hand-written map keyed by
  dataset id. A renamed path would have rendered an empty <h2>. They
  live in rabbit-holes.ts, where README already says they belong.

- Frame 2's method note omitted the chart's largest assumption: the
  dollar line is an index and the bitcoin line is a lump-sum position
  entered at the July 2010 anchor. It says so now.

- The archive note claimed nothing had been changed directly beside
  "Translated from Chinese". Two sources of truth for site freshness
  had already drifted a month apart; both derive from routes.json now.

Security:

- script-src names the sha256 of every inline script the build emits, so
  'unsafe-inline' survives only as a fallback for browsers predating
  hash support. Verified in a browser against the real Worker: all nine
  scripts execute, JSON-LD parses, islands hydrate, no violations.

The audit gained checks for CSP hash freshness, shipped .woff, the prose
link affordance, and filter-script scope; each was confirmed to fail when
its subject is broken. Its ROOT no longer uses a percent-encoded
URL.pathname, which broke on any checkout path containing a space.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@VeteranXYZ
VeteranXYZ merged commit 859460e into main Sep 2, 2026
1 check passed
@VeteranXYZ
VeteranXYZ deleted the review-fixes branch September 3, 2026 15:55
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.

1 participant