Every interaction in HNU-TimeLetter is designed around the ritual of receiving and opening a personal letter. Users do not “navigate to content” — they unseal, unfold, and explore. This document specifies the full interaction model: the envelope-opening ceremony, the dual desktop/mobile experiences, the scroll-down exhibition corridor, and the persistent global navigation bar, complete with Zustand state transitions, animation parameters, and implementation notes.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/HNU-himematsu/HNU-TimeLetter/llms.txt
Use this file to discover all available pages before exploring further.
1. Envelope Opening Ritual
The homepage (/) opens with a full-screen envelope composition. The act of clicking the wax seal is the site’s primary entry ritual.
1.1 Visual Layout
The opening screen is based on a five-column vertical grid:- Column 1 (right edge) — Red ribbon, full viewport height, serves as the left visual anchor.
- Column 2 — Vertical title “与她的海大时光笺”, stacked alongside the ribbon.
- Columns 3–5 (center) — The envelope hero, with the wax seal rendered in a physical-material style at fixed dimensions.
1.2 Click Sequence
Wax seal crack
User clicks the envelope. The wax seal plays a cracking animation. No state change yet — this is a pure micro-animation.
Cover flips open
The envelope flap lifts and rotates open. Framer Motion delays the
zIndex drop on the cover from 30 → 0 by 0.15 s to prevent the cover from overlapping the letter during the next phase.Letter unfolds and rises
The letter paper slides upward out of the envelope. Zustand transitions to the
opening stage. The title simultaneously slides upward with a light blur-fade-out.Letter fills the viewport
The letter expands from its resting position to cover the full viewport.
isTransitioning is set to true, activating the transition overlay before the letter reaches full size.Transition overlay fades out
The full-screen transition mask fades out.
isEnvelopeOpened is set to true. The page component then schedules a short timeout to dismiss the overlay.1.3 Zustand State Transitions
1.4 Entry Animations
| Element | Trigger | Duration | Easing | Technique |
|---|---|---|---|---|
| Red ribbon | Page first load | 1400 ms | cubic-bezier(0.16, 1, 0.3, 1) | clip-path vertical reveal, top → bottom; double requestAnimationFrame trigger; no position/size change |
| Envelope | Page first load (simultaneous with ribbon) | Spring | Spring physics | Falls from above, settles into gentle idle float |
| Title | After ribbon animation completes | ~300 ms | opacity fade | Fades in; fades out + upward shift when opening begins |
| Wax seal | Idle state | — | — | Fixed -5 deg tilt, no hover/tap deformation |
The ribbon uses
clip-path reveal rather than translateY so that the ribbon’s visual footprint in the grid does not shift during the entrance. The double requestAnimationFrame ensures the initial clip-path value is painted before the animation begins, preventing a flash of the final state.1.5 Scroll Strategy (Pre-opening State)
While the envelope has not been opened, the homepage extends downward into the scroll-down exhibition pages (About Project → About Us → Credits → Footer). Desktop uses Lenis for smooth inertial scrolling (hides the native scrollbar); mobile keeps native vertical scrolling. After the opening sequence completes, the entire page switches to the map / stamp-feed experience.2. Responsive Strategy
HNU-TimeLetter uses a dual-experience approach rather than simple CSS responsive scaling.| Viewport | Experience | Route implementation |
|---|---|---|
| Desktop (> 768 px) | 時光画卷 — Interactive Map | app/page.tsx + app/map/page.tsx (desktop component tree) |
| Mobile (≤ 768 px) | 掌心集邮册 — Stamp Collector Feed | app/page.tsx + app/map/page.tsx (mobile component tree) |
app/page.tsx and app/map/page.tsx based on a screen-width check. The two branches are entirely separate component trees — no shared layout that merely hides/shows.
3. Desktop Map Experience (時光画卷)
The desktop experience metaphor is a horizontally-unrolling timeline scroll — immersive, dynamic, narrative.3.1 States
| State | Visual | Trigger |
|---|---|---|
| Idle | Full-screen campus map with pin points | Default after opening / after map unrolls |
| Rolling | Map rolls rightward into 56 px scroll strip | User clicks a location pin |
| Rolled | 56 px scroll strip on right; story stage visible | Map animation completes |
| Unrolling | Story stage exits; map expands left back to full width | User clicks the scroll strip |
3.2 Rolling Interaction
Pin click
User clicks a location pin on the map. The selected location is stored in Zustand. Rolling animation begins immediately.
Map rolls right
The map container uses
overflow-hidden. The inner map (fixed at 100vw) is right-anchored, so as the container narrows, the pin coordinates remain visually accurate. The container animates from 100vw to 56px.3.3 Story Stage
- Card stack: The story stage centres a draggable card stack. Drag left or right to fly a card out; the stack loops infinitely.
- Layering: The story stage is
z-index: 30, sitting above the map/scroll-strip container (z-index: 10). Cards can freely overlap the scroll strip during drag without clipping. - Scroll strip: Always visible on the right as a persistent return-to-map entry point.
3.4 Unrolling Interaction
Map unrolls
Only after the story stage has completely exited does the map container expand leftward back to
100% width.3.5 Scroll Strip Visual Reference
The 56 px strip uses a warm-parchment gradient, an outer drop shadow, and a subtle inner edge shadow. See the Visual Spec for the exact CSS token values.4. Mobile Stamp Feed (掌心集邮册)
The mobile experience metaphor is a stamp collector’s album — efficient, intimate, readable.4.1 Story Feed
- Vertical waterfall of story cards — no interactive map component loaded on mobile.
- Each card shows a high-resolution thumbnail + location name + character info.
- FAB (Floating Action Button): Fixed bottom-right. Tapping it opens a static campus map image (view-only, not interactive) in a modal.
4.2 Detail Modal
Tapping a feed card triggers a shared element transition: the card image expands to full-screen, then the detail modal settles into its final layout. Modal layout (top → bottom):| Zone | Contents |
|---|---|
| Upper ~50 % | High-resolution image with left/right navigation arrow overlays |
| Middle | Character avatar + name + role information |
| Lower | Scrollable story text |
4.3 Gestures
| Gesture | Action |
|---|---|
| Tap left/right arrow | Switch between stories at the same location |
| Swipe left / swipe right | Switch to a different location |
| Pull down (pull-to-close) | Close the detail modal and return to the feed |
5. Scroll-Down Pages (下滚页面群)
Below the envelope on the homepage, before the user opens the letter, there is an exhibition corridor of four sections. These sections are accessible via normal scrolling and are replaced by the map experience after the envelope is opened.5.1 Global Scroll System
Lenis smooth scroll (desktop only):src/lib/useVirtualScroll.ts and related style hooks in src/app/globals.css. @waaark/luge is co-initialized alongside Lenis (its own smooth-scroll plugin is disabled) to provide scroll-driven reveal animations.
Custom scrollbar (src/components/shared/CustomScrollbar.tsx): Mounts on desktop in the pre-opening state only. Track: fixed right side, 15 px wide, transparent; thumb: 10 px wide, height calculated as vh × (vh / documentHeight), 5 px border-radius. Default style: transparent fill + 1px #c23643 outline; hover: solid #c23643 fill. Syncs strictly with lenis.on('scroll'), falls back to window.scroll if no Lenis instance.
5.2 Red Guide Line
A continuous SVG<path> spans all three content sections (About Project → About Us → Credits), acting as a visual thread connecting the narrative.
| Property | Value |
|---|---|
| Width | 100 px stroke |
| Color | #c23643 |
| Caps / joins | stroke-linecap: round, stroke-linejoin: round |
| z-index | 1 — above background, below body content |
| Mask | SVG viewport top = P1.y − 60; bottom = credits-section bottom edge |
ribbonX = vw × 0.2 − 50 px, aligning to the ribbon center):
| Phase | Scroll range | Anchor (viewport %) |
|---|---|---|
| Launch | scrollY ≤ 500 px | 1.0 — tip tracks viewport bottom |
| Mid | 500 px < scrollY < transitionStart | 0.6 — tip locked at 60 % viewport |
| Tail | scrollY ≥ 0.7 × snapScroll | 0.6 → 1.0 (linear blend) |
| Snap | viewBottom ≥ creditsBottom | Force 1.0 |
Math.max(cachedMax, current) — scrolling back up never retracts the drawn stroke.
5.3 About Project Section
Full-screen (100vh), letter-paper background. Large-title / editorial-body layout using font-serif for the heading and font-sans for the body. Wide margins, generous line-height — the text introduces the project’s shared-universe creative premise.
5.4 About Us Section
Full-screen (100vh), dual-column layout that actively routes around the diagonal guide line.
Left column (~45–55 % width):
- Fixed preamble (“关于我们 / 海带视研…”) always visible at the top.
- Below it: the active member’s role description, cross-fading on member change (
opacityfade ~300 ms).
| Property | Value |
|---|---|
| Auto-advance interval | 2.5 s |
| Spring easing | stiffness: 200, damping: 30 |
| Active item scale | 1.15× avatar, heavier nickname weight |
| Inactive items | Reduced scale + lowered opacity |
| Hover | Pauses the auto-advance timer |
| Click | Sets that member active, resets timer |
The ticker uses
translateY to slide the list, not scrollTop, to keep it compatible with Framer Motion’s layout animation and avoid scroll-event conflicts with Lenis.5.5 Credits Section
Full-screen, right-aligned overall. Upper zone: credits heading + ~100-character body text. Lower zone: three rows of contributor nicknames scrolling in an infinite marquee. Each row may have a different speed or direction for visual rhythm variation. The guide line’s final segment (P5 → P6) crosses this section diagonally and is masked at the footer boundary, making the stroke appear to “arrive into” the red footer band.
5.6 Footer
The footer is revealed via a fixed-position parallax effect:Footer is position: fixed at the viewport bottom; a FooterSpacer holds its height in the document flow; main content scrolls away and uncovers the footer beneath.
Auto-snap (src/lib/useFooterSnap.ts): When the user is actively scrolling down and is within 38 vh of the page bottom, a LERP-driven auto-scroll (lerp: 0.16, Expo ease-out) fires once to fully reveal the footer. It does not trigger on idle or upward scroll. The snap resets when the user scrolls back up past 30 % of total scrollable distance.
Footer structure (top → bottom):
Navigation links (red background #C23643)
Four horizontally-arranged link cards on a red background: 活动公告 (Feishu doc), QQ 活动群, B站主页, GitHub 仓库.
Giant HIMEMATSU logo
public/HIMEMATSU.svg stretched responsively to full site width, on the red background.#C23643, primary text #ECE9E4, secondary text #ECE9E4 at 90 % opacity.
6. Global Navigation Bar
The navigation bar is a fixed, vertically-oriented pill capsule anchored at the top-right of the viewport, present across all pages.6.1 Visual Form
| Property | Value |
|---|---|
| Position | fixed, top-right viewport, z-index above page content, below modals |
| Shape | Vertical pill (width constant, height grows with items) |
| Background | Frosted glass — backdrop-filter: blur + semi-transparent Surface base |
| Border | 1px solid #c23643 — the red border is the emotional signature |
| Border radius | Full capsule (border-radius: 50 % of width) |
| Width | clamp(44px, 4vw, 64px) — constant across all pages and routes |
The pill container’s width and height are immutable — no page customization may resize the capsule itself. Per-page visual identity is expressed only through decorative layers outside the capsule (e.g., the homepage red block below it).
6.2 Navigation Items
Three items, fixed top-to-bottom order:| Label | Route | Description |
|---|---|---|
| 主页 | / | Envelope opening screen and scroll-down pages |
| 地图 | /map | Interactive map main experience |
| 公示板 | /creation | Community creation board |
- Text direction:
writing-mode: vertical-rl - Font:
ChillDINGothic_SemiBold(heading font) - Unselected color:
#5a4748(Ink), transitions towardInk Mutedon hover - Selected color:
#ffffffagainst the red active capsule
6.3 Selected State (Layout Animation)
The active item has a solid red inner capsule (#c23643) that is smaller than the outer pill — left and right gaps preserve the “pill inside a pill” visual hierarchy. Switching routes causes this inner capsule to slide smoothly to the new active position via Framer Motion layout animation. No hard-cut.
6.4 Homepage Variant
On the homepage (/) at scrollY === 0, a red rectangle (#c23643) is appended directly below the nav pill:
| Property | Value |
|---|---|
| Height | ~30 vh |
| Width | Same as the nav pill (~7 vw) |
| Corner radii | Only bottom-left is rounded; top-left, top-right, bottom-right are square |
| Purpose | Echoes the full-height red ribbon on the left, reinforcing the visual frame |
6.5 Announcement Button
Directly below the nav pill (gap:0.5 rem), a circular button is always present:
| Property | Value |
|---|---|
| Shape | Circle, diameter = nav pill width |
| Background | Frosted glass + rgba(246, 241, 235, 0.55) + 1px solid #c23643 |
| Hover | Background fills to solid #c23643; text switches from #563B3B to #ffffff; transition 0.2 s |
| Text | 公告, vertically centered, heading font, letter-spacing: 0.18em |
| Action | Opens https://himematsu.feishu.cn/docx/EbsDdehuLo1801xBzb1cxzJLnHb in a new tab |
| Reduced-motion | Hover state switches instantly, no transition |
7. Additional Routes
| Route | Purpose | Data strategy |
|---|---|---|
/ | Envelope ritual + scroll-down exhibition corridor | Static / client state |
/map | Desktop interactive map + mobile stamp feed | Feishu-synced local static data |
/creation | Community galgame scene proposals | Feishu-synced local static data (creation-board.json) |
/map and /creation use the same architecture: Feishu data is synced to local JSON files; pages render purely from that local data with no runtime Feishu API calls.