Skip to content

Commit b71f8cb

Browse files
committed
Tweak intro animations
1 parent c62b106 commit b71f8cb

File tree

5 files changed

+167
-73
lines changed

5 files changed

+167
-73
lines changed

HOME_PAGE_NOTES.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,15 @@ This file is **append-only**. Do not rewrite or reorganize existing entries—on
4242

4343
- **Breakpoints**: Use Tailwind's default breakpoints (`sm:`, `md:`, `lg:`, etc.) instead of arbitrary breakpoints like `min-[800px]:`. The Figma frames use 800px but `md:` (768px) is close enough.
4444
- **Typography helpers (marketing)**: Shared text utility classes live in `app/styles/marketing.css` (e.g., `.rmx-body`, `.rmx-body-md`, `.rmx-body-lg`, `.rmx-heading-hero`, `.rmx-heading-lg`, `.rmx-heading-xl`, `.rmx-heading-sm`, `.rmx-button-text`, `.rmx-button-text-lg`, `.rmx-body-on-dark`).
45+
46+
- **Intro animation timing variables**: All animation timing is driven by CSS variables in `:root` (not scoped to `.rmx-intro-mask-overlay`) so they can be referenced by any element. Variables: `--rmx-intro-black-hold`, `--rmx-intro-black-fade`, `--rmx-intro-logo-delay`, `--rmx-intro-logo-duration`, `--rmx-intro-logo-easing`, `--rmx-intro-logo-start`, `--rmx-intro-r-fade-duration`, `--rmx-hero-fade-delay`, `--rmx-hero-fade-duration`.
47+
48+
- **Intro R fill fade**: The R shape in the intro mask starts with a solid fill (`--rmx-neutral-200`) that fades out, allowing users to recognize the R shape before content shows through. Implemented using an SVG `<mask>` element to avoid duplicating the R path data.
49+
50+
- **Hero content fade-in**: The `.rmx-hero` class applies a fade-in animation with a delay calculated from the intro animation timing (`--rmx-hero-fade-delay`).
51+
52+
- **Timeline section implementation**:
53+
- Desktop (`xl:+`): Uses an inline SVG (`desktop.tsx`) with glow filters, gradient fills, and year labels
54+
- Mobile/Tablet (`<xl:`): Uses a CSS grid-based layout (`mobile.tsx`) with colored track segments
55+
- Breakpoint at `xl:` (1280px) to switch between layouts
56+
- Both use CSS variables for colors (`--rmx-highlight-*`, `--rmx-shade-*`, `--rmx-neutral-*`)

HOME_PAGE_PLAN.md

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,18 @@
5454
### ✅ Completed
5555

5656
- Intro mask reveal animation (`app/ui/marketing/home/intro-mask-reveal.tsx`)
57+
- R shape starts solid (visible) then fades out to reveal content behind
58+
- Hero text content fades in after intro animation completes
59+
- Animation timing driven by CSS variables in `:root`
60+
- Reduced motion support
5761
- Section 1 — Hero (`app/ui/marketing/home/hero-section.tsx`)
5862
- Section 2 — Pitch + CTA (`app/ui/marketing/home/pitch-section.tsx`)
63+
- Section 3 — Timeline (`app/ui/marketing/home/timeline-section/`)
64+
- Heading + 3 explanatory text blocks with colored links
65+
- Desktop SVG diagram (`desktop.tsx`) with year labels, multi-lane tracks, glow effects
66+
- Mobile grid-based diagram (`mobile.tsx`) with vertical layout
67+
- Responsive breakpoint at `xl:` (1280px)
68+
- CSS variables for all colors
5969
- Section 4 — "Stay in the loop" (`app/ui/marketing/home/stay-in-the-loop-section.tsx`)
6070
- Newsletter subscription card with form
6171
- Discord community card with button
@@ -65,19 +75,16 @@
6575
- Footer (using existing `app/ui/footer.tsx`)
6676
- Typography helpers added in `app/styles/marketing.css` and applied to sections
6777

68-
### Section 3 — Timeline ("The story so far")
78+
### Remaining work
6979

70-
- ✅ Heading + 3 explanatory text blocks
71-
- Year ticks row (2014–2027)
72-
- Multi-lane timeline track (React Router / Remix / Remix 3) with markers
73-
- Export desktop and mobile SVGs from Figma, embed inline
74-
- Use CSS variables for colors
75-
- Screen-reader-friendly milestone list
80+
- **Timeline hover effects (desktop)**: Add hover states for timeline track segments/markers
81+
- **SR-friendly milestone list**: Add visually-hidden list of milestones for screen readers (TODO in `timeline-section/index.tsx`)
7682

