Skip to content

Commit 69c4835

Browse files
committed
add animation to landing
1 parent 169c025 commit 69c4835

File tree

5 files changed

+182
-18
lines changed

5 files changed

+182
-18
lines changed

site/docs/components/landing/ComponentPreview.tsx

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import type { ReactNode } from 'react';
2-
import { useEffect, useState } from 'react';
2+
import { useEffect, useState, useRef } from 'react';
33
import { useTheme } from '../../contexts/Theme.tsx';
44
import CopyIcon from '../svg/CopySvg.js';
55
import CheckIcon from '../svg/checkSvg.js';
66
import { getHighlightedCode } from './getHighlightedCode.tsx';
7+
import { animate } from 'motion';
78

89
// Demo components and code snippets
910
import CheckoutDemo, { checkoutDemoCode } from './CheckoutDemo.tsx';
@@ -88,6 +89,10 @@ function ComponentPreview() {
8889
});
8990
};
9091

92+
const handleTabChange = (index: number) => {
93+
setActiveTab(index);
94+
};
95+
9196
if (!isClient) {
9297
return <div>Loading...</div>;
9398
}
@@ -97,7 +102,7 @@ function ComponentPreview() {
97102
<div className="flex flex-col items-center gap-8 lg:flex-row lg:items-start">
98103
<ComponentList
99104
activeTab={activeTab}
100-
setActiveTab={setActiveTab}
105+
setActiveTab={handleTabChange}
101106
components={components}
102107
/>
103108
<PreviewContainer
@@ -124,6 +129,24 @@ function ComponentList({
124129
setActiveTab,
125130
components,
126131
}: ComponentListProps) {
132+
const handleClick = (index: number) => {
133+
const button = document.querySelector(`button[data-index="${index}"]`);
134+
if (button) {
135+
animate(
136+
button,
137+
{
138+
x: [-2, 0],
139+
opacity: [0.6, 1]
140+
},
141+
{
142+
duration: 0.15,
143+
easing: 'ease-out'
144+
}
145+
);
146+
}
147+
setActiveTab(index);
148+
};
149+
127150
return (
128151
<div className="w-full md:w-[300px] lg:flex-shrink-0">
129152
<h3 className="pb-4 font-medium text-3xl text-zinc-900 dark:text-zinc-100">
@@ -137,12 +160,13 @@ function ComponentList({
137160
<div key={comp.name} className="mb-4">
138161
<button
139162
type="button"
163+
data-index={index}
140164
className={`w-full px-3 py-2 text-left text-base lg:text-lg ${
141165
activeTab === index
142166
? 'rounded-lg bg-zinc-100 font-semibold text-indigo-600 dark:bg-[#0F0F0F] dark:text-indigo-400'
143167
: 'text-zinc-700 hover:rounded-lg hover:bg-zinc-100 dark:text-zinc-300 dark:hover:bg-[#0F0F0F]'
144168
}`}
145-
onClick={() => setActiveTab(index)}
169+
onClick={() => handleClick(index)}
146170
>
147171
{comp.name}
148172
{activeTab === index && (
@@ -191,7 +215,9 @@ function PreviewContainer({
191215
}, [activeTab, theme]);
192216

193217
return (
194-
<div className="h-[600px] w-[375px] overflow-hidden rounded-lg border border-zinc-200 bg-zinc-50 sm:w-[600px] md:h-[670px] md:w-[700px] dark:border-zinc-900 dark:bg-[#0f0f0f]">
218+
<div
219+
className="preview-container h-[600px] w-[375px] overflow-hidden rounded-lg border border-zinc-200 bg-zinc-50 sm:w-[600px] md:h-[670px] md:w-[700px] dark:border-zinc-900 dark:bg-[#0f0f0f]"
220+
>
195221
<div className="mt-2 flex items-center justify-between border-zinc-200 border-b px-3 dark:border-zinc-900">
196222
<div className="flex">
197223
<TabButton
@@ -245,15 +271,34 @@ function PreviewContainer({
245271
}
246272

247273
function TabButton({ isActive, onClick, children }: TabButtonProps) {
274+
const handleClick = () => {
275+
const button = document.querySelector(`button[data-tab="${children}"]`);
276+
if (button) {
277+
animate(
278+
button,
279+
{
280+
y: [2, 0],
281+
opacity: [0.7, 1]
282+
},
283+
{
284+
duration: 0.2,
285+
easing: [.22, 1.14, .59, 1] // Spring-like easing
286+
}
287+
);
288+
}
289+
onClick();
290+
};
291+
248292
return (
249293
<button
250294
type="button"
295+
data-tab={children}
251296
className={`px-4 py-2 font-medium text-sm ${
252297
isActive
253298
? 'border-indigo-600 border-b-2 text-indigo-600 dark:border-indigo-400 dark:text-indigo-400'
254299
: 'text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-100'
255300
}`}
256-
onClick={onClick}
301+
onClick={handleClick}
257302
>
258303
{children}
259304
</button>

site/docs/hooks/useScrollAnimation.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { useEffect, useRef } from 'react';
2+
import { animate } from 'motion';
3+
4+
export function useScrollAnimation(options = {
5+
threshold: 0.2,
6+
springConfig: { mass: 1, stiffness: 100, damping: 15 }
7+
}) {
8+
const ref = useRef<HTMLElement>(null);
9+
10+
useEffect(() => {
11+
const element = ref.current;
12+
if (!element) return;
13+
14+
const observer = new IntersectionObserver(
15+
([entry]) => {
16+
if (entry.isIntersecting) {
17+
animate(
18+
element,
19+
{
20+
opacity: [0, 1],
21+
y: [50, 0]
22+
} as const as any,
23+
{
24+
duration: 0.8,
25+
ease: [.22, .03, .26, 1],
26+
delay: 0.2
27+
}
28+
);
29+
observer.disconnect();
30+
}
31+
},
32+
{ threshold: options.threshold }
33+
);
34+
35+
observer.observe(element);
36+
return () => observer.disconnect();
37+
}, []);
38+
39+
return ref;
40+
}

site/docs/pages/index.mdx

Lines changed: 85 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,58 @@ import { FundButton } from '@coinbase/onchainkit/fund';
3939
import Tweets from '../components/landing/Tweets';
4040
import ComponentPreview from '../components/landing/ComponentPreview';
4141
import LandingFooter from '../components/landing/LandingFooter';
42+
import { animate } from 'motion';
43+
import { useEffect, useRef } from 'react';
44+
import { useScrollAnimation } from '../hooks/useScrollAnimation';
4245

43-
44-
<div className="max-w-[1225px] mx-auto vp-doc relative px-[24px] mb-[26px] mt-0 md:px-0 md:mb-[64px] pb-24">
45-
<div className="mx-auto max-w-2xl">
46-
<div className="md:text-center flex flex-col gap-6 pt-[100px] pb-8">
47-
<h1 className="text-center text-5xl md:text-7xl font-medium no-underline text-zinc-950 dark:text-zinc-50 tracking-[-0.1rem]">OnchainKit</h1>
48-
<div className="landing-page-hero text-center text-xl md:text-2xl text-zinc-950 dark:text-zinc-50">
49-
Build onchain apps with ready-to-use React components and Typescript utilities.
46+
<div
47+
className="max-w-[1225px] mx-auto vp-doc relative px-[24px] mb-[26px] mt-0 md:px-0 md:mb-[64px] pb-24"
48+
ref={useScrollAnimation()}
49+
>
50+
<div className="mx-auto max-w-2xl" ref={useRef()}>
51+
<div className="md:text-center flex flex-col pt-[100px] pb-8">
52+
<h1
53+
className="text-center text-5xl md:text-7xl font-medium no-underline text-zinc-950 dark:text-zinc-50 tracking-[-0.1rem]"
54+
ref={el => {
55+
if (el) {
56+
animate(
57+
el,
58+
{ opacity: [0, 1], y: [30, 0] },
59+
{ duration: 0.6, easing: 'ease-out' }
60+
)
61+
}
62+
}}
63+
>
64+
OnchainKit
65+
</h1>
66+
<div
67+
className="landing-page-hero text-center text-xl md:text-2xl text-zinc-950 dark:text-zinc-50"
68+
ref={el => {
69+
if (el) {
70+
animate(
71+
el,
72+
{ opacity: [0, 1], y: [30, 0] },
73+
{ duration: 0.6, easing: 'ease-out', delay: 0.2 }
74+
)
75+
}
76+
}}
77+
>
78+
Build onchain apps with ready-to-use React components and Typescript utilities.
5079
</div>
5180
</div>
52-
<div className="flex flex-col items-center gap-6 w-full">
81+
82+
<div
83+
className="flex flex-col items-center gap-6 w-full"
84+
ref={el => {
85+
if (el) {
86+
animate(
87+
el,
88+
{ opacity: [0, 1], y: [30, 0] },
89+
{ duration: 0.6, easing: 'ease-out', delay: 0.4 }
90+
)
91+
}
92+
}}
93+
>
5394
<div id="home-install" className="h-full w-10/12">
5495

5596
:::code-group
@@ -71,22 +112,53 @@ bun add @coinbase/onchainkit
71112
```
72113

73114
:::
74-
</div>
75-
<a href="/getting-started" title="Get started with OnchainKit" className="alternate-button mx-auto md:mx-0 flex justify-center items-center bg-indigo-600 hover:bg-indigo-700 text-gray-50 dark:bg-indigo-400 dark:text-gray-950 dark:hover:bg-indigo-300 font-semibold leading-6 px-3 py-2 rounded-lg w-[140px]">
115+
</div>
116+
<a
117+
href="/getting-started"
118+
title="Get started with OnchainKit"
119+
className="alternate-button mx-auto md:mx-0 flex justify-center items-center bg-indigo-600 hover:bg-indigo-700 text-gray-50 dark:bg-indigo-400 dark:text-gray-950 dark:hover:bg-indigo-300 font-semibold leading-6 px-3 py-2 rounded-lg w-[140px]"
120+
ref={el => {
121+
if (el) {
122+
animate(
123+
el,
124+
{ opacity: [0, 1], y: [30, 0] },
125+
{ duration: 0.6, easing: 'ease-out', delay: 0.6 }
126+
)
127+
128+
el.addEventListener('mousedown', () => {
129+
animate(el, { scale: 0.98 }, { duration: 0.1 })
130+
})
131+
el.addEventListener('mouseup', () => {
132+
animate(el, { scale: 1 }, { duration: 0.1 })
133+
})
134+
}
135+
}}
136+
>
76137
Get started
77138
</a>
78139
</div>
79140
</div>
80141
</div>
81142

82143
<main className="items-center flex flex-col vp-doc relative">
83-
<div className="live-demo-section pt-12">
144+
<div
145+
className="live-demo-section pt-12"
146+
ref={useScrollAnimation()}
147+
>
84148
<ComponentPreview />
85149
</div>
86-
<div className="testimonials-section pt-12">
150+
151+
<div
152+
className="testimonials-section pt-12"
153+
ref={useScrollAnimation()}
154+
>
87155
<Tweets />
88156
</div>
89-
<div className="footer-section pt-12">
157+
158+
<div
159+
className="footer-section pt-12"
160+
ref={useScrollAnimation()}
161+
>
90162
<LandingFooter />
91163
</div>
92164
</main>

site/docs/styles.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@
6767
padding: 8px 8px 0;
6868
}
6969

70+
.dark .vocs_NavigationMenu_content{
71+
background-color: #0F0F0F;
72+
border-color: #18181b !important;
73+
border-radius: 8px;
74+
}
75+
7076
.vocs_DesktopTopNav_item.vocs_NavigationMenu_link.vocs_Link.vocs_Link_styleless {
7177
color: #030712;
7278
}

site/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"@types/react": "latest",
1414
"@vercel/edge": "^1.1.1",
1515
"express": "^4.21.1",
16+
"motion": "^11.12.0",
1617
"permissionless": "^0.1.29",
1718
"react": "18",
1819
"react-dom": "18",

0 commit comments

Comments
 (0)