├── .eslintrc.json ├── app ├── favicon.ico ├── meeting │ ├── calendly.tsx │ └── page.tsx ├── layout.tsx ├── showcase │ └── page.tsx ├── globals.css └── page.tsx ├── public ├── images │ ├── s_1.png │ ├── s_2.png │ ├── s_3.png │ ├── s_4.png │ ├── s_5.png │ ├── s_6.png │ ├── business.webp │ └── proposal_illustration.png ├── logo │ ├── figma.png │ └── logo.webp ├── icons │ ├── squiggle.svg │ ├── design.svg │ ├── analytics.svg │ ├── receive.svg │ ├── fast.svg │ ├── flexible.svg │ ├── team.svg │ ├── money.svg │ ├── safe.svg │ ├── support.svg │ ├── scalable.svg │ └── star.svg ├── vercel.svg └── next.svg ├── next.config.mjs ├── postcss.config.mjs ├── lib └── utils.ts ├── components ├── demos │ ├── word-pull-up-demo.tsx │ ├── scroll-based-velocity-demo.tsx │ ├── cover-demo.tsx │ ├── animated-shiny-text-demo.tsx │ ├── box-reveal-demo.tsx │ ├── blur-fade-demo.tsx │ ├── shooting-stars-demo.tsx │ └── animated-beam-demo.tsx ├── footer.tsx ├── magicui │ ├── animated-shiny-text.tsx │ ├── word-fade-in.tsx │ ├── number-ticker.tsx │ ├── word-pull-up.tsx │ ├── blur-fade.tsx │ ├── box-reveal.tsx │ ├── scroll-based-velocity.tsx │ └── animated-beam.tsx ├── ui │ ├── lets-make-things-happen.tsx │ ├── infinite-moving-logos.tsx │ ├── stars-background.tsx │ ├── shooting-stars.tsx │ ├── cover.tsx │ └── sparkles.tsx └── showcase-navbar.tsx ├── components.json ├── .gitignore ├── tsconfig.json ├── package.json ├── README.md └── tailwind.config.ts /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iggy-tech/digital-marketing/HEAD/app/favicon.ico -------------------------------------------------------------------------------- /public/images/s_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iggy-tech/digital-marketing/HEAD/public/images/s_1.png -------------------------------------------------------------------------------- /public/images/s_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iggy-tech/digital-marketing/HEAD/public/images/s_2.png -------------------------------------------------------------------------------- /public/images/s_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iggy-tech/digital-marketing/HEAD/public/images/s_3.png -------------------------------------------------------------------------------- /public/images/s_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iggy-tech/digital-marketing/HEAD/public/images/s_4.png -------------------------------------------------------------------------------- /public/images/s_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iggy-tech/digital-marketing/HEAD/public/images/s_5.png -------------------------------------------------------------------------------- /public/images/s_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iggy-tech/digital-marketing/HEAD/public/images/s_6.png -------------------------------------------------------------------------------- /public/logo/figma.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iggy-tech/digital-marketing/HEAD/public/logo/figma.png -------------------------------------------------------------------------------- /public/logo/logo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iggy-tech/digital-marketing/HEAD/public/logo/logo.webp -------------------------------------------------------------------------------- /public/images/business.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iggy-tech/digital-marketing/HEAD/public/images/business.webp -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | export default nextConfig; 5 | -------------------------------------------------------------------------------- /public/images/proposal_illustration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iggy-tech/digital-marketing/HEAD/public/images/proposal_illustration.png -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx" 2 | import { twMerge } from "tailwind-merge" 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /components/demos/word-pull-up-demo.tsx: -------------------------------------------------------------------------------- 1 | import WordPullUp from "@/components/magicui/word-pull-up"; 2 | 3 | export function WordPullUpDemo() { 4 | return ( 5 | 9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /app/meeting/calendly.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { InlineWidget } from "react-calendly"; 4 | 5 | 6 | const Calendly = () => { 7 | 8 | return ( 9 |
10 | 13 |
); 14 | } 15 | 16 | export default Calendly; -------------------------------------------------------------------------------- /public/icons/squiggle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/icons/design.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /components/demos/scroll-based-velocity-demo.tsx: -------------------------------------------------------------------------------- 1 | import { VelocityScroll } from "@/components/magicui/scroll-based-velocity"; 2 | 3 | export function ScrollBasedVelocityDemo() { 4 | return ( 5 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "app/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "magicui": "@/components/magicui" 18 | } 19 | } -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /public/icons/analytics.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/icons/receive.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/icons/fast.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /components/demos/cover-demo.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Cover } from "@/components/ui/cover"; 3 | 4 | export function CoverDemo() { 5 | return ( 6 |
7 |

16 | Elevate your brand with
Bird Marketing Services 17 |

18 |
19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /public/icons/flexible.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "esModuleInterop": true, 9 | "module": "esnext", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "jsx": "preserve", 14 | "incremental": true, 15 | "plugins": [ 16 | { 17 | "name": "next" 18 | } 19 | ], 20 | "paths": { 21 | "@/*": ["./*"] 22 | } 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /public/icons/team.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/icons/money.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/icons/safe.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /components/footer.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import Link from "next/link"; 3 | 4 | const Footer = () => { 5 | return ( 6 |
7 |
8 |

9 | image{" "} 16 |

17 |

519-319-1562

18 |

19 | admin@birdsoftware.ca 20 |

21 |
22 | 23 |
24 | © 2024 Bird. All Rights Reserved. 25 | 26 | Privacy Policy 27 | 28 |
29 |
30 | ); 31 | }; 32 | 33 | export default Footer; 34 | -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Space_Grotesk } from "next/font/google"; 3 | import "./globals.css"; 4 | import { Analytics } from "@vercel/analytics/react"; 5 | 6 | const font = Space_Grotesk({ subsets: ["latin"] }); 7 | 8 | export const metadata: Metadata = { 9 | title: "Bird | Design & Development Studio", 10 | description: "Grow your business with Bird, a design & development studio.", 11 | openGraph: { 12 | images: [ 13 | { 14 | url: 'https://www.google.com/maps/uv?viewerState=lb&pb=!1s0x11ae81c94242f451:0x2a6b0cac40414137!5sGlobstand+technologies&imagekey=!1e10!2sAF1QipPrTTNGxsOszQTXFD3b68e40o0V_LiVsTWwh-H6&cr=rp_35', 15 | width: 1200, 16 | height: 630, 17 | alt: 'Bird Logo', 18 | }, 19 | ], 20 | }, 21 | }; 22 | 23 | export default function RootLayout({ 24 | children, 25 | }: Readonly<{ 26 | children: React.ReactNode; 27 | }>) { 28 | return ( 29 | 30 | 31 | 32 | {children} 33 | 34 | 35 | 36 | 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /public/icons/support.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /components/magicui/animated-shiny-text.tsx: -------------------------------------------------------------------------------- 1 | import { CSSProperties, FC, ReactNode } from "react"; 2 | 3 | import { cn } from "@/lib/utils"; 4 | 5 | interface AnimatedShinyTextProps { 6 | children: ReactNode; 7 | className?: string; 8 | shimmerWidth?: number; 9 | } 10 | 11 | const AnimatedShinyText: FC = ({ 12 | children, 13 | className, 14 | shimmerWidth = 100, 15 | }) => { 16 | return ( 17 |
35 | {children} 36 |
37 | ); 38 | }; 39 | 40 | export default AnimatedShinyText; 41 | -------------------------------------------------------------------------------- /components/magicui/word-fade-in.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { motion, Variants } from "framer-motion"; 4 | 5 | import { cn } from "@/lib/utils"; 6 | 7 | interface WordFadeInProps { 8 | words: string; 9 | className?: string; 10 | delay?: number; 11 | variants?: Variants; 12 | } 13 | 14 | export default function WordFadeIn({ 15 | words, 16 | delay = 0.15, 17 | variants = { 18 | hidden: { opacity: 0 }, 19 | visible: (i: any) => ({ 20 | y: 0, 21 | opacity: 1, 22 | transition: { delay: i * delay }, 23 | }), 24 | }, 25 | className, 26 | }: WordFadeInProps) { 27 | const _words = words.split(" "); 28 | 29 | return ( 30 | 39 | {_words.map((word, i) => ( 40 | 41 | {word}{" "} 42 | 43 | ))} 44 | 45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "digital-marketing", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@tabler/icons-react": "^3.12.0", 13 | "@tsparticles/engine": "^3.5.0", 14 | "@tsparticles/react": "^3.0.0", 15 | "@tsparticles/slim": "^3.5.0", 16 | "@vercel/analytics": "^1.3.1", 17 | "class-variance-authority": "^0.7.0", 18 | "clsx": "^2.1.1", 19 | "framer-motion": "^11.3.28", 20 | "lucide-react": "^0.428.0", 21 | "next": "14.2.5", 22 | "react": "^18", 23 | "react-calendly": "^4.3.1", 24 | "react-dom": "^18", 25 | "react-icons": "^5.3.0", 26 | "react-scroll": "^1.9.0", 27 | "tailwind-merge": "^2.5.2", 28 | "tailwindcss-animate": "^1.0.7" 29 | }, 30 | "devDependencies": { 31 | "@types/node": "^20", 32 | "@types/react": "^18", 33 | "@types/react-dom": "^18", 34 | "@types/react-scroll": "^1.8.10", 35 | "eslint": "^8", 36 | "eslint-config-next": "14.2.5", 37 | "postcss": "^8", 38 | "tailwindcss": "^3.4.1", 39 | "typescript": "^5" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /components/demos/animated-shiny-text-demo.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import { cn } from "@/lib/utils"; 3 | import AnimatedShinyText from "../magicui/animated-shiny-text"; 4 | import { FcGoogle } from "react-icons/fc"; 5 | 6 | export function AnimatedShinyTextDemo() { 7 | return ( 8 |
9 |
14 | 21 |
27 | ⭐{" "} ⭐{" "} ⭐{" "} ⭐{" "} ⭐ 28 |
29 | 30 | 31 | 4.9 stars 200+ google reviews 32 | 33 |
34 |
35 |
36 |
37 |
38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /public/icons/scalable.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/showcase/page.tsx: -------------------------------------------------------------------------------- 1 | import { BlurFadeDemo } from "@/components/demos/blur-fade-demo"; 2 | import Footer from "@/components/footer"; 3 | import WordFadeIn from "@/components/magicui/word-fade-in"; 4 | import ShowcaseNavbar from "@/components/showcase-navbar"; 5 | import LetsMakeThingsHappenSection from "@/components/ui/lets-make-things-happen"; 6 | 7 | const Showcase = () => { 8 | return ( 9 |
17 | 18 |
19 |
20 | 24 |
25 |

26 | Have a look at some of our recent projects. 27 |

28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 | 36 |
37 |
38 | ); 39 | }; 40 | 41 | export default Showcase; 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | # or 14 | bun dev 15 | ``` 16 | 17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 18 | 19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. 20 | 21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. 22 | 23 | ## Learn More 24 | 25 | To learn more about Next.js, take a look at the following resources: 26 | 27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 29 | 30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 31 | 32 | ## Deploy on Vercel 33 | 34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 35 | 36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 37 | -------------------------------------------------------------------------------- /components/demos/box-reveal-demo.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import BoxReveal from "@/components/magicui/box-reveal"; 4 | import Link from "next/link"; 5 | 6 | const BoxRevealDemo = () => { 7 | return ( 8 |
9 | 10 | 11 |

1. Connect

12 |
13 | 14 | 15 |

16 | Connect with us via 17 | 18 | {" "} 19 | meeting{" "} 20 | 21 |

22 |
23 | 24 |

2. Collaborate

25 |
26 | 27 | 28 |

29 | Outline the scope of the project 30 |

31 |
32 | 33 | 34 |

3. Create

35 |
36 | 37 | 38 |

Leave the rest to us

39 |
40 |
41 | ); 42 | } 43 | 44 | export default BoxRevealDemo; -------------------------------------------------------------------------------- /components/magicui/number-ticker.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useEffect, useRef } from "react"; 4 | import { useInView, useMotionValue, useSpring } from "framer-motion"; 5 | 6 | import { cn } from "@/lib/utils"; 7 | 8 | export default function NumberTicker({ 9 | value, 10 | direction = "up", 11 | delay = 0, 12 | className, 13 | }: { 14 | value: number; 15 | direction?: "up" | "down"; 16 | className?: string; 17 | delay?: number; // delay in s 18 | }) { 19 | const ref = useRef(null); 20 | const motionValue = useMotionValue(direction === "down" ? value : 0); 21 | const springValue = useSpring(motionValue, { 22 | damping: 60, 23 | stiffness: 100, 24 | }); 25 | const isInView = useInView(ref, { once: true, margin: "0px" }); 26 | 27 | useEffect(() => { 28 | isInView && 29 | setTimeout(() => { 30 | motionValue.set(direction === "down" ? 0 : value); 31 | }, delay * 1000); 32 | }, [motionValue, isInView, delay, value, direction]); 33 | 34 | useEffect( 35 | () => 36 | springValue.on("change", (latest) => { 37 | if (ref.current) { 38 | ref.current.textContent = Intl.NumberFormat("en-US").format( 39 | Number(latest.toFixed(0)), 40 | ); 41 | } 42 | }), 43 | [springValue], 44 | ); 45 | 46 | return ( 47 | 54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /components/magicui/word-pull-up.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { motion, Variants, useInView } from "framer-motion"; 4 | import { useRef } from "react"; 5 | import { cn } from "@/lib/utils"; 6 | 7 | interface WordPullUpProps { 8 | words: string; 9 | delayMultiple?: number; 10 | wrapperFramerProps?: Variants; 11 | framerProps?: Variants; 12 | className?: string; 13 | } 14 | 15 | export default function WordPullUp({ 16 | words, 17 | wrapperFramerProps = { 18 | hidden: { opacity: 0 }, 19 | show: { 20 | opacity: 1, 21 | transition: { 22 | staggerChildren: 0.2, 23 | delayChildren: 0.5, 24 | }, 25 | }, 26 | }, 27 | framerProps = { 28 | hidden: { y: 20, opacity: 0 }, 29 | show: { y: 0, opacity: 1 }, 30 | }, 31 | className, 32 | }: WordPullUpProps) { 33 | const ref = useRef(null); 34 | const isInView = useInView(ref, { once: true, amount: 0.5 }); 35 | 36 | return ( 37 | 47 | {words.split(" ").map((word, i) => ( 48 | 53 | {word === "" ?   : word} 54 | 55 | ))} 56 | 57 | ); 58 | } -------------------------------------------------------------------------------- /components/magicui/blur-fade.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useRef } from "react"; 4 | import { 5 | AnimatePresence, 6 | motion, 7 | useInView, 8 | UseInViewOptions, 9 | Variants, 10 | } from "framer-motion"; 11 | 12 | type MarginType = UseInViewOptions["margin"]; 13 | 14 | interface BlurFadeProps { 15 | children: React.ReactNode; 16 | className?: string; 17 | variant?: { 18 | hidden: { y: number }; 19 | visible: { y: number }; 20 | }; 21 | duration?: number; 22 | delay?: number; 23 | yOffset?: number; 24 | inView?: boolean; 25 | inViewMargin?: MarginType; 26 | blur?: string; 27 | } 28 | 29 | export default function BlurFade({ 30 | children, 31 | className, 32 | variant, 33 | duration = 0.4, 34 | delay = 0, 35 | yOffset = 6, 36 | inView = false, 37 | inViewMargin = "-50px", 38 | blur = "6px", 39 | }: BlurFadeProps) { 40 | const ref = useRef(null); 41 | const inViewResult = useInView(ref, { once: true, margin: inViewMargin }); 42 | const isInView = !inView || inViewResult; 43 | const defaultVariants: Variants = { 44 | hidden: { y: yOffset, opacity: 0, filter: `blur(${blur})` }, 45 | visible: { y: -yOffset, opacity: 1, filter: `blur(0px)` }, 46 | }; 47 | const combinedVariants = variant || defaultVariants; 48 | return ( 49 | 50 | 63 | {children} 64 | 65 | 66 | ); 67 | } 68 | -------------------------------------------------------------------------------- /public/icons/star.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /components/ui/lets-make-things-happen.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import Image from "next/image"; 4 | import Link from "next/link"; 5 | 6 | function LetsMakeThingsHappenSection() { 7 | return ( 8 |
11 |
12 |

13 | Let's make things happen 14 |

15 | 16 |

17 | Contact us today to learn more about how our digital marketing 18 | services can help your business grow and succeed online. 19 |

20 | 21 | 39 | Book a Call 40 | 41 |
42 |
43 | proposal illustration 49 |
50 |
51 | ); 52 | } 53 | 54 | export default LetsMakeThingsHappenSection; 55 | -------------------------------------------------------------------------------- /components/demos/blur-fade-demo.tsx: -------------------------------------------------------------------------------- 1 | import BlurFade from "@/components/magicui/blur-fade"; 2 | import Image from "next/image"; 3 | import Link from "next/link"; 4 | 5 | const works = [ 6 | { 7 | background: "bg-gray-200", 8 | imageUrl: "/images/business.webp", 9 | title: "Rubbish Brothers", 10 | link: "https://www.rubbishbrothers.com", 11 | }, 12 | { 13 | background: "bg-gray-200", 14 | imageUrl: "/images/business.webp", 15 | title: "Atlas Massage", 16 | link: "https://www.atlasmassage.ca", 17 | }, 18 | { 19 | background: "bg-gray-200", 20 | imageUrl: "/images/business.webp", 21 | title: "Canadian Security Solutions", 22 | link: "https://www.canadiansecuritysolutions.com", 23 | }, 24 | { 25 | background: "bg-gray-200", 26 | imageUrl: "/images/business.webp", 27 | title: "Flight 9", 28 | link: "https://www.flight9.art", 29 | }, 30 | 31 | 32 | ]; 33 | 34 | export function BlurFadeDemo() { 35 | return ( 36 |
37 |
38 | {works.map(({ imageUrl, title, link }, idx) => ( 39 | 45 | 46 | {`Random 57 |

{title}

58 | 59 |
60 | ))} 61 |
62 |
63 | ); 64 | } 65 | -------------------------------------------------------------------------------- /components/magicui/box-reveal.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useEffect, useRef } from "react"; 4 | import { motion, useAnimation, useInView } from "framer-motion"; 5 | 6 | interface BoxRevealProps { 7 | children: JSX.Element; 8 | width?: "fit-content" | "100%"; 9 | boxColor?: string; 10 | duration?: number; 11 | } 12 | 13 | export const BoxReveal = ({ 14 | children, 15 | width = "fit-content", 16 | boxColor, 17 | duration, 18 | }: BoxRevealProps) => { 19 | const mainControls = useAnimation(); 20 | const slideControls = useAnimation(); 21 | 22 | const ref = useRef(null); 23 | const isInView = useInView(ref, { once: true }); 24 | 25 | useEffect(() => { 26 | if (isInView) { 27 | slideControls.start("visible"); 28 | mainControls.start("visible"); 29 | } else { 30 | slideControls.start("hidden"); 31 | mainControls.start("hidden"); 32 | } 33 | }, [isInView, mainControls, slideControls]); 34 | 35 | return ( 36 |
37 | 46 | {children} 47 | 48 | 49 | 67 |
68 | ); 69 | }; 70 | 71 | export default BoxReveal; 72 | -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | :root { 7 | --background: 0 0% 100%; 8 | --foreground: 222.2 84% 4.9%; 9 | --card: 0 0% 100%; 10 | --card-foreground: 222.2 84% 4.9%; 11 | --popover: 0 0% 100%; 12 | --popover-foreground: 222.2 84% 4.9%; 13 | --primary: 222.2 47.4% 11.2%; 14 | --primary-foreground: 210 40% 98%; 15 | --secondary: 210 40% 96.1%; 16 | --secondary-foreground: 222.2 47.4% 11.2%; 17 | --muted: 210 40% 96.1%; 18 | --muted-foreground: 215.4 16.3% 46.9%; 19 | --accent: 210 40% 96.1%; 20 | --accent-foreground: 222.2 47.4% 11.2%; 21 | --destructive: 0 84.2% 60.2%; 22 | --destructive-foreground: 210 40% 98%; 23 | --border: 214.3 31.8% 91.4%; 24 | --input: 214.3 31.8% 91.4%; 25 | --ring: 222.2 84% 4.9%; 26 | --radius: 0.5rem; 27 | --chart-1: 12 76% 61%; 28 | --chart-2: 173 58% 39%; 29 | --chart-3: 197 37% 24%; 30 | --chart-4: 43 74% 66%; 31 | --chart-5: 27 87% 67%; 32 | } 33 | 34 | .dark { 35 | --background: 222.2 84% 4.9%; 36 | --foreground: 210 40% 98%; 37 | --card: 222.2 84% 4.9%; 38 | --card-foreground: 210 40% 98%; 39 | --popover: 222.2 84% 4.9%; 40 | --popover-foreground: 210 40% 98%; 41 | --primary: 210 40% 98%; 42 | --primary-foreground: 222.2 47.4% 11.2%; 43 | --secondary: 217.2 32.6% 17.5%; 44 | --secondary-foreground: 210 40% 98%; 45 | --muted: 217.2 32.6% 17.5%; 46 | --muted-foreground: 215 20.2% 65.1%; 47 | --accent: 217.2 32.6% 17.5%; 48 | --accent-foreground: 210 40% 98%; 49 | --destructive: 0 62.8% 30.6%; 50 | --destructive-foreground: 210 40% 98%; 51 | --border: 217.2 32.6% 17.5%; 52 | --input: 217.2 32.6% 17.5%; 53 | --ring: 212.7 26.8% 83.9%; 54 | --chart-1: 220 70% 50%; 55 | --chart-2: 160 60% 45%; 56 | --chart-3: 30 80% 55%; 57 | --chart-4: 280 65% 60%; 58 | --chart-5: 340 75% 55%; 59 | } 60 | } 61 | 62 | @layer base { 63 | * { 64 | @apply border-border; 65 | } 66 | body { 67 | @apply bg-background text-foreground; 68 | } 69 | } -------------------------------------------------------------------------------- /components/showcase-navbar.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import Link from "next/link"; 3 | 4 | const ShowcaseNavbar = () => { 5 | return ( 6 |
33 | 34 | Bird Logo 41 | 42 | 43 |
44 |
45 | 51 | Showcase 52 | 53 | 54 | Services 55 | 56 | 57 | Process 58 | 59 | 60 | Guarentees 61 | 62 | 63 |
64 |
65 | 66 | 67 |
68 | 69 | 72 | 73 | 74 | 75 | 90 | Book a call 91 | 92 |
93 |
94 | ); 95 | }; 96 | 97 | export default ShowcaseNavbar; 98 | -------------------------------------------------------------------------------- /app/meeting/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | 4 | import Calendly from "./calendly"; 5 | import ShowcaseNavbar from "@/components/showcase-navbar"; 6 | import { PiCheckCircle } from "react-icons/pi"; 7 | import { motion } from "framer-motion"; 8 | 9 | const checkItemVariants = { 10 | hidden: { opacity: 0, x: -50 }, 11 | visible: { opacity: 1, x: 0 }, 12 | }; 13 | 14 | const Meeting = () => { 15 | 16 | 17 | 18 | return ( 19 |
27 | 28 |
29 |
30 |

Let's Meet

31 |

32 | We are always excited to meet new people and discuss new projects. 33 | Please feel free to book a meeting with us. 34 |

35 | 36 | {[ 37 | { 38 | title: "Development + Design", 39 | description: 40 | "Turn your ideas into reality with our development and design services.", 41 | }, 42 | 43 | { 44 | title: "Free Consultation", 45 | description: 46 | "Get expert advice on how to improve your business and increase your online presence.", 47 | }, 48 | { 49 | title: "Technical Support", 50 | description: 51 | "Get technical support for your website or application.", 52 | }, 53 | ].map((item, index) => ( 54 | 62 | 63 |
    64 |

    65 | {item.title} 66 |

    67 |
    {item.description}
    68 |
69 |
70 | ))} 71 |
72 | 73 |
74 | 75 |
76 |
77 |
78 | ); 79 | }; 80 | 81 | export default Meeting; 82 | -------------------------------------------------------------------------------- /tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss" 2 | 3 | const config = { 4 | darkMode: ["class"], 5 | content: [ 6 | './pages/**/*.{ts,tsx}', 7 | './components/**/*.{ts,tsx}', 8 | './app/**/*.{ts,tsx}', 9 | './src/**/*.{ts,tsx}', 10 | ], 11 | prefix: "", 12 | theme: { 13 | container: { 14 | center: true, 15 | padding: "2rem", 16 | screens: { 17 | "2xl": "1400px", 18 | }, 19 | }, 20 | extend: { 21 | colors: { 22 | border: "hsl(var(--border))", 23 | input: "hsl(var(--input))", 24 | ring: "hsl(var(--ring))", 25 | background: "hsl(var(--background))", 26 | foreground: "hsl(var(--foreground))", 27 | primary: { 28 | DEFAULT: "hsl(var(--primary))", 29 | foreground: "hsl(var(--primary-foreground))", 30 | }, 31 | secondary: { 32 | DEFAULT: "hsl(var(--secondary))", 33 | foreground: "hsl(var(--secondary-foreground))", 34 | }, 35 | destructive: { 36 | DEFAULT: "hsl(var(--destructive))", 37 | foreground: "hsl(var(--destructive-foreground))", 38 | }, 39 | muted: { 40 | DEFAULT: "hsl(var(--muted))", 41 | foreground: "hsl(var(--muted-foreground))", 42 | }, 43 | accent: { 44 | DEFAULT: "hsl(var(--accent))", 45 | foreground: "hsl(var(--accent-foreground))", 46 | }, 47 | popover: { 48 | DEFAULT: "hsl(var(--popover))", 49 | foreground: "hsl(var(--popover-foreground))", 50 | }, 51 | card: { 52 | DEFAULT: "hsl(var(--card))", 53 | foreground: "hsl(var(--card-foreground))", 54 | }, 55 | }, 56 | borderRadius: { 57 | lg: "var(--radius)", 58 | md: "calc(var(--radius) - 2px)", 59 | sm: "calc(var(--radius) - 4px)", 60 | }, 61 | keyframes: { 62 | "accordion-down": { 63 | from: { height: "0" }, 64 | to: { height: "var(--radix-accordion-content-height)" }, 65 | }, 66 | "accordion-up": { 67 | from: { height: "var(--radix-accordion-content-height)" }, 68 | to: { height: "0" }, 69 | }, 70 | 'scroll': { 71 | to: { 72 | transform: "translate(calc(-50% - 0.5rem))", 73 | }, 74 | }, 75 | 'shimmer': { 76 | "0%, 90%, 100%": { 77 | "background-position": "calc(-100% - var(--shimmer-width, 200px)) 0", 78 | }, 79 | "30%, 60%": { 80 | "background-position": "calc(100% + var(--shimmer-width, 200px)) 0", 81 | }, 82 | }, 83 | 'shine' : { 84 | from: { 85 | "backgroundPosition": "0 0" 86 | }, 87 | to: { 88 | "backgroundPosition": "-200% 0" 89 | } 90 | }, 91 | 92 | }, 93 | animation: { 94 | "accordion-down": "accordion-down 0.2s ease-out", 95 | "accordion-up": "accordion-up 0.2s ease-out", 96 | 'scroll': 'scroll var(--animation-duration, 40s) var(--animation-direction, forwards) linear infinite', 97 | 'shimmer' : 'shimmer 8s infinite', 98 | 'shine' : 'shine 2s linear infinite', 99 | }, 100 | }, 101 | }, 102 | plugins: [require("tailwindcss-animate")], 103 | } satisfies Config 104 | 105 | export default config -------------------------------------------------------------------------------- /components/ui/infinite-moving-logos.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { cn } from "@/lib/utils"; 4 | import React, { useEffect, useState } from "react"; 5 | import Image from "next/image"; 6 | 7 | export const InfiniteMovingLogos = ({ 8 | items, 9 | direction = "left", 10 | speed = "slow", 11 | pauseOnHover = true, 12 | className, 13 | }: { 14 | items: { 15 | logo: string; 16 | name: string; 17 | }[]; 18 | direction?: "left" | "right"; 19 | speed?: "fast" | "normal" | "slow"; 20 | pauseOnHover?: boolean; 21 | className?: string; 22 | }) => { 23 | const containerRef = React.useRef(null); 24 | const scrollerRef = React.useRef(null); 25 | 26 | useEffect(() => { 27 | addAnimation(); 28 | }); 29 | const [start, setStart] = useState(false); 30 | function addAnimation() { 31 | if (containerRef.current && scrollerRef.current) { 32 | const scrollerContent = Array.from(scrollerRef.current.children); 33 | 34 | scrollerContent.forEach((item) => { 35 | const duplicatedItem = item.cloneNode(true); 36 | if (scrollerRef.current) { 37 | scrollerRef.current.appendChild(duplicatedItem); 38 | } 39 | }); 40 | 41 | getDirection(); 42 | getSpeed(); 43 | setStart(true); 44 | } 45 | } 46 | const getDirection = () => { 47 | if (containerRef.current) { 48 | if (direction === "left") { 49 | containerRef.current.style.setProperty( 50 | "--animation-direction", 51 | "forwards" 52 | ); 53 | } else { 54 | containerRef.current.style.setProperty( 55 | "--animation-direction", 56 | "reverse" 57 | ); 58 | } 59 | } 60 | }; 61 | const getSpeed = () => { 62 | if (containerRef.current) { 63 | if (speed === "fast") { 64 | containerRef.current.style.setProperty("--animation-duration", "20s"); 65 | } else if (speed === "normal") { 66 | containerRef.current.style.setProperty("--animation-duration", "40s"); 67 | } else { 68 | containerRef.current.style.setProperty("--animation-duration", "100s"); 69 | } 70 | } 71 | }; 72 | return ( 73 |
80 |
    88 | {items.map((item, idx) => ( 89 |
  • 101 |
    102 | {item.name} 112 |
    113 |
  • 114 | ))} 115 |
116 |
117 | ); 118 | }; -------------------------------------------------------------------------------- /components/magicui/scroll-based-velocity.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React, { useEffect, useRef, useState } from "react"; 4 | import { 5 | motion, 6 | useAnimationFrame, 7 | useMotionValue, 8 | useScroll, 9 | useSpring, 10 | useTransform, 11 | useVelocity, 12 | } from "framer-motion"; 13 | 14 | import { cn } from "@/lib/utils"; 15 | 16 | interface VelocityScrollProps { 17 | text: string; 18 | default_velocity?: number; 19 | className?: string; 20 | } 21 | 22 | interface ParallaxProps { 23 | children: string; 24 | baseVelocity: number; 25 | className?: string; 26 | } 27 | 28 | export const wrap = (min: number, max: number, v: number) => { 29 | const rangeSize = max - min; 30 | return ((((v - min) % rangeSize) + rangeSize) % rangeSize) + min; 31 | }; 32 | 33 | export function VelocityScroll({ 34 | text, 35 | default_velocity = 5, 36 | className, 37 | }: VelocityScrollProps) { 38 | function ParallaxText({ 39 | children, 40 | baseVelocity = 100, 41 | className, 42 | }: ParallaxProps) { 43 | const baseX = useMotionValue(0); 44 | const { scrollY } = useScroll(); 45 | const scrollVelocity = useVelocity(scrollY); 46 | const smoothVelocity = useSpring(scrollVelocity, { 47 | damping: 50, 48 | stiffness: 400, 49 | }); 50 | 51 | const velocityFactor = useTransform(smoothVelocity, [0, 1000], [0, 5], { 52 | clamp: false, 53 | }); 54 | 55 | const [repetitions, setRepetitions] = useState(1); 56 | const containerRef = useRef(null); 57 | const textRef = useRef(null); 58 | 59 | useEffect(() => { 60 | const calculateRepetitions = () => { 61 | if (containerRef.current && textRef.current) { 62 | const containerWidth = containerRef.current.offsetWidth; 63 | const textWidth = textRef.current.offsetWidth; 64 | const newRepetitions = Math.ceil(containerWidth / textWidth) + 2; 65 | setRepetitions(newRepetitions); 66 | } 67 | }; 68 | 69 | calculateRepetitions(); 70 | 71 | window.addEventListener("resize", calculateRepetitions); 72 | return () => window.removeEventListener("resize", calculateRepetitions); 73 | }, [children]); 74 | 75 | const x = useTransform(baseX, (v) => `${wrap(-100 / repetitions, 0, v)}%`); 76 | 77 | const directionFactor = React.useRef(1); 78 | useAnimationFrame((t, delta) => { 79 | let moveBy = directionFactor.current * baseVelocity * (delta / 1000); 80 | 81 | if (velocityFactor.get() < 0) { 82 | directionFactor.current = -1; 83 | } else if (velocityFactor.get() > 0) { 84 | directionFactor.current = 1; 85 | } 86 | 87 | moveBy += directionFactor.current * moveBy * velocityFactor.get(); 88 | 89 | baseX.set(baseX.get() + moveBy); 90 | }); 91 | 92 | return ( 93 |
97 | 98 | {Array.from({ length: repetitions }).map((_, i) => ( 99 | 100 | {children}{" "} 101 | 102 | ))} 103 | 104 |
105 | ); 106 | } 107 | 108 | return ( 109 |
110 | 111 | {text} 112 | 113 | 114 | {text} 115 | 116 |
117 | ); 118 | } 119 | -------------------------------------------------------------------------------- /components/demos/shooting-stars-demo.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ShootingStars } from "@/components/ui/shooting-stars"; 3 | import { StarsBackground } from "@/components/ui/stars-background"; 4 | import Image from "next/image"; 5 | 6 | const features = [ 7 | { 8 | icon: "/icons/fast.svg", 9 | title: "Fast Delivery", 10 | description: 11 | "1-2 weeks delivery for most projects. Call us for more information.", 12 | }, 13 | { 14 | icon: "/icons/design.svg", 15 | title: "Design & Development", 16 | description: 17 | "We design and develop your website with the latest technologies and trends.", 18 | }, 19 | { 20 | icon: "/icons/scalable.svg", 21 | title: "Scalability + Maintenance ", 22 | description: "We offer maintenance and scalability for all websites.", 23 | }, 24 | { 25 | icon: "/icons/team.svg", 26 | title: "Team of Experts", 27 | description: "A team of experts ready to help you, at all times.", 28 | }, 29 | { 30 | icon: "/icons/safe.svg", 31 | title: "Secure Building", 32 | description: 33 | "Safe and secure building practices. To ensure your data is safe.", 34 | }, 35 | { 36 | icon: "/icons/analytics.svg", 37 | title: "Analytics Tracking", 38 | description: "Track your progress with our built in analytics", 39 | }, 40 | 41 | { 42 | icon: "/icons/flexible.svg", 43 | title: "Dynamic Websites", 44 | description: "We build dynamic solutions that are easy to manage.", 45 | }, 46 | { 47 | icon: "/icons/support.svg", 48 | title: "24/7 Support", 49 | description: 50 | "We offer 24/7 support for all our clients. Call us for more information.", 51 | }, 52 | { 53 | icon: "/icons/money.svg", 54 | title: "Affordable Pricing", 55 | description: "Affordable pricing for all our clients.", 56 | }, 57 | ]; 58 | 59 | export function ShootingStarsAndStarsBackgroundDemo() { 60 | return ( 61 |
62 |

63 | Our guarantees to you. 64 |

65 | We ensure the highest quality of work, with the fastest delivery 66 | times. 67 |

68 |

69 | 70 | {/* Features */} 71 |
72 | {features.map((feature, index) => ( 73 |
77 | 92 | 93 |

94 | {feature.title} 95 |

96 |

{feature.description}

97 |
98 | ))} 99 |
100 | 101 | 102 | 103 |
104 | ); 105 | } 106 | -------------------------------------------------------------------------------- /components/ui/stars-background.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { cn } from "@/lib/utils"; 3 | import React, { 4 | useState, 5 | useEffect, 6 | useRef, 7 | RefObject, 8 | useCallback, 9 | } from "react"; 10 | 11 | interface StarProps { 12 | x: number; 13 | y: number; 14 | radius: number; 15 | opacity: number; 16 | twinkleSpeed: number | null; 17 | } 18 | 19 | interface StarBackgroundProps { 20 | starDensity?: number; 21 | allStarsTwinkle?: boolean; 22 | twinkleProbability?: number; 23 | minTwinkleSpeed?: number; 24 | maxTwinkleSpeed?: number; 25 | className?: string; 26 | } 27 | 28 | export const StarsBackground: React.FC = ({ 29 | starDensity = 0.00015, 30 | allStarsTwinkle = true, 31 | twinkleProbability = 0.7, 32 | minTwinkleSpeed = 0.5, 33 | maxTwinkleSpeed = 1, 34 | className, 35 | }) => { 36 | const [stars, setStars] = useState([]); 37 | const canvasRef: RefObject = 38 | useRef(null); 39 | 40 | const generateStars = useCallback( 41 | (width: number, height: number): StarProps[] => { 42 | const area = width * height; 43 | const numStars = Math.floor(area * starDensity); 44 | return Array.from({ length: numStars }, () => { 45 | const shouldTwinkle = 46 | allStarsTwinkle || Math.random() < twinkleProbability; 47 | return { 48 | x: Math.random() * width, 49 | y: Math.random() * height, 50 | radius: Math.random() * 0.05 + 0.5, 51 | opacity: Math.random() * 0.5 + 0.5, 52 | twinkleSpeed: shouldTwinkle 53 | ? minTwinkleSpeed + 54 | Math.random() * (maxTwinkleSpeed - minTwinkleSpeed) 55 | : null, 56 | }; 57 | }); 58 | }, 59 | [ 60 | starDensity, 61 | allStarsTwinkle, 62 | twinkleProbability, 63 | minTwinkleSpeed, 64 | maxTwinkleSpeed, 65 | ] 66 | ); 67 | 68 | useEffect(() => { 69 | const updateStars = () => { 70 | if (canvasRef.current) { 71 | const canvas = canvasRef.current; 72 | const ctx = canvas.getContext("2d"); 73 | if (!ctx) return; 74 | 75 | const { width, height } = canvas.getBoundingClientRect(); 76 | canvas.width = width; 77 | canvas.height = height; 78 | setStars(generateStars(width, height)); 79 | } 80 | }; 81 | 82 | updateStars(); 83 | 84 | const resizeObserver = new ResizeObserver(updateStars); 85 | if (canvasRef.current) { 86 | resizeObserver.observe(canvasRef.current); 87 | } 88 | 89 | return () => { 90 | if (canvasRef.current) { 91 | resizeObserver.unobserve(canvasRef.current); 92 | } 93 | }; 94 | }, [ 95 | starDensity, 96 | allStarsTwinkle, 97 | twinkleProbability, 98 | minTwinkleSpeed, 99 | maxTwinkleSpeed, 100 | generateStars, 101 | ]); 102 | 103 | useEffect(() => { 104 | const canvas = canvasRef.current; 105 | if (!canvas) return; 106 | 107 | const ctx = canvas.getContext("2d"); 108 | if (!ctx) return; 109 | 110 | let animationFrameId: number; 111 | 112 | const render = () => { 113 | ctx.clearRect(0, 0, canvas.width, canvas.height); 114 | stars.forEach((star) => { 115 | ctx.beginPath(); 116 | ctx.arc(star.x, star.y, star.radius, 0, Math.PI * 2); 117 | ctx.fillStyle = `rgba(255, 255, 255, ${star.opacity})`; 118 | ctx.fill(); 119 | 120 | if (star.twinkleSpeed !== null) { 121 | star.opacity = 122 | 0.5 + 123 | Math.abs(Math.sin((Date.now() * 0.001) / star.twinkleSpeed) * 0.5); 124 | } 125 | }); 126 | 127 | animationFrameId = requestAnimationFrame(render); 128 | }; 129 | 130 | render(); 131 | 132 | return () => { 133 | cancelAnimationFrame(animationFrameId); 134 | }; 135 | }, [stars]); 136 | 137 | return ( 138 | 142 | ); 143 | }; 144 | -------------------------------------------------------------------------------- /components/ui/shooting-stars.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { cn } from "@/lib/utils"; 3 | import React, { useEffect, useState, useRef } from "react"; 4 | 5 | interface ShootingStar { 6 | id: number; 7 | x: number; 8 | y: number; 9 | angle: number; 10 | scale: number; 11 | speed: number; 12 | distance: number; 13 | } 14 | 15 | interface ShootingStarsProps { 16 | minSpeed?: number; 17 | maxSpeed?: number; 18 | minDelay?: number; 19 | maxDelay?: number; 20 | starColor?: string; 21 | trailColor?: string; 22 | starWidth?: number; 23 | starHeight?: number; 24 | className?: string; 25 | } 26 | 27 | const getRandomStartPoint = () => { 28 | const side = Math.floor(Math.random() * 4); 29 | const offset = Math.random() * window.innerWidth; 30 | 31 | switch (side) { 32 | case 0: 33 | return { x: offset, y: 0, angle: 45 }; 34 | case 1: 35 | return { x: window.innerWidth, y: offset, angle: 135 }; 36 | case 2: 37 | return { x: offset, y: window.innerHeight, angle: 225 }; 38 | case 3: 39 | return { x: 0, y: offset, angle: 315 }; 40 | default: 41 | return { x: 0, y: 0, angle: 45 }; 42 | } 43 | }; 44 | export const ShootingStars: React.FC = ({ 45 | minSpeed = 10, 46 | maxSpeed = 30, 47 | minDelay = 1200, 48 | maxDelay = 4200, 49 | starColor = "#9E00FF", 50 | trailColor = "#2EB9DF", 51 | starWidth = 10, 52 | starHeight = 1, 53 | className, 54 | }) => { 55 | const [star, setStar] = useState(null); 56 | const svgRef = useRef(null); 57 | 58 | useEffect(() => { 59 | const createStar = () => { 60 | const { x, y, angle } = getRandomStartPoint(); 61 | const newStar: ShootingStar = { 62 | id: Date.now(), 63 | x, 64 | y, 65 | angle, 66 | scale: 1, 67 | speed: Math.random() * (maxSpeed - minSpeed) + minSpeed, 68 | distance: 0, 69 | }; 70 | setStar(newStar); 71 | 72 | const randomDelay = Math.random() * (maxDelay - minDelay) + minDelay; 73 | setTimeout(createStar, randomDelay); 74 | }; 75 | 76 | createStar(); 77 | 78 | return () => {}; 79 | }, [minSpeed, maxSpeed, minDelay, maxDelay]); 80 | 81 | useEffect(() => { 82 | const moveStar = () => { 83 | if (star) { 84 | setStar((prevStar) => { 85 | if (!prevStar) return null; 86 | const newX = 87 | prevStar.x + 88 | prevStar.speed * Math.cos((prevStar.angle * Math.PI) / 180); 89 | const newY = 90 | prevStar.y + 91 | prevStar.speed * Math.sin((prevStar.angle * Math.PI) / 180); 92 | const newDistance = prevStar.distance + prevStar.speed; 93 | const newScale = 1 + newDistance / 100; 94 | if ( 95 | newX < -20 || 96 | newX > window.innerWidth + 20 || 97 | newY < -20 || 98 | newY > window.innerHeight + 20 99 | ) { 100 | return null; 101 | } 102 | return { 103 | ...prevStar, 104 | x: newX, 105 | y: newY, 106 | distance: newDistance, 107 | scale: newScale, 108 | }; 109 | }); 110 | } 111 | }; 112 | 113 | const animationFrame = requestAnimationFrame(moveStar); 114 | return () => cancelAnimationFrame(animationFrame); 115 | }, [star]); 116 | 117 | return ( 118 | 122 | {star && ( 123 | 134 | )} 135 | 136 | 137 | 138 | 142 | 143 | 144 | 145 | ); 146 | }; 147 | -------------------------------------------------------------------------------- /components/magicui/animated-beam.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { RefObject, useEffect, useId, useState } from "react"; 4 | import { motion } from "framer-motion"; 5 | 6 | import { cn } from "@/lib/utils"; 7 | 8 | export interface AnimatedBeamProps { 9 | className?: string; 10 | containerRef: RefObject; // Container ref 11 | fromRef: RefObject; 12 | toRef: RefObject; 13 | curvature?: number; 14 | reverse?: boolean; 15 | pathColor?: string; 16 | pathWidth?: number; 17 | pathOpacity?: number; 18 | gradientStartColor?: string; 19 | gradientStopColor?: string; 20 | delay?: number; 21 | duration?: number; 22 | startXOffset?: number; 23 | startYOffset?: number; 24 | endXOffset?: number; 25 | endYOffset?: number; 26 | } 27 | 28 | export const AnimatedBeam: React.FC = ({ 29 | className, 30 | containerRef, 31 | fromRef, 32 | toRef, 33 | curvature = 0, 34 | reverse = false, // Include the reverse prop 35 | duration = Math.random() * 3 + 4, 36 | delay = 0, 37 | pathColor = "gray", 38 | pathWidth = 2, 39 | pathOpacity = 0.2, 40 | gradientStartColor = "#ffaa40", 41 | gradientStopColor = "#9c40ff", 42 | startXOffset = 0, 43 | startYOffset = 0, 44 | endXOffset = 0, 45 | endYOffset = 0, 46 | }) => { 47 | const id = useId(); 48 | const [pathD, setPathD] = useState(""); 49 | const [svgDimensions, setSvgDimensions] = useState({ width: 0, height: 0 }); 50 | 51 | // Calculate the gradient coordinates based on the reverse prop 52 | const gradientCoordinates = reverse 53 | ? { 54 | x1: ["90%", "-10%"], 55 | x2: ["100%", "0%"], 56 | y1: ["0%", "0%"], 57 | y2: ["0%", "0%"], 58 | } 59 | : { 60 | x1: ["10%", "110%"], 61 | x2: ["0%", "100%"], 62 | y1: ["0%", "0%"], 63 | y2: ["0%", "0%"], 64 | }; 65 | 66 | useEffect(() => { 67 | const updatePath = () => { 68 | if (containerRef.current && fromRef.current && toRef.current) { 69 | const containerRect = containerRef.current.getBoundingClientRect(); 70 | const rectA = fromRef.current.getBoundingClientRect(); 71 | const rectB = toRef.current.getBoundingClientRect(); 72 | 73 | const svgWidth = containerRect.width; 74 | const svgHeight = containerRect.height; 75 | setSvgDimensions({ width: svgWidth, height: svgHeight }); 76 | 77 | const startX = 78 | rectA.left - containerRect.left + rectA.width / 2 + startXOffset; 79 | const startY = 80 | rectA.top - containerRect.top + rectA.height / 2 + startYOffset; 81 | const endX = 82 | rectB.left - containerRect.left + rectB.width / 2 + endXOffset; 83 | const endY = 84 | rectB.top - containerRect.top + rectB.height / 2 + endYOffset; 85 | 86 | const controlY = startY - curvature; 87 | const d = `M ${startX},${startY} Q ${ 88 | (startX + endX) / 2 89 | },${controlY} ${endX},${endY}`; 90 | setPathD(d); 91 | } 92 | }; 93 | 94 | // Initialize ResizeObserver 95 | const resizeObserver = new ResizeObserver((entries) => { 96 | // For all entries, recalculate the path 97 | for (let entry of entries) { 98 | updatePath(); 99 | } 100 | }); 101 | 102 | // Observe the container element 103 | if (containerRef.current) { 104 | resizeObserver.observe(containerRef.current); 105 | } 106 | 107 | // Call the updatePath initially to set the initial path 108 | updatePath(); 109 | 110 | // Clean up the observer on component unmount 111 | return () => { 112 | resizeObserver.disconnect(); 113 | }; 114 | }, [ 115 | containerRef, 116 | fromRef, 117 | toRef, 118 | curvature, 119 | startXOffset, 120 | startYOffset, 121 | endXOffset, 122 | endYOffset, 123 | ]); 124 | 125 | return ( 126 | 137 | 144 | 151 | 152 | 176 | 177 | 178 | 179 | 184 | 185 | 186 | 187 | ); 188 | }; 189 | -------------------------------------------------------------------------------- /components/ui/cover.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import React, { useEffect, useId, useState } from "react"; 3 | import { AnimatePresence, motion } from "framer-motion"; 4 | import { useRef } from "react"; 5 | import { cn } from "@/lib/utils"; 6 | import { SparklesCore } from "./sparkles"; 7 | 8 | export const Cover = ({ 9 | children, 10 | className, 11 | }: { 12 | children?: React.ReactNode; 13 | className?: string; 14 | }) => { 15 | const [hovered, setHovered] = useState(false); 16 | 17 | const ref = useRef(null); 18 | 19 | const [containerWidth, setContainerWidth] = useState(0); 20 | const [beamPositions, setBeamPositions] = useState([]); 21 | 22 | useEffect(() => { 23 | if (ref.current) { 24 | setContainerWidth(ref.current?.clientWidth ?? 0); 25 | 26 | const height = ref.current?.clientHeight ?? 0; 27 | const numberOfBeams = Math.floor(height / 10); // Adjust the divisor to control the spacing 28 | const positions = Array.from( 29 | { length: numberOfBeams }, 30 | (_, i) => (i + 1) * (height / (numberOfBeams + 1)) 31 | ); 32 | setBeamPositions(positions); 33 | } 34 | }, [ref.current]); 35 | 36 | return ( 37 |
setHovered(true)} 39 | onMouseLeave={() => setHovered(false)} 40 | ref={ref} 41 | className="relative hover:bg-neutral-900 group/cover inline-block dark:bg-neutral-900 bg-neutral-100 px-2 py-2 transition duration-200 rounded-sm" 42 | > 43 | 44 | {hovered && ( 45 | 56 | 69 | 77 | 85 | 86 | 87 | )} 88 | 89 | {beamPositions.map((position, index) => ( 90 | 100 | ))} 101 | 138 | {children} 139 | 140 | 141 | 142 | 143 | 144 |
145 | ); 146 | }; 147 | 148 | export const Beam = ({ 149 | className, 150 | delay, 151 | duration, 152 | hovered, 153 | width = 600, 154 | ...svgProps 155 | }: { 156 | className?: string; 157 | delay?: number; 158 | duration?: number; 159 | hovered?: boolean; 160 | width?: number; 161 | } & React.ComponentProps) => { 162 | const id = useId(); 163 | 164 | return ( 165 | 174 | 178 | 179 | 180 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | ); 211 | }; 212 | 213 | export const CircleIcon = ({ 214 | className, 215 | delay, 216 | }: { 217 | className?: string; 218 | delay?: number; 219 | }) => { 220 | return ( 221 |
227 | ); 228 | }; 229 | -------------------------------------------------------------------------------- /components/ui/sparkles.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import React, { useId, useMemo } from "react"; 3 | import { useEffect, useState } from "react"; 4 | import Particles, { initParticlesEngine } from "@tsparticles/react"; 5 | import type { Container, SingleOrMultiple } from "@tsparticles/engine"; 6 | import { loadSlim } from "@tsparticles/slim"; 7 | import { cn } from "@/lib/utils"; 8 | import { motion, useAnimation } from "framer-motion"; 9 | 10 | type ParticlesProps = { 11 | id?: string; 12 | className?: string; 13 | background?: string; 14 | particleSize?: number; 15 | minSize?: number; 16 | maxSize?: number; 17 | speed?: number; 18 | particleColor?: string; 19 | particleDensity?: number; 20 | }; 21 | export const SparklesCore = (props: ParticlesProps) => { 22 | const { 23 | id, 24 | className, 25 | background, 26 | minSize, 27 | maxSize, 28 | speed, 29 | particleColor, 30 | particleDensity, 31 | } = props; 32 | const [init, setInit] = useState(false); 33 | useEffect(() => { 34 | initParticlesEngine(async (engine) => { 35 | await loadSlim(engine); 36 | }).then(() => { 37 | setInit(true); 38 | }); 39 | }, []); 40 | const controls = useAnimation(); 41 | 42 | const particlesLoaded = async (container?: Container) => { 43 | if (container) { 44 | console.log(container); 45 | controls.start({ 46 | opacity: 1, 47 | transition: { 48 | duration: 1, 49 | }, 50 | }); 51 | } 52 | }; 53 | 54 | const generatedId = useId(); 55 | return ( 56 | 57 | {init && ( 58 | | undefined, 162 | }, 163 | groups: {}, 164 | move: { 165 | angle: { 166 | offset: 0, 167 | value: 90, 168 | }, 169 | attract: { 170 | distance: 200, 171 | enable: false, 172 | rotate: { 173 | x: 3000, 174 | y: 3000, 175 | }, 176 | }, 177 | center: { 178 | x: 50, 179 | y: 50, 180 | mode: "percent", 181 | radius: 0, 182 | }, 183 | decay: 0, 184 | distance: {}, 185 | direction: "none", 186 | drift: 0, 187 | enable: true, 188 | gravity: { 189 | acceleration: 9.81, 190 | enable: false, 191 | inverse: false, 192 | maxSpeed: 50, 193 | }, 194 | path: { 195 | clamp: true, 196 | delay: { 197 | value: 0, 198 | }, 199 | enable: false, 200 | options: {}, 201 | }, 202 | outModes: { 203 | default: "out", 204 | }, 205 | random: false, 206 | size: false, 207 | speed: { 208 | min: 0.1, 209 | max: 1, 210 | }, 211 | spin: { 212 | acceleration: 0, 213 | enable: false, 214 | }, 215 | straight: false, 216 | trail: { 217 | enable: false, 218 | length: 10, 219 | fill: {}, 220 | }, 221 | vibrate: false, 222 | warp: false, 223 | }, 224 | number: { 225 | density: { 226 | enable: true, 227 | width: 400, 228 | height: 400, 229 | }, 230 | limit: { 231 | mode: "delete", 232 | value: 0, 233 | }, 234 | value: particleDensity || 120, 235 | }, 236 | opacity: { 237 | value: { 238 | min: 0.1, 239 | max: 1, 240 | }, 241 | animation: { 242 | count: 0, 243 | enable: true, 244 | speed: speed || 4, 245 | decay: 0, 246 | delay: 0, 247 | sync: false, 248 | mode: "auto", 249 | startValue: "random", 250 | destroy: "none", 251 | }, 252 | }, 253 | reduceDuplicates: false, 254 | shadow: { 255 | blur: 0, 256 | color: { 257 | value: "#000", 258 | }, 259 | enable: false, 260 | offset: { 261 | x: 0, 262 | y: 0, 263 | }, 264 | }, 265 | shape: { 266 | close: true, 267 | fill: true, 268 | options: {}, 269 | type: "circle", 270 | }, 271 | size: { 272 | value: { 273 | min: minSize || 1, 274 | max: maxSize || 3, 275 | }, 276 | animation: { 277 | count: 0, 278 | enable: false, 279 | speed: 5, 280 | decay: 0, 281 | delay: 0, 282 | sync: false, 283 | mode: "auto", 284 | startValue: "random", 285 | destroy: "none", 286 | }, 287 | }, 288 | stroke: { 289 | width: 0, 290 | }, 291 | zIndex: { 292 | value: 0, 293 | opacityRate: 1, 294 | sizeRate: 1, 295 | velocityRate: 1, 296 | }, 297 | destroy: { 298 | bounds: {}, 299 | mode: "none", 300 | split: { 301 | count: 1, 302 | factor: { 303 | value: 3, 304 | }, 305 | rate: { 306 | value: { 307 | min: 4, 308 | max: 9, 309 | }, 310 | }, 311 | sizeOffset: true, 312 | }, 313 | }, 314 | roll: { 315 | darken: { 316 | enable: false, 317 | value: 0, 318 | }, 319 | enable: false, 320 | enlighten: { 321 | enable: false, 322 | value: 0, 323 | }, 324 | mode: "vertical", 325 | speed: 25, 326 | }, 327 | tilt: { 328 | value: 0, 329 | animation: { 330 | enable: false, 331 | speed: 0, 332 | decay: 0, 333 | sync: false, 334 | }, 335 | direction: "clockwise", 336 | enable: false, 337 | }, 338 | twinkle: { 339 | lines: { 340 | enable: false, 341 | frequency: 0.05, 342 | opacity: 1, 343 | }, 344 | particles: { 345 | enable: false, 346 | frequency: 0.05, 347 | opacity: 1, 348 | }, 349 | }, 350 | wobble: { 351 | distance: 5, 352 | enable: false, 353 | speed: { 354 | angle: 50, 355 | move: 10, 356 | }, 357 | }, 358 | life: { 359 | count: 0, 360 | delay: { 361 | value: 0, 362 | sync: false, 363 | }, 364 | duration: { 365 | value: 0, 366 | sync: false, 367 | }, 368 | }, 369 | rotate: { 370 | value: 0, 371 | animation: { 372 | enable: false, 373 | speed: 0, 374 | decay: 0, 375 | sync: false, 376 | }, 377 | direction: "clockwise", 378 | path: false, 379 | }, 380 | orbit: { 381 | animation: { 382 | count: 0, 383 | enable: false, 384 | speed: 1, 385 | decay: 0, 386 | delay: 0, 387 | sync: false, 388 | }, 389 | enable: false, 390 | opacity: 1, 391 | rotation: { 392 | value: 45, 393 | }, 394 | width: 1, 395 | }, 396 | links: { 397 | blink: false, 398 | color: { 399 | value: "#fff", 400 | }, 401 | consent: false, 402 | distance: 100, 403 | enable: false, 404 | frequency: 1, 405 | opacity: 1, 406 | shadow: { 407 | blur: 5, 408 | color: { 409 | value: "#000", 410 | }, 411 | enable: false, 412 | }, 413 | triangles: { 414 | enable: false, 415 | frequency: 1, 416 | }, 417 | width: 1, 418 | warp: false, 419 | }, 420 | repulse: { 421 | value: 0, 422 | enabled: false, 423 | distance: 1, 424 | duration: 1, 425 | factor: 1, 426 | speed: 1, 427 | }, 428 | }, 429 | detectRetina: true, 430 | }} 431 | /> 432 | )} 433 | 434 | ); 435 | }; 436 | -------------------------------------------------------------------------------- /app/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { AnimatedBeamMultipleOutputDemo } from "@/components/demos/animated-beam-demo"; 4 | import { AnimatedShinyTextDemo } from "@/components/demos/animated-shiny-text-demo"; 5 | import BoxRevealDemo from "@/components/demos/box-reveal-demo"; 6 | import { CoverDemo } from "@/components/demos/cover-demo"; 7 | import { ScrollBasedVelocityDemo } from "@/components/demos/scroll-based-velocity-demo"; 8 | import { WordPullUpDemo } from "@/components/demos/word-pull-up-demo"; 9 | import BoxReveal from "@/components/magicui/box-reveal"; 10 | import NumberTicker from "@/components/magicui/number-ticker"; 11 | import { InfiniteMovingLogos } from "@/components/ui/infinite-moving-logos"; 12 | import Image from "next/image"; 13 | import Link from "next/link"; 14 | import { PiCheckBold } from "react-icons/pi"; 15 | import { Link as ScrollLink, Element } from "react-scroll"; 16 | import { IconStarFilled } from "@tabler/icons-react"; 17 | import { ShootingStarsAndStarsBackgroundDemo } from "@/components/demos/shooting-stars-demo"; 18 | import LetsMakeThingsHappenSection from "@/components/ui/lets-make-things-happen"; 19 | 20 | 21 | const services = [ 22 | { 23 | icon: "/images/s_6.png", 24 | title: "Web Design + Development", 25 | description: 26 | "Take your business to the next level with our web design and development services", 27 | }, 28 | { 29 | icon: "/images/s_1.png", 30 | title: "Search Engine Optimization", 31 | description: 32 | "Get your website to the top of search engine results with our SEO services", 33 | }, 34 | { 35 | icon: "/images/s_5.png", 36 | title: "Content Creation", 37 | description: 38 | "Boost your brand's online presence with our social media marketing services", 39 | }, 40 | { 41 | icon: "/images/s_3.png", 42 | title: "Social Media Marketing", 43 | description: 44 | "Interact with your customers and increase sales with our email marketing services", 45 | }, 46 | { 47 | icon: "/images/s_4.png", 48 | title: "Email Marketing", 49 | description: 50 | "With our content creation services, we help businesses drive results", 51 | }, 52 | { 53 | icon: "/images/s_2.png", 54 | title: "Pay-Per-Click Advertising", 55 | description: 56 | "Don't waste money on ineffective advertising. Our PPC services help you reach your target audience", 57 | }, 58 | ]; 59 | 60 | export default function Home() { 61 | return ( 62 |
69 | 75 | 76 | Logo 83 | 84 | 85 |
86 |
87 | 88 | Showcase 89 | 90 | 91 | 96 | Services 97 | 98 | 99 | 104 | Process 105 | 106 | 107 | 112 | Guarentees 113 | 114 |
115 |
116 | 117 |
118 | 119 | 122 | 123 | 124 | 140 | Book a call 141 | 142 |
143 |
144 | 145 |
146 |
147 | 148 | 149 |

150 | 151 |

152 |

156 | Schedule a call with us to discuss your project and get a quote in 157 | minutes 158 |

159 | 160 |
168 | 185 | Book a Call 186 | 187 | 200 | Showcase 201 | 202 |
203 | 204 |
205 | 206 |

207 | 208 | Design 209 |

210 |
211 | 212 |

213 | 214 | Development 215 |

216 |
217 | 218 |

219 | 220 | Marketing 221 |

222 |
223 | 224 |

225 | 226 | Strategy 227 |

228 |
229 |
230 | 231 |
232 |
233 |

234 | Trusted by fast moving brands worldwide 235 |

236 | 237 |
238 |
239 |

240 | + 241 |

242 | Happy Clients 243 |

244 |

245 |
246 | 247 |
248 | 249 |
250 |

251 | + 252 |

253 | Projects Completed 254 |

255 |

256 |
257 |
258 |
259 | 260 |
261 | 275 |
276 |
277 |
278 |
279 | 280 | 281 |
282 |

283 | 284 |

285 |

286 | All of our services are designed to help your business stand out 287 |

288 | 289 |
290 | {services.map((service) => ( 291 |
295 | image 302 |

{service.title}

303 |

{service.description}

304 |
305 | ))} 306 |
307 |
308 |
309 | 310 |
311 | 312 |
313 | 314 | 315 |
316 |

317 | Our{" "} 318 | 319 | {" "} 320 | image 327 | Creative 328 | image 335 | {" "} 336 | Process 337 |

338 | 339 |

342 | All of our services are designed to help your business to get 343 | noticed. 344 |

345 | 346 |
347 |
348 | 349 |
350 |
351 | 352 |
353 | 354 |
355 | 356 |
357 |
358 | 359 |
360 |
361 | image 368 |
369 |

370 | "We've been working with Bird for over 2 years and 371 | they've been amazing to work with. They've helped us 372 | grow our business and we couldn't be happier with the 373 | results. " 374 |

375 |
376 | 377 | 378 | 379 | 380 | 381 |
382 | 383 | 384 | Jordan, Brisson
385 | CEO, Atlas Massage 386 |
387 |
388 |
389 |
390 | 391 | 392 | 393 | 394 | 395 |
396 | 397 |
398 | 399 |
400 |
401 |

402 | image{" "} 409 |

410 |

519-200-5000

411 |

412 | admin@birdsoftware.ca 413 |

414 |
415 | 416 |
417 | © 2025 Bird. All Rights Reserved. 418 | 419 | Privacy Policy 420 | 421 |
422 |
423 |
424 | ); 425 | } 426 | -------------------------------------------------------------------------------- /components/demos/animated-beam-demo.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React, { forwardRef, useRef } from "react"; 4 | 5 | import { cn } from "@/lib/utils"; 6 | import { AnimatedBeam } from "@/components/magicui/animated-beam"; 7 | import Image from "next/image"; 8 | import { PiFigmaLogo } from "react-icons/pi"; 9 | 10 | const Circle = forwardRef< 11 | HTMLDivElement, 12 | { className?: string; children?: React.ReactNode } 13 | >(({ className, children }, ref) => { 14 | return ( 15 |
22 | {children} 23 |
24 | ); 25 | }); 26 | 27 | Circle.displayName = "Circle"; 28 | 29 | export function AnimatedBeamMultipleOutputDemo({ 30 | className, 31 | }: { 32 | className?: string; 33 | }) { 34 | const containerRef = useRef(null); 35 | const div1Ref = useRef(null); 36 | const div2Ref = useRef(null); 37 | const div3Ref = useRef(null); 38 | const div4Ref = useRef(null); 39 | const div5Ref = useRef(null); 40 | const div6Ref = useRef(null); 41 | const div7Ref = useRef(null); 42 | 43 | return ( 44 |
51 |
52 |
53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | image 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 |
75 |
76 | 77 | logo 83 | 84 |
85 |
86 | 87 | 88 | 89 |
90 |
91 | 92 | 97 | 102 | 107 | 112 | 117 | 122 |
123 | ); 124 | } 125 | 126 | const Icons = { 127 | notion: () => ( 128 | 135 | 139 | 145 | 146 | ), 147 | 148 | googleDrive: () => ( 149 | 155 | 159 | 163 | 167 | 171 | 175 | 179 | 180 | ), 181 | 182 | googleDocs: () => ( 183 | 189 | 190 | 194 | 198 | 205 | 206 | 207 | 208 | 212 | 216 | 220 | 224 | 228 | 237 | 238 | 239 | 240 | 241 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 349 | 350 | 351 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | ), 364 | zapier: () => ( 365 | 372 | 376 | 380 | 384 | 388 | 392 | 396 | 400 | 401 | 402 | ), 403 | messenger: () => ( 404 | 410 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 430 | 434 | 438 | 442 | 443 | ), 444 | user: () => ( 445 | 454 | 455 | 456 | 457 | ), 458 | }; --------------------------------------------------------------------------------