7783
### Cleanup
7884

7985
- Update `meta()` title/description/og image
8086
- Delete `app/ui/homepage-scroll-experience.tsx` once unused
87+
- Remove `public/remix-logo-mask.svg` if still present (mask SVG is now inlined)
8188

8289
## Acceptance criteria
8390

@@ -87,7 +94,7 @@
8794
- Accessible semantics (heading hierarchy, landmarks, form labels, SR-only timeline)
8895
- Styles scoped to `app/styles/marketing.css`
8996

90-
## Questions
97+
## Questions (resolved)
9198

92-
- **Timeline SVG export**: Store exports in `design-assets/` or just paste SVG markup into component?
93-
- **Timeline breakpoint**: Switch SVGs at `<768px` or align with Figma frame sizes (380 vs 800 vs 1400/1920)?
99+
- **Timeline SVG export**: ~~Store exports in `design-assets/` or just paste SVG markup into component?~~ → Inlined in components (`desktop.tsx` / `mobile.tsx`)
100+
- **Timeline breakpoint**: ~~Switch SVGs at `<768px` or align with Figma frame sizes?~~ → Switch at `xl:` (1280px) — desktop diagram on xl+, mobile grid below

app/styles/marketing.css

Lines changed: 98 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,31 @@
2727
--rmx-button-surface-secondary: #ebeff2;
2828
--rmx-button-label-secondary: #25292d;
2929

30-
/* Shadow recipes (from Figma) */
3130
--rmx-shadow-low:
3231
0 1px 1px 1px #00274f59, 0 2px 3px 0 #0138701a, 0 4px 4px 0 #0138700d;
3332
--rmx-shadow-mid:
3433
0 2px 4px 0 #011d3940, 0 8px 12px 0 #01387014, 0 12px 16px 0 #0138700a,
3534
0 16px 20px 0 #0138700a, 0 20px 24px 0 #01387005;
3635

36+
--rmx-intro-black-hold: 150ms;
37+
--rmx-intro-black-fade: 400ms;
38+
39+
--rmx-intro-logo-delay: calc(
40+
var(--rmx-intro-black-hold) + var(--rmx-intro-black-fade)
41+
);
42+
--rmx-intro-logo-duration: 1500ms;
43+
--rmx-intro-logo-easing: cubic-bezier(0.85, 0, 0.15, 1);
44+
--rmx-intro-logo-start: calc(
45+
var(--rmx-intro-black-hold) + var(--rmx-intro-logo-delay)
46+
);
47+
48+
--rmx-intro-r-fade-duration: 600ms;
49+
50+
--rmx-hero-fade-delay: calc(
51+
var(--rmx-intro-logo-start) + (var(--rmx-intro-logo-duration) / 2)
52+
);
53+
--rmx-hero-fade-duration: 750ms;
54+
3755
&:where(.dark) {
3856
--rmx-text-primary: #e5e7eb;
3957
--rmx-text-muted: #9aa0a6;
@@ -227,66 +245,43 @@
227245
background: var(--rmx-surface-3);
228246
}
229247

230-
.rmx-intro-mask-overlay {
231-
/* How long the screen stays black before fading */
232-
--intro-black-hold: 150ms;
233-
/* How long the black fade-out takes */
234-
--intro-black-fade: 400ms;
235-
/* Delay after black fade before logo starts animating */
236-
--intro-logo-delay: calc(var(--intro-black-hold) + var(--intro-black-fade));
237-
/* How long the logo scale/reveal animation takes */
238-
--intro-logo-duration: 1500ms;
239-
/* Easing for the logo animation */
240-
--intro-logo-easing: cubic-bezier(0.85, 0, 0.15, 1);
241-
242-
/* Computed: total delay before logo animation starts */
243-
--intro-logo-start: calc(var(--intro-black-hold) + var(--intro-logo-delay));
248+
/* ========================================
249+
* Intro & Hero Animations
250+
* ========================================
251+
*
252+
* Animation sequence:
253+
* 1. Black screen holds, then fades out
254+
* 2. Logo scales up and reveals content behind
255+
* 3. R fill fades out (so users can see the R shape first)
256+
* 4. Hero text content fades in
257+
*
258+
* ======================================== */
244259

245-
position: fixed;
246-
inset: 0;
247-
z-index: 9999;
248-
overflow: hidden;
249-
pointer-events: none;
250-
251-
animation: rmx-intro-reveal var(--intro-logo-duration)
252-
var(--intro-logo-easing) var(--intro-logo-start) forwards;
253-
}
260+
/* --- Keyframes --- */
254261

