Skip to content

feat: replace the home experience carousel with a scroll-driven timeline - #93

Merged
parammehta merged 2 commits into
mainfrom
feat/experience-timeline
Aug 26, 2026
Merged

parammehta merged 2 commits into
mainfrom
feat/experience-timeline

Conversation

@parammehta

@parammehta parammehta commented Aug 26, 2026 •

Copy link
Copy Markdown
Owner

The home page's 3D carousel becomes a horizontal timeline: one node per role, present to past left to right, panning sideways as the page scrolls down. Inspired by interfaces.kylejeong.com.

The component is refract-ui's new ScrollTimeline (refract-ui#7, released as 1.1.0). This PR is the wiring, plus the data correction it surfaced.

Six nodes, not three slides

One node per role rather than per company. The argument a timeline makes is that the roles are a sequence, and collapsing three Walmart years into one node would flatten the part worth showing:

Senior SWE, Identity Authentication Experiences Jan 2024 – Present
Staff SWE, Intuit Design System Jul 2022 – Jan 2024
Senior SWE, Full Stack (Rivian) Jan 2022 – Jul 2022
Senior SWE, Marketplace & UGC (Walmart) Jan 2021 – Jan 2022
SWE, Walmart Core Components Jan 2020 – Jan 2021
SWE, Wireless Verticals & Baby Registry Jun 2018 – Jan 2020

Nothing sorts at render time — companies is newest-first and each company's roles are too, so a flat map already reads present to past. If the order ever looks wrong, the data is what's wrong.

The device models are kept, but windowed to the active node and its immediate neighbours. Model builds a WebGLRenderer per instance and browsers cap live contexts; mounting all six would sit near that ceiling for the whole page, on a landing page that also runs the hero sphere. Verified in the browser: 3 canvases live at any time, not 6.

A data bug this surfaced

Company.roles is documented "Newest role first", but Intuit's array was oldest-first — and the carousel read company.roles[0].title alongside the company's date range. So the live page has been rendering the older role's title welded to the current dates: "Staff Software Engineer, Intuit Design System · Jul 2022 – Present".

Fixed by reordering newest-first. Titles and highlights stay with their teams — Staff was the Design System role, Senior is the current Identity role. This also corrects /experience, which renders roles[0] as the lead heading.

Home page: the snap deck had to give a little

.home is a scroll-snap-type: y mandatory deck where every section is exactly one snapport tall. The timeline needs the opposite — a section several viewports tall with a sticky child — so #experience now carries data-runway, which releases the height: 100% and drops scroll-snap-stop: always for that pane only. It still snaps at its top edge.

That broke activeSection = Math.round(scrollTop / clientHeight), which hard-assumed uniform pane heights and would have reported sections 2–5 while you were still inside the third. Replaced with an offsetTop lookup over the section refs — more correct regardless.

.home is now position: relative, and that is load-bearing. framer-motion measures scroll progress inside a custom container by walking offsetParent up from the target until it reaches the container. A static container is never in that chain, so the walk runs off the top of the document and progress pins at 0 — the timeline renders perfectly and never moves. It's commented in place so it doesn't get "cleaned up" later.

Smaller notes

  • The visible company name under the track is gone; the CTA below it already reads "Read about Intuit". Replaced with a visually-hidden live region, since a link silently retargeting itself as the track pans is not announced.
  • Camera pulled from z: 10.4 to 7.8 — a timeline card is far wider than it is tall and the vertical FOV is fixed, so the carousel's framing left the devices small in a lot of empty width.
  • home_experience_slide keeps its name and now fires on active-node change, so the Worker's ALLOWED_EVENTS allowlist needs no change.
  • Removed a stale storybook entry from .claude/launch.json pointing at a binary that no longer exists here (Storybook lives in refract-ui now).

Testing

npm run preflight passes against the published refract-ui 1.1.0 — lint, stylelint, typecheck, 36 unit, 24 integration, worker tests, build, 37 e2e.

Three new integration tests: every role appears in data/experience order (derived from the data, so adding a role can't leave the test asserting a stale list), the CTA points at the active role's company, and #experience carries its runway attribute.

Verified in a real browser on desktop and mobile — full traversal in both directions with actual wheel input, sticky viewport holding, every node centring at rest, no collisions between cards and the pinned heading/CTA or the fixed navbar.

🤖 Generated with Claude Code

The home page's 3D carousel becomes a horizontal timeline: one node per role,
present to past left to right, panning sideways as the page scrolls down. The
component itself is refract-ui's new ScrollTimeline (1.1.0); this is the wiring
plus the data correction it surfaced.

Six nodes instead of three slides, since the argument the timeline makes is that
the roles are a sequence — collapsing three Walmart years into one node would
flatten the part worth showing. Models are windowed to the active node and its
neighbours, so three WebGL contexts stay live rather than six.

data/experience: Intuit's roles were ordered oldest-first while Company.roles is
documented newest-first, and the home carousel read roles[0] — so the page has
been rendering the older role's title welded to the current date range ('Staff
Software Engineer, Intuit Design System - Jul 2022 - Present'). Reordered
newest-first, which fixes that. Titles and highlights stay with their teams:
Staff was Intuit Design System, Senior is the current Identity role.

Home: #experience can no longer be one snapport tall, so it takes a runway and
opts out of scroll-snap-stop. activeSection moves from scrollTop / clientHeight
to an offsetTop lookup, since that division assumed every section was exactly
one viewport and would have reported four panes while inside the third.

Home.module.css: .home is now positioned. That is required, not cosmetic —
scroll progress is measured by walking offsetParent up from the timeline to the
container, and a static container is never in that chain, so progress pins at 0
and the timeline renders without ever moving.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@parammehta
parammehta force-pushed the feat/experience-timeline branch from 6ed499f to 5786b41 Compare August 26, 2026 01:42
Cards alternate sides by index, which carries no meaning on its own. Starting
above put the Senior Identity role over the Staff role directly beside it — two
adjacent titles at the same company, which reads as a ranking whether or not one
is meant.

Starting below fixes that pair and improves the rest: Staff now sits above both
Seniors adjacent to it, Walmart's Senior sits above the engineer role beside it,
and the same-level pairs imply nothing either way.

Needs refract-ui 1.2.0 for the startSide prop.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@parammehta
parammehta merged commit 360301a into main Aug 26, 2026
9 of 10 checks passed
@parammehta
parammehta deleted the feat/experience-timeline branch August 26, 2026 02:36
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