4.4 KiB
public-site-motion Specification
Purpose
Define progressive, accessible and non-blocking motion choreography for the public site, including shared timings, one-shot reveals, interaction feedback and safe fallbacks.
Requirements
Requirement: Public pages use shared progressive motion choreography
The public site SHALL expose a declarative motion contract using data-motion="page-open", data-motion-beat, data-reveal-group, data-reveal, and data-reveal-from="up|left|right". Home, services, portfolio index/detail, about, contact, privacy, and branded 404/419/429/500/503 pages MUST use the contract without introducing route transitions.
Scenario: Visual public route exposes motion hooks
- WHEN a visitor loads any visual public route or branded error page
- THEN the rendered page MUST expose a page opening hook and appropriate reveal hooks
Scenario: Technical endpoints remain outside motion
- WHEN a visitor requests
sitemap.xmlorrobots.txt - THEN the response MUST NOT depend on the public motion runtime
Requirement: Editorial entrance timing and direction are consistent
Entrances SHALL use shared tokens for an approximately 500 ms duration, 90 ms stagger capped at 270 ms, 12 px vertical distance, and 24 px desktop / 16 px mobile lateral distance. Text MUST remain fully visible; motion MUST use transforms and MAY use media clipping without opacity fades for text.
Scenario: Reveal group computes capped stagger
- WHEN more than four reveal items are rendered in one group
- THEN the applied delay MUST NOT exceed 270 ms
Scenario: Text enters without opacity fade
- WHEN page opening or scroll reveal motion runs
- THEN text MUST remain visible throughout the transition
Requirement: Reveals execute once without blocking interaction
Each scroll reveal SHALL activate once, reach its final state, and be removed from observation. Visitors MUST be able to activate links and form controls while entrance motion is running.
Scenario: Revealed content leaves observer
- WHEN a reveal target intersects the configured viewport threshold
- THEN it MUST receive the final state and be unobserved
- AND scrolling away and back MUST NOT replay the reveal
Scenario: CTA remains interactive during entrance
- WHEN a visitor activates a CTA while its entrance is in progress
- THEN navigation MUST proceed without waiting for animation completion
Requirement: Motion enhancement has immediate safe fallbacks
Without JavaScript, without IntersectionObserver, or when prefers-reduced-motion: reduce is active, the site SHALL render every motion target immediately in its final usable state. Non-essential animation and transition MUST NOT run under reduced motion.
Scenario: JavaScript is unavailable
- WHEN a public page renders without executing JavaScript
- THEN all content MUST be visible and usable in its final state
Scenario: IntersectionObserver is unavailable
- WHEN JavaScript runs but
IntersectionObserveris not supported - THEN all reveal targets MUST remain in their final state
Scenario: Reduced motion is preferred
- WHEN the browser reports
prefers-reduced-motion: reduce - THEN page openings, reveals, media clips, and non-essential feedback transitions MUST NOT run
Requirement: Public interaction feedback is short and accessible
CTAs, links, navigation, and form controls SHALL provide short feedback using shared tokens while preserving visible :focus-visible indication. Alerts and validation messages MUST NOT be animated by this contract.
Scenario: Keyboard focus remains visible
- WHEN a visitor focuses an interactive element with the keyboard
- THEN its focus indicator MUST remain visible and MUST NOT be displaced by motion feedback
Scenario: Validation message appears without motion
- WHEN server validation renders an error message
- THEN the message MUST appear in its final position without entrance animation
Requirement: Home progress updates at most once per animation frame
Scroll-driven chapter index and progress updates on the home SHALL be coalesced through requestAnimationFrame, with at most one pending update per frame.
Scenario: Multiple scroll events occur in one frame
- WHEN multiple scroll events fire before the next animation frame
- THEN the home progress calculation MUST execute only once for that frame