255-
/* Black cover that fades out before the logo animation starts */
256-
.rmx-intro-mask-overlay::before {
257-
content: "";
258-
position: absolute;
259-
inset: 0;
260-
background: black;
261-
z-index: 1;
262-
animation: rmx-intro-black-fade var(--intro-black-fade) ease-out
263-
var(--intro-black-hold) forwards;
262+
@keyframes rmx-fade-in {
263+
from {
264+
opacity: 0;
265+
}
266+
to {
267+
opacity: 1;
268+
}
264269
}
265270

266-
@keyframes rmx-intro-black-fade {
267-
0% {
271+
@keyframes rmx-fade-out {
272+
from {
268273
opacity: 1;
269274
}
270-
100% {
275+
to {
271276
opacity: 0;
272277
}
273278
}
274279

275-
.rmx-intro-mask-svg {
276-
position: absolute;
277-
width: 100%;
278-
height: 100%;
279-
transform-origin: center center;
280-
281-
animation: rmx-intro-scale var(--intro-logo-duration) var(--intro-logo-easing)
282-
var(--intro-logo-start) forwards;
283-
}
284-
285280
@keyframes rmx-intro-scale {
286-
0% {
281+
from {
287282
transform: translate(0, 0) scale(1);
288283
}
289-
100% {
284+
to {
290285
transform: translate(-55%, 100%) scale(80);
291286
}
292287
}
@@ -306,19 +301,71 @@
306301
}
307302
}
308303

304+
.rmx-intro-mask-overlay {
305+
position: fixed;
306+
inset: 0;
307+
z-index: 9999;
308+
overflow: hidden;
309+
pointer-events: none;
310+
animation: rmx-intro-reveal var(--rmx-intro-logo-duration)
311+
var(--rmx-intro-logo-easing) var(--rmx-intro-logo-start) forwards;
312+
}
313+
314+
.rmx-intro-mask-overlay::before {
315+
content: "";
316+
position: absolute;
317+
inset: 0;
318+
background: black;
319+
z-index: 1;
320+
animation: rmx-fade-out var(--rmx-intro-black-fade) ease-out
321+
var(--rmx-intro-black-hold) forwards;
322+
}
323+
324+
/* SVG that scales up to reveal content */
325+
.rmx-intro-mask-svg {
326+
position: absolute;
327+
width: 100%;
328+
height: 100%;
329+
transform-origin: center center;
330+
animation: rmx-intro-scale var(--rmx-intro-logo-duration)
331+
var(--rmx-intro-logo-easing) var(--rmx-intro-logo-start) forwards;
332+
}
333+
334+
/* Solid fill behind the R that fades out so users can see the R shape first */
335+
.rmx-intro-r-fill {
336+
animation: rmx-fade-out var(--rmx-intro-r-fade-duration) ease-out
337+
var(--rmx-intro-logo-start) forwards;
338+
}
339+
340+
.rmx-hero {
341+
opacity: 0;
342+
animation: rmx-fade-in var(--rmx-hero-fade-duration) ease-out
343+
var(--rmx-hero-fade-delay) forwards;
344+
}
345+
309346
@media (prefers-reduced-motion: reduce) {
310-
.rmx-intro-mask-overlay {
347+
.rmx-intro-mask-overlay,
348+
.rmx-intro-mask-overlay::before,
349+
.rmx-intro-mask-svg,
350+
.rmx-intro-r-fill {
311351
animation: none;
352+
}
353+
354+
.rmx-intro-mask-overlay {
312355
visibility: hidden;
313356
opacity: 0;
314357
}
315358

316359
.rmx-intro-mask-overlay::before {
317-
animation: none;
318360
opacity: 0;
319361
}
320362

321-
.rmx-intro-mask-svg {
363+
.rmx-intro-r-fill {
364+
opacity: 0;
365+
}
366+
367+
.rmx-hero {
322368
animation: none;
369+
opacity: 1;
323370
}
324371
}

app/ui/marketing/home/hero-section.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1+
import cx from "clsx";
12
import heroImageSrc from "/racecar-teaser-hero.webp";
23

34
export function HeroSection() {
45
return (
56
<section className="flex min-h-[540px] flex-col items-center justify-end overflow-hidden px-12 pb-6 md:min-h-[90vh] md:pb-12">
67
<div className="flex w-full flex-col items-center gap-12 md:gap-24">
7-
<div className="text-rmx-primary flex w-full flex-col items-start gap-12 md:items-center md:gap-6 md:text-center">
8+
<div
9+
className={cx(
10+
"rmx-hero",
11+
"text-rmx-primary flex w-full flex-col items-start gap-12 md:items-center md:gap-6 md:text-center",
12+
)}
13+
>
814
<h1 className="rmx-heading-hero">
915
Remix 3 is under active development
1016
</h1>

app/ui/marketing/home/intro-mask-reveal.tsx

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
const REMIX_R_PATH =
2+
"M1757.07 884.025L1749.55 912.385H1889.4C1894.66 912.385 1898.3 914.76 1897.52 917.688H1897.52C1896.74 920.617 1891.84 922.991 1886.59 922.991H1746.73L1739.21 951.354H1819.06C1821.32 951.354 1823.54 952.008 1825.46 953.241L1881.68 990.322H1968.08L1909 951.354H1911.88C1940 951.354 1966.17 938.658 1970.33 922.995L1973.14 912.39C1977.3 896.727 1957.86 884.03 1929.73 884.03V884.027L1929.74 884.025H1757.07ZM1736.61 961.143L1728.88 990.318H1803.09L1809.47 966.268C1810.16 963.682 1808.23 961.143 1805.58 961.143H1736.61Z";
3+
14
export function IntroMaskReveal() {
25
return (
36
<div aria-hidden="true" className="rmx-intro-mask-overlay">
@@ -8,10 +11,39 @@ export function IntroMaskReveal() {
811
xmlns="http://www.w3.org/2000/svg"
912
preserveAspectRatio="xMidYMid slice"
1013
>
14+
<defs>
15+
{/* Mask: white = visible, black = transparent */}
16+
<mask id="r-cutout-mask">
17+
<rect x="0" y="0" width="3600" height="1921" fill="white" />
18+
<path d={REMIX_R_PATH} fill="black" />
19+
</mask>
20+
<clipPath id="color-stripes">
21+
<rect
22+
width="374"
23+
height="134"
24+
fill="white"
25+
transform="translate(1613 856)"
26+
/>
27+
</clipPath>
28+
</defs>
29+
30+
{/* Solid R fill that fades out so users can see the shape first */}
1131
<path
12-
d="M3600 1920.5H0V0.5H3600V1920.5ZM1757.07 884.025L1749.55 912.385H1889.4C1894.66 912.385 1898.3 914.76 1897.52 917.688H1897.52C1896.74 920.617 1891.84 922.991 1886.59 922.991H1746.73L1739.21 951.354H1819.06C1821.32 951.354 1823.54 952.008 1825.46 953.241L1881.68 990.322H1968.08L1909 951.354H1911.88C1940 951.354 1966.17 938.658 1970.33 922.995L1973.14 912.39C1977.3 896.727 1957.86 884.03 1929.73 884.03V884.027L1929.74 884.025H1757.07ZM1736.61 961.143L1728.88 990.318H1803.09L1809.47 966.268C1810.16 963.682 1808.23 961.143 1805.58 961.143H1736.61Z"
32+
className="rmx-intro-r-fill"
33+
d={REMIX_R_PATH}
34+
fill="var(--rmx-neutral-200)"
35+
/>
36+
37+
{/* Black overlay with R cutout */}
38+
<rect
39+
x="0"
40+
y="0.5"
41+
width="3600"
42+
height="1920"
1343
fill="black"
44+
mask="url(#r-cutout-mask)"
1445
/>
46+
1547
<g clipPath="url(#color-stripes)">
1648
<path
1749
d="M1690.45 960.945H1667.23L1659.48 990H1682.69L1690.45 960.945Z"
@@ -74,16 +106,6 @@ export function IntroMaskReveal() {
74106
fill="#F44250"
75107
/>
76108
</g>
77-
<defs>
78-
<clipPath id="color-stripes">
79-
<rect
80-
width="374"
81-
height="134"
82-
fill="white"
83-
transform="translate(1613 856)"
84-
/>
85-
</clipPath>
86-
</defs>
87109
</svg>
88110
</div>
89111
);

0 commit comments

Comments
 (0)