Skip to content

feat(marketing): live conduit scroll hero on the homepage - #10

Merged
reecelikesramen merged 17 commits into
mainfrom
claude/camera-story-hero-c838ee
Sep 28, 2026
Merged

reecelikesramen merged 17 commits into
mainfrom
claude/camera-story-hero-c838ee

Conversation

@reecelikesramen

@reecelikesramen reecelikesramen commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Reece · project thread

Before: the homepage hero is static copy beside a counter.wire code window over a CSS graph-paper grid.

After: the hero is a pinned scroll story over a WebGL circuit grid, grown from the design system's camera-story exploration. In the hero, the cursor acts as a probe: current flows along the traces toward it, and a press discharges along them. Then four beats follow as the camera frames a server card and a browser window:

  1. Python: the counter.wire server card compiles out of the grid.
  2. Conduit: the old stack's REST API / JSON / client store routes merge into one live wire.
  3. Patch: a click crosses the wire instantly and only the changed text node flashes. Three scripted demo clicks run, then it's the visitor's turn.
  4. Transport: the wire splits into WebSocket / WebTransport / HTTP long polling lanes.

The "Why pywire" section then rises over the dimmed last frame as a 3-column feature row.

How: HeroStory.astro holds the markup and styles. src/scripts/hero-story/ has the runtime (DPR cap, adaptive render scale, offscreen/background pause), the fragment shader (grid, probe, footprints, wires, arcs) and the story (layout, camera keys, demo and round-trip state, HTML overlays sharing the camera transform). It mounts on astro:page-load and tears down on astro:before-swap. Reduced motion gets still frames and an instant round trip on click. Without WebGL, the copy still steps through over a CSS grid. Portrait screens stack the server above the browser in the top half.

Checks: pnpm format:check, pnpm lint and pnpm typecheck pass. pnpm build can't finish in the cloud sandbox because the blog pages fetch jsonplaceholder.typicode.com, which is blocked there; CI builds it. Pushing the branch runs deploy.yml, which publishes non-main branches to the Cloudflare Pages nightly branch.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MRL8Z7p5CWKsXXXoi2mRG8

Port the design system's D3 "camera story" hero exploration into a
HeroStory component: a pinned WebGL grid where the camera zooms into the
server node, follows the wire and pulls back to the page as you scroll.

On top of the exploration:
- a live counter loop: server state changes flash the node, a labelled
  diff packet travels the wire and patches a real HTML card in the page
- the card's Add one button sends an @click event up the wire and the
  diff comes back, the actual pywire round trip
- HTML labels (server, browser, count = wire(n)) ride the camera
- the Why pywire section rises over the dimmed last frame with a scrim
- teardown/remount for Astro view transitions, reduced-motion still
  frames, no-WebGL grid fallback, portrait layout

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRL8Z7p5CWKsXXXoi2mRG8
Reworks the camera story after the first review:
- the cursor is a probe: current flows along the traces toward it and a
  press discharges along them (replaces the pulsing ring)
- the server is a counter.wire card and the browser a real window, both
  HTML riding the camera, compiling out of the grid as they appear
- new beats: the old stack's REST/JSON/client-store routes merge into one
  conduit, and the conduit splits into WebSocket/WebTransport/long-polling
- round trips are instant arcs across the wire, only the changed text
  node flashes, and the server only changes on clicks: three scripted
  demo clicks, then the visitor's turn
- the section after the story drops the code window for a 3-column
  feature row; step copy no longer cross-fades over itself

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRL8Z7p5CWKsXXXoi2mRG8
@reecelikesramen reecelikesramen changed the title feat(marketing): camera story scroll hero on the homepage feat(marketing): live conduit scroll hero on the homepage Sep 27, 2026
…code

- Drop every pointer effect from the hero background. The board is now
  buses of traces routed with 45 degree jogs on two layers that conduct
  now and then; scrolling speeds up and brightens the conduction.
- Add a Next pill that names the upcoming step and scrolls to it.
- Render the server card's counter.wire with Shiki and the pywire grammar.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRL8Z7p5CWKsXXXoi2mRG8
- Between the hero and the end of the story, a scroll gesture (wheel,
  swipe or arrow keys) past a small threshold glides to the next step
  instead of scrolling freely. Reduced motion keeps native scrolling.
- Next uses the same slower glide, and is bigger with an accent arrow.
- Reveals (server compiling, browser, wire, merge, transport) keep their
  own pace, so a fast scroll can't rush them.
- The hero's right side now has the pywire chip, its pins fanning out
  into traces that conduct into it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRL8Z7p5CWKsXXXoi2mRG8
- Glides run on a critically damped spring (about 0.7 s) and new input
  retargets them mid-glide, so steps chain and reverse without waiting.
  The momentum tail of a gesture that already took a step is swallowed.
- A gesture the page is already scrolling natively (coming back up from
  the section after the story) runs its course instead of being fought;
  if it stops between steps, it settles onto the next one.
