├── .eslintrc.json
├── .github
└── FUNDING.yml
├── .gitignore
├── .prettierrc.json
├── README.md
├── desktop.ini
├── jsconfig.json
├── next.config.mjs
├── package.json
├── pnpm-lock.yaml
├── postcss.config.mjs
├── public
├── assets
│ ├── banner.png
│ ├── desktop.png
│ ├── mobile.png
│ └── preview.mp4
└── images
│ ├── favicon-dark.ico
│ └── favicon-light.ico
└── src
├── app
├── globals.css
├── layout.jsx
└── page.jsx
├── components
├── common
│ ├── ButtonCopy.jsx
│ ├── ButtonSwitch.jsx
│ └── TooltipList.jsx
├── effects
│ ├── BottomBlur.jsx
│ ├── TextBlurIn.jsx
│ └── TextScroll.jsx
├── icons
│ └── Favicon.jsx
├── layouts
│ ├── Content.jsx
│ ├── Footer.jsx
│ ├── Header.jsx
│ └── Hero.jsx
└── snippets
│ └── CodeViewer.jsx
├── hooks
├── useClickOutside.js
├── useInjectAnimations.js
└── useTheme.js
└── styles
└── gradient-blur.css
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["next", "next/core-web-vitals"]
3 | }
4 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [JhojanGgarcia]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
12 | polar: # Replace with a single Polar username
13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
14 | thanks_dev: # Replace with a single thanks.dev username
15 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["prettier-plugin-tailwindcss"]
3 | }
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | Welcome to **Popply**, a refined animation library for tooltips—those subtle yet powerful UI elements that enhance user guidance and interaction.
4 |
5 | Tooltips are typically activated by a `mouseEnter` event. With **Popply**, bringing them to life is seamless and efficient.
6 | Simply copy the desired animation, place it into your `./styles` folder, and apply it to any tooltip that could use a touch of motion.
7 |
8 | Explore a curated collection of **50+ animations**, including `fadeIn`, `slideIn`, `flyIn`, `punchIn`, `zoomIn`, and many others—each designed to complement your user experience with precision.
9 |
10 | Start exploring and find the animation that best aligns with your design vision.
11 |
12 |
13 |
14 | # Preview
15 |
16 | By default, **Popply** animations are adaptive, this ensures smooth performance on all devices.
17 | However, tooltips often behave differently on desktop (hovering) and mobile (tapping), so it is important to preview them in both contexts.
18 |
19 |
20 |
21 | On mobile, consider triggering tooltips via ```onClick```, or accessibility-focused interactions.
22 |
23 |
24 |
25 |
26 |
27 |
28 | > [!IMPORTANT]
29 | > **Popply** is a free and open source project.
30 | > If you find it useful, please consider [donating](https://buymeacoffee.com/jhojanggar6).
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/desktop.ini:
--------------------------------------------------------------------------------
1 | [.ShellClassInfo]
2 | IconResource=C:\Users\USUARIO\Pictures\Logos\popply.ico,0
3 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "paths": {
5 | "@/*": ["src/*"]
6 | }
7 | },
8 | "include": ["src"]
9 | }
10 |
--------------------------------------------------------------------------------
/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {};
3 |
4 | export default nextConfig;
5 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "popply",
3 | "version": "0.1.0",
4 | "type": "module",
5 | "private": true,
6 | "scripts": {
7 | "dev": "next dev",
8 | "build": "next build",
9 | "start": "next start",
10 | "lint": "next lint"
11 | },
12 | "dependencies": {
13 | "@tailwindcss/postcss": "^4.0.15",
14 | "@vercel/speed-insights": "^1.2.0",
15 | "framer-motion": "^11.9.0",
16 | "lucide-react": "^0.483.0",
17 | "next": "^14.2.25",
18 | "postcss": "^8.5.3",
19 | "react": "^18.3.1",
20 | "react-dom": "^18.3.1",
21 | "react-syntax-highlighter": "^15.6.1",
22 | "tailwindcss": "^4.0.15"
23 | },
24 | "devDependencies": {
25 | "@types/node": "^22.7.4",
26 | "@types/react": "18.3.3",
27 | "eslint": "^8.57.1",
28 | "eslint-config-next": "14.2.3",
29 | "eslint-import-resolver-alias": "^1.1.2",
30 | "eslint-plugin-import": "^2.31.0",
31 | "prettier": "^3.5.3",
32 | "prettier-plugin-tailwindcss": "^0.6.11",
33 | "shiki": "^1.20.0"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | "@tailwindcss/postcss": {},
5 | },
6 | };
7 | export default config;
8 |
--------------------------------------------------------------------------------
/public/assets/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JhojanGgarcia/PopplyLibrary/8fde4baa642f311e1c631e9dc5a51c780e5f819e/public/assets/banner.png
--------------------------------------------------------------------------------
/public/assets/desktop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JhojanGgarcia/PopplyLibrary/8fde4baa642f311e1c631e9dc5a51c780e5f819e/public/assets/desktop.png
--------------------------------------------------------------------------------
/public/assets/mobile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JhojanGgarcia/PopplyLibrary/8fde4baa642f311e1c631e9dc5a51c780e5f819e/public/assets/mobile.png
--------------------------------------------------------------------------------
/public/assets/preview.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JhojanGgarcia/PopplyLibrary/8fde4baa642f311e1c631e9dc5a51c780e5f819e/public/assets/preview.mp4
--------------------------------------------------------------------------------
/public/images/favicon-dark.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JhojanGgarcia/PopplyLibrary/8fde4baa642f311e1c631e9dc5a51c780e5f819e/public/images/favicon-dark.ico
--------------------------------------------------------------------------------
/public/images/favicon-light.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JhojanGgarcia/PopplyLibrary/8fde4baa642f311e1c631e9dc5a51c780e5f819e/public/images/favicon-light.ico
--------------------------------------------------------------------------------
/src/app/globals.css:
--------------------------------------------------------------------------------
1 | @import "tailwindcss";
2 | @custom-variant dark (&:where(.dark, .dark *));
3 |
4 | /* Custom scrollbar styles */
5 | ::-webkit-scrollbar {
6 | width: 3px;
7 | height: 3px;
8 | }
9 |
10 | ::-webkit-scrollbar-thumb {
11 | background-color: #323232;
12 | border-radius: 10px;
13 | -webkit-border-radius: 10px;
14 | -moz-border-radius: 10px;
15 | -ms-border-radius: 10px;
16 | -o-border-radius: 10px;
17 | }
18 |
19 | ::-webkit-scrollbar {
20 | width: 4px;
21 | height: 4px;
22 | }
23 |
24 | ::-webkit-scrollbar-thumb {
25 | background-color: #bfb3dc77;
26 | border-radius: 10px;
27 | }
28 |
29 | ::-webkit-scrollbar-thumb:hover {
30 | background-color: #222121a1;
31 | }
32 |
33 | ::-webkit-scrollbar-track {
34 | background-color: #0f0f0f;
35 | }
36 |
37 | .dark ::-webkit-scrollbar-thumb {
38 | background-color: #bfb3dc;
39 | }
40 |
41 | .dark ::-webkit-scrollbar-track {
42 | background-color: #fff;
43 | }
44 |
45 | html {
46 | position: relative;
47 | }
48 |
--------------------------------------------------------------------------------
/src/app/layout.jsx:
--------------------------------------------------------------------------------
1 | import { Bricolage_Grotesque } from "next/font/google";
2 | import { SpeedInsights } from "@vercel/speed-insights/next"
3 | import "./globals.css";
4 |
5 | const bricolageFont = Bricolage_Grotesque({
6 | subsets: ["latin"],
7 | variable: "--font-bricolage",
8 | weight: ["200", "300", "400", "500", "600", "700", "800"],
9 | });
10 |
11 | export const metadata = {
12 | title: "Popply library",
13 | description: "Components about animations and Effects for your website.",
14 | icons: {
15 | icon: [
16 | {
17 | media: "(prefers-color-scheme: light)",
18 | url: "/images/favicon-dark.ico",
19 | href: "/images/favicon-dark.ico",
20 | },
21 | {
22 | media: "(prefers-color-scheme: dark)",
23 | url: "/images/favicon-light.ico",
24 | href: "/images/favicon-light.ico",
25 | },
26 | ],
27 | },
28 | };
29 |
30 | export default function RootLayout({ children }) {
31 | return (
32 |
33 |
37 | {children}
38 |
39 |
40 |
41 | );
42 | }
43 |
--------------------------------------------------------------------------------
/src/app/page.jsx:
--------------------------------------------------------------------------------
1 | import Header from "@/components/layouts/Header";
2 | import Hero from "@/components/layouts/Hero";
3 | import Content from "@/components/layouts/Content";
4 | import Footer from "@/components/layouts/Footer";
5 |
6 | export default function Home() {
7 | return (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
18 |
19 |
23 |
27 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/src/components/common/ButtonCopy.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useCallback, useState } from "react";
4 | import { Check, Copy, LoaderCircle } from "lucide-react";
5 | import { AnimatePresence, motion } from "framer-motion";
6 |
7 | const buttonCopy = {
8 | idle: ,
9 | loading: ,
10 | success: ,
11 | };
12 |
13 | export default function ButtonCopy({ onClick }) {
14 | const [buttonState, setButtonState] = useState("idle");
15 |
16 | const handleClick = useCallback(() => {
17 | setButtonState("loading");
18 | setTimeout(() => {
19 | setButtonState("success");
20 | }, 1000);
21 |
22 | setTimeout(() => {
23 | setButtonState("idle");
24 | }, 3000);
25 | }, []);
26 |
27 | return (
28 |
29 |
51 |
52 | );
53 | }
54 |
--------------------------------------------------------------------------------
/src/components/common/ButtonSwitch.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import React from "react";
4 |
5 | import useTheme from "@/hooks/useTheme";
6 | import { Moon, Sun } from "lucide-react";
7 | import { motion } from "framer-motion";
8 |
9 | export default function Switch() {
10 | const { theme, toggleTheme } = useTheme();
11 | return (
12 |
26 | );
27 | }
28 |
--------------------------------------------------------------------------------
/src/components/common/TooltipList.jsx:
--------------------------------------------------------------------------------
1 | import Favicon from "@/components/icons/Favicon";
2 | import Image from "next/image";
3 |
4 | const TooltipContent = [
5 | {
6 | name: "fadeIn",
7 | category: "fade",
8 | css: `
9 | .fadein {
10 | animation: fade-in 1s ease-in-out;
11 | -webkit-animation: fade-in 1s ease-in-out;
12 | }
13 | @keyframes fade-in {
14 | from {
15 | opacity: 0;
16 | }
17 | to {
18 | opacity: 1;
19 | }
20 | }
21 | `,
22 | tooltipContainer: () => {
23 | return (
24 |
25 |
26 |
27 | );
28 | },
29 | tooltipSample: () => {
30 | return (
31 |
32 | Sample
33 |
34 | );
35 | },
36 | },
37 | {
38 | name: "fadeInDown",
39 | category: "fade",
40 | css: `
41 | .fadeindown {
42 | animation: fade-in-down 1s ease-in;
43 | -webkit-animation: fade-in-down 1s ease-in;
44 | }
45 | @keyframes fade-in-down {
46 | from {
47 | opacity: 0;
48 | -webkit-transform: translate3d(0, -100%, 0);
49 | transform: translate3d(0, -100%, 0);
50 | }
51 | to {
52 | opacity: 1;
53 | -webkit-transform: translate3d(0, 0, 0);
54 | transform: translate3d(0, 0, 0);
55 | }
56 | }
57 | `,
58 | tooltipContainer: () => {
59 | return (
60 |
61 |
62 |
63 | );
64 | },
65 | tooltipSample: () => {
66 | return (
67 |
68 | Sample
69 |
70 | );
71 | },
72 | },
73 | {
74 | name: "fadeInLeft",
75 | category: "fade",
76 | css: `
77 | .fadeinleft {
78 | animation: fade-in-left 1s ease-in-out;
79 | -webkit-animation: fade-in-left 1s ease-in-out;
80 | }
81 | @keyframes fade-in-left {
82 | from {
83 | opacity: 0;
84 | -webkit-transform: translate3d(-100%, 0, 0);
85 | transform: translate3d(-100%, 0, 0);
86 | }
87 | to {
88 | opacity: 1;
89 | -webkit-transform: translate3d(0, 0, 0);
90 | transform: translate3d(0, 0, 0);
91 | }
92 | }
93 | `,
94 | tooltipContainer: () => {
95 | return (
96 |
97 |
98 |
99 | );
100 | },
101 | tooltipSample: () => {
102 | return (
103 |
104 | Sample
105 |
106 | );
107 | },
108 | },
109 | {
110 | name: "fadeInRight",
111 | category: "fade",
112 | css: `
113 | .fadeinright {
114 | animation: fade-in-right 1s ease-in-out;
115 | -webkit-animation: fade-in-right 1s ease-in-out;
116 | }
117 | @keyframes fade-in-right {
118 | from {
119 | opacity: 0;
120 | -webkit-transform: translate3d(100%, 0, 0);
121 | transform: translate3d(100%, 0, 0);
122 | }
123 | to {
124 | opacity: 1;
125 | -webkit-transform: translate3d(0, 0, 0);
126 | transform: translate3d(0, 0, 0);
127 | }
128 | }
129 | `,
130 | tooltipContainer: () => {
131 | return (
132 |
133 |
134 |
135 | );
136 | },
137 | tooltipSample: () => {
138 | return (
139 |
140 | Sample
141 |
142 | );
143 | },
144 | },
145 | {
146 | name: "fadeInUp",
147 | category: "fade",
148 | css: `
149 | .fadeinup {
150 | animation: fade-in-up 1s ease-in-out;
151 | -webkit-animation: fade-in-up 1s ease-in-out;
152 | }
153 | @keyframes fade-in-up {
154 | from {
155 | opacity: 0;
156 | -webkit-transform: translate3d(0, -100%, 0);
157 | transform: translate3d(0, -100%, 0);
158 | }
159 | to {
160 | opacity: 1;
161 | -webkit-transform: translate3d(0, 0, 0);
162 | transform: translate3d(0, 0, 0);
163 | }
164 | }
165 | `,
166 |
167 | tooltipContainer: () => {
168 | return (
169 |
170 |
171 |
172 | );
173 | },
174 | tooltipSample: () => {
175 | return (
176 |
177 | Sample
178 |
179 | );
180 | },
181 | },
182 | {
183 | name: "fadeOut",
184 | category: "fade",
185 | css: `
186 | .fadeout {
187 | animation: fade-out 1s ease-out;
188 | -webkit-animation: fade-out 1s ease-out;
189 | }
190 | @keyframes fade-out {
191 | from {
192 | opacity: 1;
193 | }
194 | to {
195 | opacity: 0;
196 | }
197 | }
198 | `,
199 |
200 | tooltipContainer: () => {
201 | return (
202 |
203 |
204 |
205 | );
206 | },
207 | tooltipSample: () => {
208 | return (
209 |
210 | Sample
211 |
212 | );
213 | },
214 | },
215 | {
216 | name: "fadeOutDown",
217 | category: "fade",
218 | css: `
219 | .fadeoutdown {
220 | animation: fade-out-down 1s ease-out;
221 | -webkit-animation: fade-out-down 1s ease-out;
222 | }
223 | @keyframes fade-out-down {
224 | from {
225 | opacity: 1;
226 | }
227 | to {
228 | opacity: 0;
229 | }
230 | }
231 | `,
232 | tooltipContainer: () => {
233 | return (
234 |
235 |
236 |
237 | );
238 | },
239 | tooltipSample: () => {
240 | return (
241 |
242 | Sample
243 |
244 | );
245 | },
246 | },
247 | {
248 | name: "fadeOutLeft",
249 | category: "fade",
250 | css: `
251 | .fadeoutleft {
252 | animation: fade-out-left 1s ease-out;
253 | -webkit-animation: fade-out-left 1s ease-out;
254 | }
255 | @keyframes fade-out-left {
256 | from {
257 | opacity: 1;
258 | }
259 | to {
260 | opacity: 0;
261 | }}
262 | `,
263 | tooltipContainer: () => {
264 | return (
265 |
266 |
267 |
268 | );
269 | },
270 | tooltipSample: () => {
271 | return (
272 |
273 | Sample
274 |
275 | );
276 | },
277 | },
278 | {
279 | name: "fadeOutRight",
280 | category: "fade",
281 | css: `
282 | .fadeoutright {
283 | animation: fade-out-right 1s ease-out;
284 | -webkit-animation: fade-out-right 1s ease-out;
285 | }
286 | @keyframes fade-out-right {
287 | from {
288 | opacity: 1;
289 | }
290 | to {
291 | opacity: 0;
292 | }}
293 | `,
294 | tooltipContainer: () => {
295 | return (
296 |
297 |
298 |
299 | );
300 | },
301 | tooltipSample: () => {
302 | return (
303 |
304 | Sample
305 |
306 | );
307 | },
308 | },
309 | {
310 | name: "fadeOutUp",
311 | category: "fade",
312 | css: `
313 | .fadeoutup {
314 | animation: fade-out-up 1s ease-out;
315 | -webkit-animation: fade-out-up 1s ease-out;
316 | }
317 | @keyframes fade-out-up {
318 | from {
319 | opacity: 1;
320 | }
321 | to {
322 | opacity: 0;
323 | }}
324 | `,
325 | tooltipContainer: () => {
326 | return (
327 |
328 |
329 |
330 | );
331 | },
332 | tooltipSample: () => {
333 | return (
334 |
335 | Sample
336 |
337 | );
338 | },
339 | },
340 | {
341 | name: "slideInDown",
342 | category: "slide",
343 | css: `
344 | .slideindown {
345 | animation: slide-in-down 1s ease-out;
346 | -webkit-animation: slide-in-down 1s ease-out;
347 | }
348 | @keyframes slide-in-down {
349 | from {
350 | opacity: 0;
351 | transform: translate3d(0, -100%, 0);
352 | }
353 | to {
354 | opacity: 1;
355 | transform: translate3d(0, 0, 0);
356 | }
357 | }
358 | `,
359 | tooltipContainer: () => {
360 | return (
361 |
362 |
363 |
364 | );
365 | },
366 | tooltipSample: () => {
367 | return (
368 |
369 | Sample
370 |
371 | );
372 | },
373 | },
374 | {
375 | name: "slideInLeft",
376 | category: "slide",
377 | css: `
378 | .slideinleft {
379 | animation: slide-in-left 1s ease-out;
380 | -webkit-animation: slide-in-left 1s ease-out;
381 | }
382 | @keyframes slide-in-left {
383 | from {
384 | opacity: 0;
385 | transform: translate3d(-100%, 0, 0);
386 | }
387 | to {
388 | opacity: 1;
389 | transform: translate3d(0, 0, 0);
390 | }
391 | }
392 | `,
393 | tooltipContainer: () => {
394 | return (
395 |
396 |
397 |
398 | );
399 | },
400 | tooltipSample: () => {
401 | return (
402 |
403 | Sample
404 |
405 | );
406 | },
407 | },
408 | {
409 | name: "slideInRight",
410 | category: "slide",
411 | css: `
412 | .slideinright {
413 | animation: slide-in-right 1s ease-out;
414 | -webkit-animation: slide-in-right 1s ease-out;
415 | }
416 | @keyframes slide-in-right {
417 | from {
418 | opacity: 0;
419 | transform: translate3d(100%, 0, 0);
420 | }
421 | to {
422 | opacity: 1;
423 | transform: translate3d(0, 0, 0);
424 | }
425 | }
426 | `,
427 | tooltipContainer: () => {
428 | return (
429 |
430 |
431 |
432 | );
433 | },
434 | tooltipSample: () => {
435 | return (
436 |
437 | Sample
438 |
439 | );
440 | },
441 | },
442 | {
443 | name: "slideInUp",
444 | category: "slide",
445 | css: `
446 | .slideinup {
447 | animation: slide-in-up 1s ease-out;
448 | -webkit-animation: slide-in-up 1s ease-out;
449 | }
450 | @keyframes slide-in-up {
451 | from {
452 | opacity: 0;
453 | transform: translate3d(0, 100%, 0);
454 | }
455 | to {
456 | opacity: 1;
457 | transform: translate3d(0, 0, 0);
458 | }
459 | }
460 | `,
461 | tooltipContainer: () => {
462 | return (
463 |
464 |
465 |
466 | );
467 | },
468 | tooltipSample: () => {
469 | return (
470 |
471 | Sample
472 |
473 | );
474 | },
475 | },
476 | {
477 | name: "slideOutDown",
478 | category: "slide",
479 | css: `
480 | .slideoutdown {
481 | animation: slide-out-down 1s ease-out;
482 | -webkit-animation: slide-out-down 1s ease-out;
483 | }
484 | @keyframes slide-out-down {
485 | from {
486 | opacity: 1;
487 | transform: translate3d(0, 0, 0);
488 | }
489 | to {
490 | opacity: 0;
491 | transform: translate3d(0, 100%, 0);
492 | }
493 | }
494 | `,
495 | tooltipContainer: () => {
496 | return (
497 |
498 |
499 |
500 | );
501 | },
502 | tooltipSample: () => {
503 | return (
504 |
505 | Sample
506 |
507 | );
508 | },
509 | },
510 | {
511 | name: "slideOutLeft",
512 | category: "slide",
513 | css: `
514 | .slideoutleft {
515 | animation: slide-out-left 1s ease-out;
516 | -webkit-animation: slide-out-left 1s ease-out;
517 | }
518 | @keyframes slide-out-left {
519 | from {
520 | opacity: 1;
521 | transform: translate3d(0, 0, 0);
522 | }
523 | to {
524 | opacity: 0;
525 | transform: translate3d(-100%, 0, 0);
526 | }
527 | }
528 | `,
529 | tooltipContainer: () => {
530 | return (
531 |
532 |
533 |
534 | );
535 | },
536 | tooltipSample: () => {
537 | return (
538 |
539 | Sample
540 |
541 | );
542 | },
543 | },
544 | {
545 | name: "slideOutRight",
546 | category: "slide",
547 | css: `
548 | .slideoutright {
549 | animation: slide-out-right 1s ease-out;
550 | -webkit-animation: slide-out-right 1s ease-out;
551 | }
552 | @keyframes slide-out-right {
553 | from {
554 | opacity: 1;
555 | transform: translate3d(0, 0, 0);
556 | }
557 | to {
558 | opacity: 0;
559 | transform: translate3d(100%, 0, 0);
560 | }
561 | }
562 | `,
563 | tooltipContainer: () => {
564 | return (
565 |
566 |
567 |
568 | );
569 | },
570 | tooltipSample: () => {
571 | return (
572 |
573 | Sample
574 |
575 | );
576 | },
577 | },
578 | {
579 | name: "slideOutUp",
580 | category: "slide",
581 | css: `
582 | .slideoutup {
583 | animation: slide-out-up 1s ease-out;
584 | -webkit-animation: slide-out-up 1s ease-out;
585 | }
586 | @keyframes slide-out-up {
587 | from {
588 | opacity: 1;
589 | transform: translate3d(0, 0, 0);
590 | }
591 | to {
592 | opacity: 0;
593 | transform: translate3d(0, -100%, 0);
594 | }
595 | }
596 | `,
597 | tooltipContainer: () => {
598 | return (
599 |
600 |
601 |
602 | );
603 | },
604 | tooltipSample: () => {
605 | return (
606 |
607 | Sample
608 |
609 | );
610 | },
611 | },
612 | {
613 | name: "slideDown",
614 | category: "slide",
615 | css: `
616 | .slidedown {
617 | animation: slide-down 1s ease-out;
618 | -webkit-animation: slide-down 1s ease-out;
619 | }
620 | @keyframes slide-down {
621 | from {
622 | opacity: 0;
623 | transform: translate3d(0, -100%, 0);
624 | }
625 | to {
626 | opacity: 1;
627 | transform: translate3d(0, 0, 0);
628 | }
629 | }
630 | `,
631 | tooltipContainer: () => {
632 | return (
633 |
634 |
635 |
636 | );
637 | },
638 | tooltipSample: () => {
639 | return (
640 |
641 | Sample
642 |
643 | );
644 | },
645 | },
646 | {
647 | name: "slideLeft",
648 | category: "slide",
649 | css: `
650 | .slideleft {
651 | animation: slide-left 1s ease-out;
652 | -webkit-animation: slide-left 1s ease-out;
653 | }
654 | @keyframes slide-left {
655 | from {
656 | opacity: 0;
657 | transform: translate3d(-100%, 0, 0);
658 | }
659 | to {
660 | opacity: 1;
661 | transform: translate3d(0, 0, 0);
662 | }
663 | }
664 | `,
665 | tooltipContainer: () => {
666 | return (
667 |
668 |
669 |
670 | );
671 | },
672 | tooltipSample: () => {
673 | return (
674 |
675 | Sample
676 |
677 | );
678 | },
679 | },
680 | {
681 | name: "slideRight",
682 | category: "slide",
683 | css: `
684 | .slideright {
685 | animation: slide-right 1s ease-out;
686 | -webkit-animation: slide-right 1s ease-out;
687 | }
688 | @keyframes slide-right {
689 | from {
690 | opacity: 0;
691 | transform: translate3d(100%, 0, 0);
692 | }
693 | to {
694 | opacity: 1;
695 | transform: translate3d(0, 0, 0);
696 | }
697 | }
698 | `,
699 | tooltipContainer: () => {
700 | return (
701 |
702 |
703 |
704 | );
705 | },
706 | tooltipSample: () => {
707 | return (
708 |
709 | Sample
710 |
711 | );
712 | },
713 | },
714 | {
715 | name: "slideUp",
716 | category: "slide",
717 | css: `
718 | .slideup {
719 | animation: slide-up 1s ease-out;
720 | -webkit-animation: slide-up 1s ease-out;
721 | }
722 | @keyframes slide-up {
723 | from {
724 | opacity: 0;
725 | transform: translate3d(0, 100%, 0);
726 | }
727 | to {
728 | opacity: 1;
729 | transform: translate3d(0, 0, 0);
730 | }
731 | }
732 | `,
733 | tooltipContainer: () => {
734 | return (
735 |
736 |
737 |
738 | );
739 | },
740 | tooltipSample: () => {
741 | return (
742 |
743 | Sample
744 |
745 | );
746 | },
747 | },
748 | {
749 | name: "zoomIn",
750 | category: "zoom",
751 | css: `
752 | .zoomin {
753 | animation: zoom-in 1s ease-out;
754 | -webkit-animation: zoom-in 1s ease-out;
755 | }
756 | @keyframes zoom-in {
757 | from {
758 | opacity: 0;
759 | transform: scale(0);
760 | }
761 | to {
762 | opacity: 1;
763 | transform: scale(1);
764 | }
765 | }
766 | `,
767 | tooltipContainer: () => {
768 | return (
769 |
770 |
771 |
772 | );
773 | },
774 | tooltipSample: () => {
775 | return (
776 |
777 | Sample
778 |
779 | );
780 | },
781 | },
782 | {
783 | name: "zoomOut",
784 | category: "zoom",
785 | css: `
786 | .zoomout {
787 | animation: zoom-out 1s ease-out;
788 | -webkit-animation: zoom-out 1s ease-out;
789 | }
790 | @keyframes zoom-out {
791 | from {
792 | opacity: 1;
793 | transform: scale(1);
794 | }
795 | to {
796 | opacity: 0;
797 | transform: scale(0);
798 | }
799 | }
800 | `,
801 | tooltipContainer: () => {
802 | return (
803 |
804 |
805 |
806 | );
807 | },
808 | tooltipSample: () => {
809 | return (
810 |
811 | Sample
812 |
813 | );
814 | },
815 | },
816 | {
817 | name: "Tada",
818 | category: "special",
819 | css: `
820 | .tada {
821 | animation: tada 1s ease-out;
822 | -webkit-animation: tada 1s ease-out;
823 | }
824 | @keyframes tada {
825 | 0% {
826 | transform: scale3d(1, 1, 1);
827 | }
828 | 10%,
829 | 20% {
830 | transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg);
831 | }
832 | 30%,
833 | 50%,
834 | 70%,
835 | 90% {
836 | transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg);
837 | }
838 | 40%,
839 | 60%,
840 | 80% {
841 | transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg);
842 | }
843 | 100% {
844 | transform: scale3d(1, 1, 1);
845 | }
846 | }
847 | `,
848 | tooltipContainer: () => {
849 | return (
850 |
851 |
852 |
853 | );
854 | },
855 | tooltipSample: () => {
856 | return (
857 |
858 | Sample
859 |
860 | );
861 | },
862 | },
863 | {
864 | name: "spinnerGrow",
865 | category: "special",
866 | css: `
867 | .spinnergrow {
868 | animation: spinner-grow 1s ease-out infinite;
869 | -webkit-animation: spinner-grow 1s ease-out infinite;
870 | transform-origin: center;
871 | display: inline-block;
872 | }
873 |
874 | @keyframes spinner-grow {
875 | 0% {
876 | transform: scale(0);
877 | opacity: 0.2;
878 | }
879 | 50% {
880 | transform: scale(1.1);
881 | opacity: 1;
882 | }
883 | 100% {
884 | transform: scale(0.95);
885 | opacity: 0;
886 | }
887 | }
888 | `,
889 | tooltipContainer: () => {
890 | return (
891 |
892 |
893 |
894 | );
895 | },
896 | tooltipSample: () => {
897 | return (
898 |
899 | Sample
900 |
901 | );
902 | },
903 | },
904 | {
905 | name: "spinnerShrink",
906 | category: "special",
907 | css: `
908 | .spinnershrink {
909 | animation: spinner-shrink 1s ease-in-out forwards;
910 | -webkit-animation: spinner-shrink 1s ease-in-out forwards;
911 | transform-origin: center;
912 | display: inline-block;
913 | }
914 |
915 | @keyframes spinner-shrink {
916 | 0% {
917 | transform: scale(1);
918 | opacity: 1;
919 | }
920 | 50% {
921 | transform: scale(0.8);
922 | opacity: 0.7;
923 | }
924 | 100% {
925 | transform: scale(0);
926 | opacity: 0;
927 | }
928 | }
929 | `,
930 | tooltipContainer: () => {
931 | return (
932 |
933 |
934 |
935 | );
936 | },
937 | tooltipSample: () => {
938 | return (
939 |
940 | Sample
941 |
942 | );
943 | },
944 | },
945 | {
946 | name: "fadeInTopLeft",
947 | category: "fade",
948 | css: `
949 | .fadeintopleft {
950 | animation: fade-in-top-left 1s ease-out;
951 | -webkit-animation: fade-in-top-left 1s ease-out;
952 | }
953 | @keyframes fade-in-top-left {
954 | from {
955 | opacity: 0;
956 | transform: translate3d(-100%, -100%, 0);
957 | }
958 | to {
959 | opacity: 1;
960 | transform: translate3d(0, 0, 0);
961 | }
962 | }
963 | `,
964 | tooltipContainer: () => {
965 | return (
966 |
967 |
968 |
969 | );
970 | },
971 | tooltipSample: () => {
972 | return (
973 |
974 | Sample
975 |
976 | );
977 | },
978 | },
979 | {
980 | name: "fadeInTopRight",
981 | category: "fade",
982 | css: `
983 | .fadeintopright {
984 | animation: fade-in-top-right 1s ease-out;
985 | -webkit-animation: fade-in-top-right 1s ease-out;
986 | }
987 | @keyframes fade-in-top-right {
988 | from {
989 | opacity: 0;
990 | transform: translate3d(100%, -100%, 0);
991 | }
992 | to {
993 | opacity: 1;
994 | transform: translate3d(0, 0, 0);
995 | }
996 | }
997 | `,
998 | tooltipContainer: () => {
999 | return (
1000 |
1001 |
1002 |
1003 | );
1004 | },
1005 | tooltipSample: () => {
1006 | return (
1007 |
1008 | Sample
1009 |
1010 | );
1011 | },
1012 | },
1013 | {
1014 | name: "fadeInBottomLeft",
1015 | category: "fade",
1016 | css: `
1017 | .fadeinbottomleft {
1018 | animation: fade-in-bottom-left 1s ease-out;
1019 | -webkit-animation: fade-in-bottom-left 1s ease-out;
1020 | }
1021 | @keyframes fade-in-bottom-left {
1022 | from {
1023 | opacity: 0;
1024 | transform: translate3d(-100%, 100%, 0);
1025 | }
1026 | to {
1027 | opacity: 1;
1028 | transform: translate3d(0, 0, 0);
1029 | }
1030 | }
1031 | `,
1032 | tooltipContainer: () => {
1033 | return (
1034 |
1035 |
1036 |
1037 | );
1038 | },
1039 | tooltipSample: () => {
1040 | return (
1041 |
1042 | Sample
1043 |
1044 | );
1045 | },
1046 | },
1047 | {
1048 | name: "fadeInBottomRight",
1049 | category: "fade",
1050 | css: `
1051 | .fadeinbottomright {
1052 | animation: fade-in-bottom-right 1s ease-out;
1053 | -webkit-animation: fade-in-bottom-right 1s ease-out;
1054 | }
1055 | @keyframes fade-in-bottom-right {
1056 | from {
1057 | opacity: 0;
1058 | transform: translate3d(100%, 100%, 0);
1059 | }
1060 | to {
1061 | opacity: 1;
1062 | transform: translate3d(0, 0, 0);
1063 | }
1064 | }
1065 | `,
1066 | tooltipContainer: () => {
1067 | return (
1068 |
1069 |
1070 |
1071 | );
1072 | },
1073 | tooltipSample: () => {
1074 | return (
1075 |
1076 | Sample
1077 |
1078 | );
1079 | },
1080 | },
1081 | {
1082 | name: "fadeInBounceDown",
1083 | category: "fade",
1084 | css: `
1085 | .fadeinbouncedown {
1086 | animation: fade-in-bounce-down 1s ease-out;
1087 | -webkit-animation: fade-in-bounce-down 1s ease-out;
1088 | }
1089 | @keyframes fade-in-bounce-down {
1090 | from {
1091 | opacity: 0;
1092 | transform: translate3d(0, -100%, 0);
1093 | }
1094 | to {
1095 | opacity: 1;
1096 | transform: translate3d(0, 0, 0);
1097 | }
1098 | }
1099 | `,
1100 | tooltipContainer: () => {
1101 | return (
1102 |
1103 |
1104 |
1105 | );
1106 | },
1107 | tooltipSample: () => {
1108 | return (
1109 |
1110 | Sample
1111 |
1112 | );
1113 | },
1114 | },
1115 | {
1116 | name: "fadeInBounceUp",
1117 | category: "fade",
1118 | css: `
1119 | .fadeinbounceup {
1120 | animation: fade-in-bounce-up 1s ease-out;
1121 | -webkit-animation: fade-in-bounce-up 1s ease-out;
1122 | }
1123 | @keyframes fade-in-bounce-up {
1124 | from {
1125 | opacity: 0;
1126 | transform: translate3d(0, 100%, 0);
1127 | }
1128 | to {
1129 | opacity: 1;
1130 | transform: translate3d(0, 0, 0);
1131 | }
1132 | }
1133 | `,
1134 | tooltipContainer: () => {
1135 | return (
1136 |
1137 |
1138 |
1139 | );
1140 | },
1141 | tooltipSample: () => {
1142 | return (
1143 |
1144 | Sample
1145 |
1146 | );
1147 | },
1148 | },
1149 | {
1150 | name: "fadeInBounceRight",
1151 | category: "fade",
1152 | css: `
1153 | .fadeinbounceright {
1154 | animation: fade-in-bounce-right 1s ease-out;
1155 | -webkit-animation: fade-in-bounce-right 1s ease-out;
1156 | }
1157 | @keyframes fade-in-bounce-right {
1158 | from {
1159 | opacity: 0;
1160 | transform: translate3d(-100%, 0, 0);
1161 | }
1162 | to {
1163 | opacity: 1;
1164 | transform: translate3d(0, 0, 0);
1165 | }
1166 | }
1167 | `,
1168 | description: "Fade in bounce right effect",
1169 | tooltipContainer: () => {
1170 | return (
1171 |
1172 |
1173 |
1174 | );
1175 | },
1176 | tooltipSample: () => {
1177 | return (
1178 |
1179 | Sample
1180 |
1181 | );
1182 | },
1183 | },
1184 | {
1185 | name: "fadeInBounceLeft",
1186 | category: "fade",
1187 | css: `
1188 | .fadeinbounceleft {
1189 | animation: fade-in-bounce-left 1s ease-out;
1190 | -webkit-animation: fade-in-bounce-left 1s ease-out;
1191 | }
1192 | @keyframes fade-in-bounce-left {
1193 | from {
1194 | opacity: 0;
1195 | transform: translate3d(100%, 0, 0);
1196 | }
1197 | to {
1198 | opacity: 1;
1199 | transform: translate3d(0, 0, 0);
1200 | }
1201 | }
1202 | `,
1203 | tooltipContainer: () => {
1204 | return (
1205 |
1206 |
1207 |
1208 | );
1209 | },
1210 | tooltipSample: () => {
1211 | return (
1212 |
1213 | Sample
1214 |
1215 | );
1216 | },
1217 | },
1218 | {
1219 | name: "fadeOutLeft",
1220 | category: "fade",
1221 | css: `
1222 | .fadeoutleft {
1223 | animation: fade-out-left 1s ease-out;
1224 | -webkit-animation: fade-out-left 1s ease-out;
1225 | }
1226 | @keyframes fade-out-left {
1227 | from {
1228 | opacity: 1;
1229 | transform: translate3d(0, 0, 0);
1230 | }
1231 | to {
1232 | opacity: 0;
1233 | transform: translate3d(-100%, 0, 0);
1234 | }
1235 | }
1236 | `,
1237 | tooltipContainer: () => {
1238 | return (
1239 |
1240 |
1241 |
1242 | );
1243 | },
1244 | tooltipSample: () => {
1245 | return (
1246 |
1247 | Sample
1248 |
1249 | );
1250 | },
1251 | },
1252 | {
1253 | name: "fadeOutTopLeft",
1254 | category: "fade",
1255 | css: `
1256 | .fadeouttopleft {
1257 | animation: fade-out-top-left 1s ease-out;
1258 | -webkit-animation: fade-out-top-left 1s ease-out;
1259 | }
1260 | @keyframes fade-out-top-left {
1261 | from {
1262 | opacity: 1;
1263 | transform: translate3d(0, 0, 0);
1264 | }
1265 | to {
1266 | opacity: 0;
1267 | transform: translate3d(-100%, -100%, 0);
1268 | }
1269 | }
1270 | `,
1271 | tooltipContainer: () => {
1272 | return (
1273 |
1274 |
1275 |
1276 | );
1277 | },
1278 | tooltipSample: () => {
1279 | return (
1280 |
1281 | Sample
1282 |
1283 | );
1284 | },
1285 | },
1286 | {
1287 | name: "fadeOutTopRight",
1288 | category: "fade",
1289 | css: `
1290 | .fadeouttopright {
1291 | animation: fade-out-top-right 1s ease-out;
1292 | -webkit-animation: fade-out-top-right 1s ease-out;
1293 | }
1294 | @keyframes fade-out-top-right {
1295 | from {
1296 | opacity: 1;
1297 | transform: translate3d(0, 0, 0);
1298 | }
1299 | to {
1300 | opacity: 0;
1301 | transform: translate3d(100%, -100%, 0);
1302 | }
1303 | }
1304 | `,
1305 | tooltipContainer: () => {
1306 | return (
1307 |
1308 |
1309 |
1310 | );
1311 | },
1312 | tooltipSample: () => {
1313 | return (
1314 |
1315 | Sample
1316 |
1317 | );
1318 | },
1319 | },
1320 | {
1321 | name: "dropIn",
1322 | category: "drop",
1323 | css: `
1324 | .dropin {
1325 | animation: drop-in 1s ease-out;
1326 | -webkit-animation: drop-in 1s ease-out;
1327 | }
1328 | @keyframes drop-in {
1329 | from {
1330 | opacity: 0;
1331 | transform: translate3d(0, 100%, 0);
1332 | }
1333 | to {
1334 | opacity: 1;
1335 | transform: translate3d(0, 0, 0);
1336 | }
1337 | }
1338 | `,
1339 | tooltipContainer: () => {
1340 | return (
1341 |
1342 |
1343 |
1344 | );
1345 | },
1346 | tooltipSample: () => {
1347 | return (
1348 |
1349 | Sample
1350 |
1351 | );
1352 | },
1353 | },
1354 | {
1355 | name: "dropOut",
1356 | category: "drop",
1357 | css: `
1358 | .dropout {
1359 | animation: drop-out 1s ease-out;
1360 | -webkit-animation: drop-out 1s ease-out;
1361 | }
1362 | @keyframes drop-out {
1363 | from {
1364 | opacity: 1;
1365 | transform: translate3d(0, 0, 0);
1366 | }
1367 | to {
1368 | opacity: 0;
1369 | transform: translate3d(0, 100%, 0);
1370 | }
1371 | }
1372 | `,
1373 | tooltipContainer: () => {
1374 | return (
1375 |
1376 |
1377 |
1378 | );
1379 | },
1380 | tooltipSample: () => {
1381 | return (
1382 |
1383 | Sample
1384 |
1385 | );
1386 | },
1387 | },
1388 | {
1389 | name: "flyIn",
1390 | category: "fly",
1391 | css: `
1392 | .flyin {
1393 | animation: fly-in 1s ease-out;
1394 | -webkit-animation: fly-in 1s ease-out;
1395 | }
1396 | @keyframes fly-in {
1397 | from {
1398 | opacity: 0;
1399 | transform: translate3d(-100%, 0, 0);
1400 | }
1401 | to {
1402 | opacity: 1;
1403 | transform: translate3d(0, 0, 0);
1404 | }
1405 | }
1406 | `,
1407 | tooltipContainer: () => {
1408 | return (
1409 |
1410 |
1411 |
1412 | );
1413 | },
1414 | tooltipSample: () => {
1415 | return (
1416 |
1417 | Sample
1418 |
1419 | );
1420 | },
1421 | },
1422 | {
1423 | name: "flyInUp",
1424 | category: "fly",
1425 | css: `
1426 | .flyinup {
1427 | animation: fly-in-up 1s ease-out;
1428 | -webkit-animation: fly-in-up 1s ease-out;
1429 | }
1430 | @keyframes fly-in-up {
1431 | from {
1432 | opacity: 0;
1433 | transform: translate3d(0, -100%, 0);
1434 | }
1435 | to {
1436 | opacity: 1;
1437 | transform: translate3d(0, 0, 0);
1438 | }
1439 | }
1440 | `,
1441 | tooltipContainer: () => {
1442 | return (
1443 |
1444 |
1445 |
1446 | );
1447 | },
1448 | tooltipSample: () => {
1449 | return (
1450 |
1451 | Sample
1452 |
1453 | );
1454 | },
1455 | },
1456 | {
1457 | name: "flyInDown",
1458 | category: "fly",
1459 | css: `
1460 | .flyindown {
1461 | animation: fly-in-down 1s ease-out;
1462 | -webkit-animation: fly-in-down 1s ease-out;
1463 | }
1464 | @keyframes fly-in-down {
1465 | from {
1466 | opacity: 0;
1467 | transform: translate3d(0, 100%, 0);
1468 | }
1469 | to {
1470 | opacity: 1;
1471 | transform: translate3d(0, 0, 0);
1472 | }
1473 | }
1474 | `,
1475 | tooltipContainer: () => {
1476 | return (
1477 |
1478 |
1479 |
1480 | );
1481 | },
1482 | tooltipSample: () => {
1483 | return (
1484 |
1485 | Sample
1486 |
1487 | );
1488 | },
1489 | },
1490 | {
1491 | name: "flyInLeft",
1492 | category: "fly",
1493 | css: `
1494 | .flyinleft {
1495 | animation: fly-in-left 1s ease-out;
1496 | -webkit-animation: fly-in-left 1s ease-out;
1497 | }
1498 | @keyframes fly-in-left {
1499 | from {
1500 | opacity: 0;
1501 | transform: translate3d(100%, 0, 0);
1502 | }
1503 | to {
1504 | opacity: 1;
1505 | transform: translate3d(0, 0, 0);
1506 | }
1507 | }
1508 | `,
1509 | description: "Fly in left effect",
1510 | tooltipContainer: () => {
1511 | return (
1512 |
1513 |
1514 |
1515 | );
1516 | },
1517 | tooltipSample: () => {
1518 | return (
1519 |
1520 | Sample
1521 |
1522 | );
1523 | },
1524 | },
1525 | {
1526 | name: "flyInRight",
1527 | category: "fly",
1528 | css: `
1529 | .flyinright {
1530 | animation: fly-in-right 1s ease-out;
1531 | -webkit-animation: fly-in-right 1s ease-out;
1532 | }
1533 | @keyframes fly-in-right {
1534 | from {
1535 | opacity: 0;
1536 | transform: translate3d(-100%, 0, 0);
1537 | }
1538 | to {
1539 | opacity: 1;
1540 | transform: translate3d(0, 0, 0);
1541 | }
1542 | }
1543 | `,
1544 | tooltipContainer: () => {
1545 | return (
1546 |
1547 |
1548 |
1549 | );
1550 | },
1551 | tooltipSample: () => {
1552 | return (
1553 |
1554 | Sample
1555 |
1556 | );
1557 | },
1558 | },
1559 | {
1560 | name: "flyOut",
1561 | category: "fly",
1562 | css: `
1563 | .flyout {
1564 | animation: fly-out 1s ease-out;
1565 | -webkit-animation: fly-out 1s ease-out;
1566 | }
1567 | @keyframes fly-out {
1568 | from {
1569 | opacity: 1;
1570 | transform: translate3d(0, 0, 0);
1571 | }
1572 | to {
1573 | opacity: 0;
1574 | transform: translate3d(0, 100%, 0);
1575 | }
1576 | }
1577 | `,
1578 | tooltipContainer: () => {
1579 | return (
1580 |
1581 |
1582 |
1583 | );
1584 | },
1585 | tooltipSample: () => {
1586 | return (
1587 |
1588 | Sample
1589 |
1590 | );
1591 | },
1592 | },
1593 | {
1594 | name: "flyOutUp",
1595 | category: "fly",
1596 | css: `
1597 | .flyoutup {
1598 | animation: fly-out-up 1s ease-out;
1599 | -webkit-animation: fly-out-up 1s ease-out;
1600 | }
1601 | @keyframes fly-out-up {
1602 | from {
1603 | opacity: 1;
1604 | transform: translate3d(0, 0, 0);
1605 | }
1606 | to {
1607 | opacity: 0;
1608 | transform: translate3d(0, -100%, 0);
1609 | }
1610 | }
1611 | `,
1612 | tooltipContainer: () => {
1613 | return (
1614 |
1615 |
1616 |
1617 | );
1618 | },
1619 | tooltipSample: () => {
1620 | return (
1621 |
1622 | Sample
1623 |
1624 | );
1625 | },
1626 | },
1627 | {
1628 | name: "flyOutDown",
1629 | category: "fly",
1630 | css: `
1631 | .flyoutdown {
1632 | animation: fly-out-down 1s ease-out;
1633 | -webkit-animation: fly-out-down 1s ease-out;
1634 | }
1635 | @keyframes fly-out-down {
1636 | from {
1637 | opacity: 1;
1638 | transform: translate3d(0, 0, 0);
1639 | }
1640 | to {
1641 | opacity: 0;
1642 | transform: translate3d(0, 100%, 0);
1643 | }
1644 | }
1645 | `,
1646 | tooltipContainer: () => {
1647 | return (
1648 |
1649 |
1650 |
1651 | );
1652 | },
1653 | tooltipSample: () => {
1654 | return (
1655 |
1656 | Sample
1657 |
1658 | );
1659 | },
1660 | },
1661 | {
1662 | name: "flyOutLeft",
1663 | category: "fly",
1664 | css: `
1665 | .flyoutleft {
1666 | animation: fly-out-left 1s ease-out;
1667 | -webkit-animation: fly-out-left 1s ease-out;
1668 | }
1669 | @keyframes fly-out-left {
1670 | from {
1671 | opacity: 1;
1672 | transform: translate3d(0, 0, 0);
1673 | }
1674 | to {
1675 | opacity: 0;
1676 | transform: translate3d(-100%, 0, 0);
1677 | }
1678 | }
1679 | `,
1680 | tooltipContainer: () => {
1681 | return (
1682 |
1683 |
1684 |
1685 | );
1686 | },
1687 | tooltipSample: () => {
1688 | return (
1689 |
1690 | Sample
1691 |
1692 | );
1693 | },
1694 | },
1695 | {
1696 | name: "flyOutRight",
1697 | category: "fly",
1698 | css: `
1699 | .flyoutright {
1700 | animation: fly-out-right 1s ease-out;
1701 | -webkit-animation: fly-out-right 1s ease-out;
1702 | }
1703 | @keyframes fly-out-right {
1704 | from {
1705 | opacity: 1;
1706 | transform: translate3d(0, 0, 0);
1707 | }
1708 | to {
1709 | opacity: 0;
1710 | transform: translate3d(100%, 0, 0);
1711 | }
1712 | }
1713 | `,
1714 | tooltipContainer: () => {
1715 | return (
1716 |
1717 |
1718 |
1719 | );
1720 | },
1721 | tooltipSample: () => {
1722 | return (
1723 |
1724 | Sample
1725 |
1726 | );
1727 | },
1728 | },
1729 | {
1730 | name: "browseIn",
1731 | category: "browse",
1732 | css: `
1733 | .browsein {
1734 | animation: browse-in 1s ease-out;
1735 | -webkit-animation: browse-in 1s ease-out;
1736 | }
1737 | @keyframes browse-in {
1738 | from {
1739 | opacity: 0;
1740 | }
1741 | to {
1742 | opacity: 1;
1743 | }
1744 | }
1745 | `,
1746 | tooltipContainer: () => {
1747 | return (
1748 |
1749 |
1750 |
1751 | );
1752 | },
1753 | tooltipSample: () => {
1754 | return (
1755 |
1756 | Sample
1757 |
1758 | );
1759 | },
1760 | },
1761 | {
1762 | name: "browseOut",
1763 | category: "browse",
1764 | css: `
1765 | .browseout {
1766 | animation: browse-out 1s ease-out;
1767 | -webkit-animation: browse-out 1s ease-out;
1768 | }
1769 | @keyframes browse-out {
1770 | from {
1771 | opacity: 1;
1772 | }
1773 | to {
1774 | opacity: 0;
1775 | }
1776 | }
1777 | `,
1778 | tooltipContainer: () => {
1779 | return (
1780 |
1781 |
1782 |
1783 | );
1784 | },
1785 | tooltipSample: () => {
1786 | return (
1787 |
1788 | Sample
1789 |
1790 | );
1791 | },
1792 | },
1793 | {
1794 | name: "browseOutLeft",
1795 | category: "browse",
1796 | css: `
1797 | .browseoutleft {
1798 | animation: browse-out-left 1s ease-out;
1799 | -webkit-animation: browse-out-left 1s ease-out;
1800 | }
1801 | @keyframes browse-out-left {
1802 | from {
1803 | opacity: 1;
1804 | }
1805 | to {
1806 | opacity: 0;
1807 | }
1808 | }
1809 | `,
1810 | tooltipContainer: () => {
1811 | return (
1812 |
1813 |
1814 |
1815 | );
1816 | },
1817 | tooltipSample: () => {
1818 | return (
1819 |
1820 | Sample
1821 |
1822 | );
1823 | },
1824 | },
1825 | {
1826 | name: "browseOutRight",
1827 | category: "browse",
1828 | css: `
1829 | .browseoutright {
1830 | animation: browse-out-right 1s ease-out;
1831 | -webkit-animation: browse-out-right 1s ease-out;
1832 | }
1833 | @keyframes browse-out-right {
1834 | from {
1835 | opacity: 1;
1836 | }
1837 | to {
1838 | opacity: 0;
1839 | }
1840 | }
1841 | `,
1842 | tooltipContainer: () => {
1843 | return (
1844 |
1845 |
1846 |
1847 | );
1848 | },
1849 | tooltipSample: () => {
1850 | return (
1851 |
1852 | Sample
1853 |
1854 | );
1855 | },
1856 | },
1857 | {
1858 | name: "jiggle",
1859 | category: "special",
1860 | css: `
1861 | .jiggle {
1862 | animation: jiggle 1s ease-out;
1863 | -webkit-animation: jiggle 1s ease-out;
1864 | }
1865 | @keyframes jiggle {
1866 | 0% {
1867 | transform: scale3d(1, 1, 1);
1868 | }
1869 | 30% {
1870 | transform: scale3d(1.25, 0.75, 1);
1871 | }
1872 | 40% {
1873 | transform: scale3d(0.75, 1.25, 1);
1874 | }
1875 | 50% {
1876 | transform: scale3d(1.15, 0.85, 1);
1877 | }
1878 | 65% {
1879 | transform: scale3d(0.95, 1.05, 1);
1880 | }
1881 | 75% {
1882 | transform: scale3d(1.05, 0.95, 1);
1883 | }
1884 | 100% {
1885 | transform: scale3d(1, 1, 1);
1886 | }
1887 | }
1888 | `,
1889 | tooltipContainer: () => {
1890 | return (
1891 |
1892 |
1893 |
1894 | );
1895 | },
1896 | tooltipSample: () => {
1897 | return (
1898 |
1899 | Sample
1900 |
1901 | );
1902 | },
1903 | },
1904 | {
1905 | name: "flash",
1906 | category: "special",
1907 | css: `
1908 | .flash {
1909 | animation: flash 1s ease-out;
1910 | -webkit-animation: flash 1s ease-out;
1911 | }
1912 | @keyframes flash {
1913 | 0%,
1914 | 50%,
1915 | 100% {
1916 | opacity: 1;
1917 | }
1918 | 25%,
1919 | 75% {
1920 | opacity: 0;
1921 | }
1922 | }
1923 | `,
1924 | tooltipContainer: () => {
1925 | return (
1926 |
1927 |
1928 |
1929 | );
1930 | },
1931 | tooltipSample: () => {
1932 | return (
1933 |
1934 | Sample
1935 |
1936 | );
1937 | },
1938 | },
1939 | {
1940 | name: "shake",
1941 | category: "special",
1942 | css: `
1943 | .shake {
1944 | animation: shake 1s ease-out;
1945 | -webkit-animation: shake 1s ease-out;
1946 | }
1947 | @keyframes shake {
1948 | 0%,
1949 | 100% {
1950 | transform: translateX(0);
1951 | }
1952 | 10%,
1953 | 30%,
1954 | 50%,
1955 | 70%,
1956 | 90% {
1957 | transform: translateX(-10px);
1958 | }
1959 | 20%,
1960 | 40%,
1961 | 60%,
1962 | 80% {
1963 | transform: translateX(10px);
1964 | }
1965 | }
1966 | `,
1967 | tooltipContainer: () => {
1968 | return (
1969 |
1970 |
1971 |
1972 | );
1973 | },
1974 | tooltipSample: () => {
1975 | return (
1976 |
1977 | Sample
1978 |
1979 | );
1980 | },
1981 | },
1982 | {
1983 | name: "glow",
1984 | category: "special",
1985 | css: `
1986 | .glow {
1987 | animation: glow 1s ease-out;
1988 | -webkit-animation: glow 1s ease-out;
1989 | }
1990 | @keyframes glow {
1991 | 0%,
1992 | 100% {
1993 | opacity: 1;
1994 | }
1995 | 50% {
1996 | opacity: 0.5;
1997 | }
1998 | }
1999 | `,
2000 | tooltipContainer: () => {
2001 | return (
2002 |
2003 |
2004 |
2005 | );
2006 | },
2007 | tooltipSample: () => {
2008 | return (
2009 |
2010 | Sample
2011 |
2012 | );
2013 | },
2014 | },
2015 | {
2016 | name: "wiggle",
2017 | category: "special",
2018 | css: `
2019 | .wiggle {
2020 | animation: wiggle 1s ease-out;
2021 | -webkit-animation: wiggle 1s ease-out;
2022 | }
2023 | @keyframes wiggle {
2024 | 0%,
2025 | 100% {
2026 | transform: translateX(0);
2027 | }
2028 | 10%,
2029 | 30%,
2030 | 50%,
2031 | 70%,
2032 | 90% {
2033 | transform: translateX(-10px);
2034 | }
2035 | 20%,
2036 | 40%,
2037 | 60%,
2038 | 80% {
2039 | transform: translateX(10px);
2040 | }
2041 | }
2042 | `,
2043 | tooltipContainer: () => {
2044 | return (
2045 |
2046 |
2047 |
2048 | );
2049 | },
2050 | tooltipSample: () => {
2051 | return (
2052 |
2053 | Sample
2054 |
2055 | );
2056 | },
2057 | },
2058 | {
2059 | name: "bounceBlurIn",
2060 | badge: "New",
2061 | category: "special",
2062 | css: `
2063 | .bounceblurin {
2064 | animation: bounce-blur-in 0.5s cubic-bezier(0.4, 0, 0.2, 1) both;
2065 | }
2066 |
2067 | @keyframes bounce-blur-in {
2068 | 0% {
2069 | transform: scale(0.8);
2070 | opacity: 0;
2071 | filter: blur(4px);
2072 | }
2073 | 60% {
2074 | transform: scale(1.05);
2075 | opacity: 1;
2076 | filter: blur(0);
2077 | }
2078 | 100% {
2079 | transform: scale(1);
2080 | }
2081 | }
2082 | `,
2083 | tooltipContainer: () => {
2084 | return (
2085 |
2086 |
2087 |
2088 | );
2089 | },
2090 | tooltipSample: () => {
2091 | return (
2092 |
2093 | Sample
2094 |
2095 | );
2096 | },
2097 | },
2098 | {
2099 | name: "floatFadeIn",
2100 | badge: "New",
2101 | category: "special",
2102 | css: `
2103 | .floatfadein {
2104 | animation: float-fade-in 0.5s ease-out both;
2105 | }
2106 | @keyframes float-fade-in {
2107 | 0% {
2108 | opacity: 0;
2109 | transform: translateY(10px);
2110 | }
2111 | 100% {
2112 | opacity: 1;
2113 | transform: translateY(0);
2114 | }
2115 | }
2116 | `,
2117 | tooltipContainer: () => {
2118 | return (
2119 |
2120 |
2121 |
2122 | );
2123 | },
2124 | tooltipSample: () => {
2125 | return (
2126 |
2127 | Sample
2128 |
2129 | );
2130 | },
2131 | },
2132 | {
2133 | name: "scalePunchIn",
2134 | badge: "New",
2135 | category: "special",
2136 | css: `
2137 | .scalepunchin {
2138 | animation: scale-punch-in 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) both;
2139 | }
2140 | @keyframes scale-punch-in {
2141 | 0% {
2142 | opacity: 0;
2143 | transform: scale(0.5);
2144 | }
2145 | 60% {
2146 | opacity: 1;
2147 | transform: scale(1.2);
2148 | }
2149 | 100% {
2150 | transform: scale(1);
2151 | }
2152 | }
2153 | `,
2154 | tooltipContainer: () => {
2155 | return (
2156 |
2157 |
2158 |
2159 | );
2160 | },
2161 | tooltipSample: () => {
2162 | return (
2163 |
2164 | Sample
2165 |
2166 | );
2167 | },
2168 | },
2169 | {
2170 | name: "flipPopIn",
2171 | badge: "New",
2172 | category: "special",
2173 | css: `
2174 | .flippopin {
2175 | animation: flip-pop-in 0.6s ease-out both;
2176 | transform-style: preserve-3d;
2177 | }
2178 | @keyframes flip-pop-in {
2179 | 0% {
2180 | opacity: 0;
2181 | transform: rotateX(90deg) scale(0.8);
2182 | transform-origin: top;
2183 | }
2184 | 100% {
2185 | opacity: 1;
2186 | transform: rotateX(0deg) scale(1);
2187 | }
2188 | }
2189 | `,
2190 | tooltipContainer: () => {
2191 | return (
2192 |
2193 |
2194 |
2195 | );
2196 | },
2197 | tooltipSample: () => {
2198 | return (
2199 |
2200 | Sample
2201 |
2202 | );
2203 | },
2204 | },
2205 | ];
2206 | export default TooltipContent;
2207 |
--------------------------------------------------------------------------------
/src/components/effects/BottomBlur.jsx:
--------------------------------------------------------------------------------
1 | import "@/styles/gradient-blur.css";
2 |
3 | export default function BottomBlurOut() {
4 | return (
5 |
6 | {Array.from({ length: 5 }).map((_, index) => (
7 |
16 | ))}
17 |
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/effects/TextBlurIn.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { motion } from "framer-motion";
3 |
4 | export const BlurIn = ({ text = "", className = "" }) => {
5 | const container = {
6 | hidden: {},
7 | visible: {
8 | transition: {
9 | staggerChildren: 0.05,
10 | },
11 | },
12 | };
13 |
14 | const letter = {
15 | hidden: {
16 | opacity: 0,
17 | filter: "blur(10px)",
18 | y: 20,
19 | scale: 0.95,
20 | rotate: -3,
21 | },
22 | visible: {
23 | opacity: 1,
24 | filter: "blur(0px)",
25 | y: 0,
26 | scale: 1,
27 | rotate: 0,
28 | transition: {
29 | duration: 0.8,
30 | ease: [0.22, 1, 0.36, 1],
31 | },
32 | },
33 | };
34 |
35 | return (
36 |
42 | {text.split("").map((char, i) => (
43 |
44 | {char === " " ? "\u00A0" : char}
45 |
46 | ))}
47 |
48 | );
49 | };
50 |
--------------------------------------------------------------------------------
/src/components/effects/TextScroll.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import React, { useRef } from "react";
4 | import { useScroll, motion, useTransform } from "framer-motion";
5 |
6 | export default function Character({ value }) {
7 | const element = useRef(null);
8 | const { scrollYProgress } = useScroll({
9 | target: element,
10 | offset: ["start 0.9", "start 0.25"],
11 | });
12 |
13 | const words = value.split(" ");
14 |
15 | return (
16 |
20 | {words.map((word, i) => {
21 | const start = i / words.length;
22 | const end = start + 1 / words.length;
23 | return (
24 |
25 | {word}
26 |
27 | );
28 | })}
29 |
30 | );
31 | }
32 |
33 | const Word = ({ children, range, progress }) => {
34 | const characters = children.split("");
35 | const amount = range[1] - range[0];
36 | const step = amount / characters.length;
37 | return (
38 |
39 | {characters.map((character, i) => {
40 | const start = range[0] + step * i;
41 | const end = range[0] + step * (i + 1);
42 | return (
43 |
44 | {character}
45 |
46 | );
47 | })}
48 |
49 | );
50 | };
51 |
52 | const Characters = ({ children, range, progress }) => {
53 | const opacity = useTransform(progress, range, [0, 1]);
54 | return (
55 |
56 |
57 | {children}
58 |
59 | {children}
60 |
61 | );
62 | };
63 |
--------------------------------------------------------------------------------
/src/components/icons/Favicon.jsx:
--------------------------------------------------------------------------------
1 | export default function Favicon() {
2 | return (
3 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/layouts/Content.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import React, { useRef, useState } from "react";
4 | import TooltipContent from "@/components/common/TooltipList";
5 | import BottomBlurOut from "@/components/effects/BottomBlur";
6 | import useInjectAnimations from "@/hooks/useInjectAnimations";
7 | import Code from "@/components/snippets/CodeViewer";
8 | import useClickOutside from "@/hooks/useClickOutside";
9 | import ButtonCopy from "@/components/common/ButtonCopy";
10 |
11 | import { AnimatePresence, motion } from "framer-motion";
12 |
13 | import { Info, Frame, X, ChevronRight, Terminal } from "lucide-react";
14 |
15 | export default function Content() {
16 | useInjectAnimations(TooltipContent);
17 |
18 | const [showCode, setShowCode] = useState(
19 | new Array(TooltipContent.length).fill(false),
20 | );
21 | const [isOpenTooltip, setIsOpenTooltip] = useState(
22 | new Array(TooltipContent.length).fill(false),
23 | );
24 | const [copied, setCopied] = useState(
25 | new Array(TooltipContent.length).fill(false),
26 | );
27 | const [openModals, setOpenModals] = useState({});
28 | const modalRef = useRef(null);
29 |
30 | const handleOpenModal = (index) => {
31 | setOpenModals((prevOpenModals) => ({ ...prevOpenModals, [index]: true }));
32 | };
33 |
34 | const handleCloseModal = (index) => {
35 | setOpenModals((prevOpenModals) => ({ ...prevOpenModals, [index]: false }));
36 | };
37 |
38 | const handleCopy = (index) => {
39 | navigator.clipboard.writeText(TooltipContent[index].css);
40 | setCopied((prevCopied) => {
41 | const newCopied = [...prevCopied];
42 | newCopied[index] = true;
43 | return newCopied;
44 | });
45 | };
46 |
47 | const groupTooltips = TooltipContent.reduce((acc, tooltip) => {
48 | const { category } = tooltip;
49 | if (!acc[category]) acc[category] = [];
50 | acc[category].push(tooltip);
51 | return acc;
52 | }, {});
53 |
54 | useClickOutside(modalRef, () => {
55 | setOpenModals({});
56 | });
57 |
58 | const modalVariants = {
59 | hidden: {
60 | opacity: 0,
61 | y: 100,
62 | rotateX: 90,
63 | transformOrigin: "bottom center",
64 | },
65 | visible: {
66 | opacity: 1,
67 | y: 0,
68 | rotateX: 0,
69 | transition: {
70 | duration: 0.6,
71 | ease: [0.16, 1, 0.3, 1],
72 | },
73 | },
74 | exit: {
75 | opacity: 0,
76 | y: 100,
77 | rotateX: 90,
78 | transition: {
79 | duration: 0.4,
80 | ease: [0.4, 0, 0.2, 1],
81 | },
82 | },
83 | };
84 |
85 | return (
86 |
87 |
88 |
89 |
90 | {Object.entries(groupTooltips).map(([category, tooltips]) => (
91 |
92 |
93 |
94 |
95 | {category}
96 |
97 |
98 | {tooltips.length}
99 |
100 |
101 |
102 | {tooltips.map((tooltip, index) => {
103 | const globalIndex = TooltipContent.findIndex(
104 | (t) => t.name === tooltip.name,
105 | );
106 | return (
107 |
111 |
116 |
117 |
118 |
119 | {tooltip.name}
120 |
121 | {tooltip.badge && (
122 |
123 | {tooltip.badge}
124 |
125 | )}
126 |
136 |
{
139 | const newIsOpenTooltip = [...isOpenTooltip];
140 | newIsOpenTooltip[globalIndex] = true;
141 | setIsOpenTooltip(newIsOpenTooltip);
142 | }}
143 | onMouseLeave={() => {
144 | const newIsOpenTooltip = [...isOpenTooltip];
145 | newIsOpenTooltip[globalIndex] = false;
146 | setIsOpenTooltip(newIsOpenTooltip);
147 | }}
148 | >
149 | {tooltip.tooltipContainer && tooltip.tooltipContainer()}
150 | {isOpenTooltip[globalIndex] &&
151 | tooltip.tooltipSample &&
152 | tooltip.tooltipSample()}
153 |
154 |
155 |
156 | {openModals[globalIndex] && (
157 |
164 |
172 |
179 |
180 |
185 |
186 | handleCopy(globalIndex)}
188 | />
189 |
190 |
191 |
192 |
193 |
194 |
195 | Adjust the time according to your app's
196 | requirements
197 |
198 |
199 |
200 |
201 | )}
202 |
203 |
204 | );
205 | })}
206 |
207 | ))}
208 |
209 |
210 | );
211 | }
212 |
--------------------------------------------------------------------------------
/src/components/layouts/Footer.jsx:
--------------------------------------------------------------------------------
1 | import { Github } from "lucide-react";
2 |
3 | import Link from "next/link";
4 |
5 | export default function Footer() {
6 | return (
7 |
42 | );
43 | }
44 |
--------------------------------------------------------------------------------
/src/components/layouts/Header.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React from "react";
3 | import Image from "next/image";
4 | import Switch from "@/components/common/ButtonSwitch";
5 | import { motion } from "framer-motion";
6 | import Favicon from "../icons/Favicon";
7 |
8 | export default function Header() {
9 | return (
10 |
11 |
12 |
13 |
19 |
20 | Popply Library
21 |
22 |
23 |
29 |
30 |
31 |
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/src/components/layouts/Hero.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import BottomBlurOut from "@/components/effects/BottomBlur";
4 | import Character from "@/components/effects/TextScroll";
5 | import { BlurIn } from "../effects/TextBlurIn";
6 | import { motion } from "framer-motion";
7 |
8 | export default function Hero() {
9 | return (
10 |
11 |
17 |
23 |
29 |
30 |
31 |
32 |
33 | Eleveate Your
34 |
35 |
36 |
37 | Tooltip's
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
53 |
58 |
59 |
60 |
66 |
67 |
73 |
74 | );
75 | }
76 |
--------------------------------------------------------------------------------
/src/components/snippets/CodeViewer.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
4 | import { materialDark } from "react-syntax-highlighter/dist/esm/styles/prism";
5 | import { useEffect, useState } from "react";
6 |
7 | export default function Code({ code, language = "css", filename, children }) {
8 | const [mounted, setMounted] = useState(false);
9 |
10 | useEffect(() => {
11 | setMounted(true);
12 | }, []);
13 |
14 | if (!mounted) return null;
15 |
16 | return (
17 |
18 |
19 | {filename && (
20 |
21 | {filename}
22 |
23 | )}
24 |
25 |
26 |
27 | {children}
28 |
42 | {code}
43 |
44 |
45 |
46 |
47 |
48 | );
49 | }
50 |
--------------------------------------------------------------------------------
/src/hooks/useClickOutside.js:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useEffect } from "react";
4 |
5 | export default function useClickOutside(ref, handler) {
6 | useEffect(() => {
7 | const handleClickOutside = (event) => {
8 | if (!ref || !ref.current || ref.current.contains(event.target)) {
9 | return;
10 | }
11 |
12 | handler(event);
13 | };
14 |
15 | document.addEventListener("mousedown", handleClickOutside);
16 | document.addEventListener("touchstart", handleClickOutside);
17 |
18 | return () => {
19 | document.removeEventListener("mousedown", handleClickOutside);
20 | document.removeEventListener("touchstart", handleClickOutside);
21 | };
22 | }, [ref, handler]);
23 | }
24 |
--------------------------------------------------------------------------------
/src/hooks/useInjectAnimations.js:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useEffect } from "react";
4 |
5 | export default function useInjectAnimations(animations) {
6 | useEffect(() => {
7 | if (typeof window === "undefined") return;
8 |
9 | const styleId = "global-animation-styles";
10 |
11 | if (!document.getElementById(styleId)) {
12 | const style = document.createElement("style");
13 | style.id = styleId;
14 | style.innerHTML = animations.map((anim) => anim.css).join("\n\n");
15 | document.head.appendChild(style);
16 | }
17 | }, [animations]);
18 | }
19 |
--------------------------------------------------------------------------------
/src/hooks/useTheme.js:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useState, useEffect } from "react";
4 |
5 | export default function useTheme() {
6 | const [theme, setTheme] = useState(null);
7 |
8 | useEffect(() => {
9 | const savedTheme =
10 | localStorage.getItem("theme") ||
11 | (window.matchMedia("(prefers-color-scheme: dark)").matches
12 | ? "dark"
13 | : "light");
14 | setTheme(savedTheme);
15 | }, [setTheme]);
16 |
17 | useEffect(() => {
18 | if (theme) {
19 | document.documentElement.classList.toggle("dark", theme === "dark");
20 | localStorage.setItem("theme", theme);
21 | }
22 | }, [theme]);
23 |
24 | const toggleTheme = () => {
25 | setTheme((prev) => (prev === "dark" ? "light" : "dark"));
26 | };
27 |
28 | return { theme, toggleTheme };
29 | }
30 |
--------------------------------------------------------------------------------
/src/styles/gradient-blur.css:
--------------------------------------------------------------------------------
1 | .gradient-blur:before {
2 | -webkit-mask: linear-gradient(
3 | 180deg,
4 | transparent 0%,
5 | #000 12.5%,
6 | #000 25%,
7 | transparent 37.5%
8 | );
9 | mask: linear-gradient(
10 | 180deg,
11 | transparent 0%,
12 | #000 12.5%,
13 | #000 25%,
14 | transparent 37.5%
15 | );
16 | }
17 |
18 | .gradient-blur > div:nth-of-type(1) {
19 | -webkit-mask: linear-gradient(
20 | 180deg,
21 | transparent 12.5%,
22 | #000 25%,
23 | #000 37.5%,
24 | transparent 50%
25 | );
26 | mask: linear-gradient(
27 | 180deg,
28 | transparent 12.5%,
29 | #000 25%,
30 | #000 37.5%,
31 | transparent 50%
32 | );
33 | }
34 |
35 | .gradient-blur > div:nth-of-type(2) {
36 | -webkit-mask: linear-gradient(
37 | 180deg,
38 | transparent 25%,
39 | #000 37.5%,
40 | #000 50%,
41 | transparent 62.5%
42 | );
43 | mask: linear-gradient(
44 | 180deg,
45 | transparent 25%,
46 | #000 37.5%,
47 | #000 50%,
48 | transparent 62.5%
49 | );
50 | }
51 |
52 | .gradient-blur > div:nth-of-type(3) {
53 | -webkit-mask: linear-gradient(
54 | 180deg,
55 | transparent 37.5%,
56 | #000 50%,
57 | #000 62.5%,
58 | transparent 75%
59 | );
60 | mask: linear-gradient(
61 | 180deg,
62 | transparent 37.5%,
63 | #000 50%,
64 | #000 62.5%,
65 | transparent 75%
66 | );
67 | }
68 |
69 | .gradient-blur > div:nth-of-type(4) {
70 | -webkit-mask: linear-gradient(
71 | 180deg,
72 | transparent 50%,
73 | #000 62.5%,
74 | #000 75%,
75 | transparent 87.5%
76 | );
77 | mask: linear-gradient(
78 | 180deg,
79 | transparent 50%,
80 | #000 62.5%,
81 | #000 75%,
82 | transparent 87.5%
83 | );
84 | }
85 |
86 | .gradient-blur > div:nth-of-type(5) {
87 | -webkit-mask: linear-gradient(
88 | 180deg,
89 | transparent 62.5%,
90 | #000 75%,
91 | #000 87.5%,
92 | transparent
93 | );
94 | mask: linear-gradient(
95 | 180deg,
96 | transparent 62.5%,
97 | #000 75%,
98 | #000 87.5%,
99 | transparent
100 | );
101 | }
102 |
103 | .gradient-blur:after {
104 | -webkit-mask: linear-gradient(180deg, transparent 75%, #000 87.5%, #000);
105 | mask: linear-gradient(180deg, transparent 75%, #000 87.5%, #000);
106 | }
107 |
--------------------------------------------------------------------------------