docsite: contain the desktop-pinned hero layers so overscroll suppression can be removed entirely
- Dominant language
- TypeScript
- Stars
- 13.1k
- Forks
- 1.1k
- Avg merge
- 1d 14h
- Merged PRs (30d)
- 669
Description
## Summary
Step 2 of #5392. #5415 restored pull-to-refresh on mobile by bounding the hero's aurora glow below 1024px and scoping the overscroll suppression to desktop:
```css
@media (min-width: 1024px) {
html { overscroll-behavior-y: none; }
}
```
That was always meant to be a stepping stone, not the destination. At ≥1024px three layers are still `position: fixed` — `heroContent` (`(site)/page.tsx`), `HeroFloatingCards.stage`, and `HeroThemeReel.backdropGlow` — so the suppression has to stay, and macOS trackpads still get no native rubber-band in Safari, Chrome or Firefox. That is the case #3032 was actually filed for.
This issue is to finish the job: contain the desktop-pinned layers so `overscroll-behavior-y: none` can be **deleted outright** rather than gated.
## Why `sticky` is the answer
A `fixed` layer is glued to the viewport and is not part of the document. When the document rubber-bands past its own bottom edge, the fixed layer does not lift with it — so it is sitting in the exposed gap. That is the bleed.
A `sticky` layer *is* in the document. It lifts with it, and **cannot paint below the document's own bottom edge**. That is structural rather than empirical, which is why it lets the rule be removed instead of narrowed.
Concretely, at max scroll today the fixed glow occupies viewport y=48–1098 — the entire viewport — so any bottom overscroll necessarily paints it.
## Prototype: the effect survives
DOM surgery on a dev build, canary banner hidden to match production, one `position: absolute; inset: 0` rail per layer as a direct child of `heroScope`, with `backdropGlow` + `stage` + `heroContent` all `position: sticky; top: var(--appshell-header-height)`:
| scroll | 0 | 100 | 200 | 400 | 760 | 1400 | 2200 | max |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| pixels differing from `fixed` | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Document height unchanged (3540px). Verified at 1280×900, 1280×600, 1440×1400 and 1024×768. Layer geometry matches `fixed` everywhere except max scroll, where sticky releases — invisible, because the opaque `showcaseOverlay` already covers the hero there. Hero CTA hit-testing unaffected (`elementFromPoint` returns the button before and after).
So the tuned pin-and-cover effect is reproducible with `sticky`. The work is in the plumbing, not the design.
## Two traps, both paid for already
**1. The rails must go in the right place.** The pinned layers are not children of `heroScope`. Its only two children are `swipeArea` (`HeroReelProvider`'s root, 760px, holding every hero layer) and `showcaseOverlay`. A sticky layer inside `swipeArea` releases after ~48px, so each layer needs a containing block spanning hero + showcase.
Insert each rail **immediately before `showcaseOverlay`**, not at the front of `heroScope`. Inserting at the front puts the layers behind `themeFill` (opaque, still inside `swipeArea`, later in tree order) and produces a 29%-of-pixels difference at scroll 0 that looks exactly like sticky failing but is pure paint order.
**2. `display: contents` on `swipeArea` is NOT a shortcut — I tried it.** `` already renders `display: contents`, so `swipeArea` is the only real box between the layers and `heroScope`, which makes "just make `swipeArea` contents and flip the three layers to sticky" look like a two-line fix. Measured, it is not:
- document height 3540 → **5302px** (+1762): `heroContent` becomes in-flow and adds its own height on top of `heroSpacer`, which still reserves 760px
- `heroContent` lands at viewport y=1858 at scroll 0 — below the fold, after the spacer
- glow `x` shifts 40 → **−520**: `left: 50%` + `translateX(-50%)` no longer resolves the way it did
Anyone taking this will reach for that shortcut. It needs the rails, plus `heroSpacer` bookkeeping and a centering change.
## Scope
- Three `absolute; inset: 0` rails as direct children of `heroScope`, inserted before `showcaseOverlay`; the pinned layers move into them. This means the layers currently rendered by `HeroReelCards` inside `HeroReelProvider` have to be reachable as direct children of `heroScope` while keeping reel context and ``.
- `heroContent`, `stage`, `backdropGlow`: `fixed` → `sticky` at ≥1024px.
- Centering: `left: 50%` + `transform: translateX(-50%)` → `margin-inline: auto` inside a full-width rail (verified equivalent: x=40, w=1200 either way).
- `heroSpacer` height bookkeeping once the hero is sticky rather than fixed.
- Delete `@media (min-width: 1024px) { html { overscroll-behavior-y: none } }` from `apps/docsite/src/app/globals.css`.
## Add a comment where it will save someone
`fixed` breaks if an ancestor gains `transform` or `filter`. `sticky` breaks **additionally** if any ancestor gains `overflow: hidden` or `overflow: auto` — silently, no error, the hero just stops pinning and nobody notices until they look at the home page.
`.astryx-layout-content` (AppShell) already carries `overflow: clip`. That is safe — `clip` creates no scroll container — but it is one property *value* away from un-pinning the entire landing page. Worth a comment on that rule. (It is also why the `box-shadow` slab workaround in #5392 fails.)
## What is not yet proven
The prototype covered static scroll positions with transitions and animations disabled, in Chromium only. Untested: the theme-swap animation mid-scroll, resize while scrolled, `prefers-reduced-motion`, and **Safari** — which is the browser the rubber-band actually matters in.
Layout equivalence is proven; the *feel* of the tuned effect is not. That is the careful visual pass #3032 asked for, and it is the real cost here.
## Worth sizing honestly
Desktop has no pull-to-refresh. The entire payoff is native macOS trackpad rubber-band — the mobile fix restored a **function**, this restores a **feel**. Windows and Linux have no vertical bounce, so the rule is already a no-op there. It is a refactor of a hand-tuned effect for a polish win on the site's front door: worth doing deliberately, not worth rushing.
Contributor guide
Research direction
Start with `(site)/page.tsx`, `HeroFloatingCards`, `HeroThemeReel`, and `apps/docsite/src/app/globals.css`; trace `heroScope`, `swipeArea`, `showcaseOverlay`, and `heroSpacer` before changing the layer structure. Reproduce the prototype at the listed viewport sizes, then verify sticky layers preserve geometry and CTA hit-testing, the overscroll rule is removed, and the effect remains correct during theme swaps, resizing, reduced motion, and Safari testing.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- css, typescript
- Domain
- frontend, web-dev
- Issue type
- Refactor
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 48/100