- Hysteresis on the hand-off to the next section so the copy can't flicker.
- Snapping moves into its own module, snap.ts.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRL8Z7p5CWKsXXXoi2mRG8
- New step 05 "Stateless, at the edge" (marked coming soon, since edge
  mode is still in review): an edge node appears beside the browser, the
  wire's server end slides over to it, the server card dims to "origin",
  events carry "@click + state", and keepalive pings stop.
- Add one stays disabled, and now looks it, until step 3 wires it up.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRL8Z7p5CWKsXXXoi2mRG8
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRL8Z7p5CWKsXXXoi2mRG8
- Step 05 now shows edge compute as a network: edge nodes pop up around
  the stage (fra, iad, sin, syd), the server card goes, and visitors
  drift between them, each wired to whichever node is nearest, with a
  flash on handoff and an event and patch every couple of seconds. The
  browser keeps its own nearest edge node.
- The hero chip's logo gets real padding: it was sized with a percentage
  padding that resolved against the zero-width world layer.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRL8Z7p5CWKsXXXoi2mRG8
Step 05 now shows a dot-matrix world map with eight edge locations. You
travel east around it, and every click lands on whichever edge is
nearest, so the handoffs between edges are visible. The old edge-node
chip and network code are gone from the story and the shader.

Portrait and landscape layouts now measure the step copy and fit the
diagram into the band above it with a base camera zoom, so phones with
Safari's toolbars showing, iPads and short laptops all fit every step.
Short screens get tighter copy.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRL8Z7p5CWKsXXXoi2mRG8
…oolbar

- The edge story's clicks now show the step 3 cursor pressing Add one,
  and a ping leaves "you" on the map with each event.
- iPhone Safari floats its toolbar over the bottom of the page, so the
  step copy and the next button sat under it. Phones on iOS WebKit now
  keep a 72px dock clear at the bottom, and the layout fits above it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRL8Z7p5CWKsXXXoi2mRG8
…s than 100svh

iPhone Safari can count the strip under its floating toolbar in 100svh, so
the pinned stage ran past what's on screen and the next button sat under
the toolbar. The story now measures the visual viewport against the stage
and keeps the difference clear (--over), on top of the iPhone dock.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRL8Z7p5CWKsXXXoi2mRG8
The measured strip below what the browser shows already covers iPhone
Safari's floating toolbar, so the fixed iPhone dock is now a fallback
(max, not sum) and the next button no longer floats high. The step copy
keeps a little more room above the next button.

?viewport shows what the browser reports (inner size, visual viewport,
svh/dvh/lvh, safe area, stage and next button positions), for tuning on
devices we can't run in CI. Temporary; remove before this PR is ready.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRL8Z7p5CWKsXXXoi2mRG8
- The strip kept clear at the bottom is now max(0, 100svh - 100dvh) in
  CSS, so it follows Safari's toolbar live instead of a measurement taken
  at load, which came out too big without the ?viewport readout. The iPhone
  dock fallback is gone; the layout measures 100dvh the same way.
- The edge beat is quieter: one click per city, a softer ping on the
  edge, no ping from "you", no glow on the browser window, and no
  "your turn" pulse on Add one.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRL8Z7p5CWKsXXXoi2mRG8
- Scrolling takes one step per gesture: input is ignored while a step
  plays, and the rest of the gesture that took it (trackpad momentum, a
  spun wheel, a held key) is swallowed, so a fast scroll can't skip steps.
  A new gesture at the last step heading out scrolls the page natively.
- Step 3: an event lands on the <button @click> line first; a beat later
  the count = wire() line changes and the patch goes down.
- Softer effects: dimmer arcs and card glows, line highlights that hold
  then fade, and a slower, fainter "your turn" pulse.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRL8Z7p5CWKsXXXoi2mRG8
…dling

- Step 2's REST API / JSON / client store routes hold for a moment so the
  labels can be read, then ease into the conduit instead of collapsing
  linearly the moment the step starts.
- Step 3: the count line changes sooner after the @click line lights.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRL8Z7p5CWKsXXXoi2mRG8
Steps now lock for 0.56s instead of 0.85s, and a new gesture during the
tail of a glide carries on from the stop it is heading to, so spam
scrolling moves about one step every 0.7s while still showing each step.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRL8Z7p5CWKsXXXoi2mRG8
A round button in the bottom right scrolls back to the top once you are
a screen down; on phones it stays hidden while the story's Next button
is on screen. The header now sticks just above the screen once scrolled
away and slides back in when the mouse nears the top or focus lands in it.
The body uses min-height so the sticky header spans the whole page.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRL8Z7p5CWKsXXXoi2mRG8
@reecelikesramen
reecelikesramen marked this pull request as ready for review September 28, 2026 02:37
@reecelikesramen
reecelikesramen merged commit a790081 into main Sep 28, 2026
5 checks passed

This branch was successfully deployed

1 active deployment
preview — 0bc39b94 Deployed Sep 28, 2026 by github-actions[bot]
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.

2 participants