Files
dating/README.md
T
tiennm99 b0cd86e07c feat: make the scroll film land, and give the story a last frame
The GSAP tier was a net negative: entrance triggers were created before the
pins and without refreshPriority, so ScrollTrigger measured them before each
pin inserted its spacer and every scene's animation completed below the fold.
Film mode also collapsed the two-column editorial layout, because
`html.film-ready .film-scene{display:flex}` outranks `.section-grid`, and both
pins held scroll without playing anything.

Engine:
- Create pins first, with refreshPriority, so reveals measure against the real
  document height.
- Replace the per-scene scrub with per-beat, non-reversing batches. Fixes tall
  scenes a single trigger can never time, and stops beats — including the
  closing CTAs — from sitting at opacity 0 while still in the tab order.
- Give both pins real work: the cold open scrubs a camera push-in and
  dissolves its copy; the closing pin scrubs a decorative vignette only, never
  content opacity.
- Slow the honesty scene deliberately, and restore the longer landing the
  closing beat is meant to get.
- Route in-page links through Lenis so nav travel shares the film's easing.

Layout and craft:
- Stop clobbering `display`; grid scenes keep their columns in every tier.
- Request Fraunces as a weight range — 550 and 600 were both snapping to 650,
  so h1, h2 and h3 rendered identically.
- Collapse the mobile header from three rows to one (254px to 73px) and drive
  scroll-margin-top from the measured height, so anchor jumps stop landing
  headings behind the bar.
- Un-hide the page if no motion engine claims it within 1200ms, so a slow CDN
  no longer means a wordless first paint.
- Full-bleed header, per-tween will-change, no backdrop blur on phones, cached
  scroll extent, preloaded opening frame.

Story:
- Three shapes for three registers in the sagging middle: a numbered package,
  an unboxed vow, and a climax set quieter than anything around it.
- An end card after the appendix, carrying the freshness stamp out of the
  pinned closing scene and repeating the one apply door at the true bottom.
- Dissolve the hero into the page instead of cutting to ivory.

Vietnamese copy is moved, not reworded; the end card's single new word is
marked for the owner. Verified at 1440x900, 390x844 and 320x640, both themes,
reduced motion, CDN blocked and engine blocked.
2026-07-25 11:45:59 +07:00

2.5 KiB

Dating

Warm-witty dating JD and CV site for Tiến Nguyễn Minh.

Structure

Single-page site: index.html at the repo root holds the whole story, told as scenes and navigable via anchors:

  • #home — cold open, the vacant position
  • #applicant — who the candidate is
  • #honest — saying it straight
  • #offer — what you get
  • #shared — what you both protect
  • #no-test — bonus points, and the written climax
  • #closing — the connect beat
  • #dossier — appendix, the detail table
  • #end — end card

Features

  • Light theme: warm ivory and terracotta-rose
  • Dark theme: candlelit plum and rose-gold
  • First visit follows the OS colour scheme; an explicit choice is persisted
  • Compact emoji theme switch
  • Vietnamese-only copy

Tech

  • Plain HTML, CSS, and JavaScript — no framework, no build step
  • GitHub Pages (classic, deploy from branch)

Motion tiers

Motion is a three-tier progressive enhancement. Any change must keep all three working, and no [data-reveal] element may ever be left invisible:

  1. Film (html.film-ready) — GSAP + ScrollTrigger + Lenis from a CDN, in assets/story-film.js. Activates only on ≥768px, with motion allowed, and only when the CDN libraries actually arrived in time.
  2. Reveal (html.js-ready:not(.film-ready)) — IntersectionObserver fade-and-rise in assets/story.js. This is what phones and reduced-motion visitors get.
  3. Static — no JS, or a stalled CDN. A watchdog in the index.html head un-hides everything after 1200ms if no engine has claimed the page, so the first paint is never wordless.

Two ordering rules in story-film.js are load-bearing and commented there: pins are created before reveals and carry refreshPriority: 1, and reveals are per-beat and non-reversing.

Files

  • assets/styles.css — tokens, layout, components (both themes)
  • assets/story.css / story.js — tier 2 reveal
  • assets/story-film.css / story-film.js — tier 1 film
  • assets/story-chrome.css / story-chrome.js — progress bar + active nav
  • assets/theme-switch.js — theme toggle (keep in sync with the head bootstrap)
  • assets/freshness-stamp.js — "cập nhật [ngày]" from the GitHub commit date

Developing

No install needed. Open index.html directly, or serve the folder:

python -m http.server 8000

Then visit http://localhost:8000/.

Deploying

Classic GitHub Pages serves the main branch root directly — pushing to main publishes the site. .nojekyll disables Jekyll processing.