├── .eslintrc.json
├── .github
└── workflows
│ └── nextjs.yml
├── .gitignore
├── .prettierignore
├── .prettierrc.json
├── .vscode
├── settings.json
└── tailwind.json
├── README.md
├── Screenshot.png
├── next.config.mjs
├── package-lock.json
├── package.json
├── postcss.config.mjs
├── public
├── assets
│ └── lottie
│ │ ├── click.lottie
│ │ ├── stars.lottie
│ │ └── vroom.lottie
├── next.svg
└── vercel.svg
├── src
├── app
│ ├── favicon.ico
│ ├── layout.tsx
│ └── page.tsx
├── assets
│ ├── avatar-1.png
│ ├── avatar-2.png
│ ├── avatar-3.png
│ ├── avatar-4.png
│ ├── grid-lines.png
│ ├── icon-click.svg
│ ├── icon-guage.svg
│ ├── icon-menu.svg
│ ├── icon-stars.svg
│ ├── index.ts
│ ├── logo-acme.png
│ ├── logo-apex.png
│ ├── logo-celestial.png
│ ├── logo-echo.png
│ ├── logo-pulse.png
│ ├── logo-quantum.png
│ ├── logo.svg
│ ├── product-image.png
│ ├── social-instagram.svg
│ ├── social-x.svg
│ ├── social-youtube.svg
│ └── stars.png
├── components
│ ├── .gitkeep
│ ├── Button.tsx
│ └── FeatureTab.tsx
├── data
│ └── data.ts
├── hooks
│ └── useRelativeMousePosition.ts
├── sections
│ ├── CallToAction.tsx
│ ├── Features.tsx
│ ├── Footer.tsx
│ ├── Header.tsx
│ ├── Hero.tsx
│ ├── LogoTicker.tsx
│ └── Testimonials.tsx
└── styles
│ └── globals.css
├── tailwind.config.ts
├── tsconfig.json
└── yarn.lock
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "extends": ["next/core-web-vitals", "eslint:recommended"],
4 | "overrides": [
5 | {
6 | "files": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"]
7 | }
8 | ],
9 | "rules": {
10 | "no-unused-vars": "warn",
11 | "no-console": "warn",
12 | "no-debugger": "error",
13 | "no-alert": "error",
14 | "no-eval": "error",
15 | "no-empty": "error",
16 | "no-undef": "warn",
17 | "no-var": "error",
18 | "eqeqeq": ["error", "always"],
19 | "curly": "error",
20 | "strict": ["error", "global"]
21 | },
22 | "parserOptions": {
23 | "ecmaVersion": 2020,
24 | "sourceType": "module"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/.github/workflows/nextjs.yml:
--------------------------------------------------------------------------------
1 | # Sample workflow for building and deploying a Next.js site to GitHub Pages
2 | #
3 | # To get started with Next.js see: https://nextjs.org/docs/getting-started
4 | #
5 | name: Deploy Next.js site to Pages
6 |
7 | on:
8 | # Runs on pushes targeting the default branch
9 | push:
10 | branches: ["main"]
11 |
12 | # Allows you to run this workflow manually from the Actions tab
13 | workflow_dispatch:
14 |
15 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
16 | permissions:
17 | contents: read
18 | pages: write
19 | id-token: write
20 |
21 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
22 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
23 | concurrency:
24 | group: "pages"
25 | cancel-in-progress: false
26 |
27 | jobs:
28 | # Build job
29 | build:
30 | runs-on: ubuntu-latest
31 | steps:
32 | - name: Checkout
33 | uses: actions/checkout@v4
34 | - name: Detect package manager
35 | id: detect-package-manager
36 | run: |
37 | if [ -f "${{ github.workspace }}/yarn.lock" ]; then
38 | echo "manager=yarn" >> $GITHUB_OUTPUT
39 | echo "command=install" >> $GITHUB_OUTPUT
40 | echo "runner=yarn" >> $GITHUB_OUTPUT
41 | exit 0
42 | elif [ -f "${{ github.workspace }}/package.json" ]; then
43 | echo "manager=npm" >> $GITHUB_OUTPUT
44 | echo "command=ci" >> $GITHUB_OUTPUT
45 | echo "runner=npx --no-install" >> $GITHUB_OUTPUT
46 | exit 0
47 | else
48 | echo "Unable to determine package manager"
49 | exit 1
50 | fi
51 | - name: Setup Node
52 | uses: actions/setup-node@v4
53 | with:
54 | node-version: "20"
55 | cache: ${{ steps.detect-package-manager.outputs.manager }}
56 | - name: Setup Pages
57 | uses: actions/configure-pages@v5
58 | with:
59 | # Automatically inject basePath in your Next.js configuration file and disable
60 | # server side image optimization (https://nextjs.org/docs/api-reference/next/image#unoptimized).
61 | #
62 | # You may remove this line if you want to manage the configuration yourself.
63 | static_site_generator: next
64 | - name: Restore cache
65 | uses: actions/cache@v4
66 | with:
67 | path: |
68 | .next/cache
69 | # Generate a new cache whenever packages or source files change.
70 | key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }}
71 | # If source files changed but packages didn't, rebuild from a prior cache.
72 | restore-keys: |
73 | ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-
74 | - name: Install dependencies
75 | run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }}
76 | - name: Build with Next.js
77 | run: ${{ steps.detect-package-manager.outputs.runner }} next build
78 | - name: Upload artifact
79 | uses: actions/upload-pages-artifact@v3
80 | with:
81 | path: ./out
82 |
83 | # Deployment job
84 | deploy:
85 | environment:
86 | name: github-pages
87 | url: ${{ steps.deployment.outputs.page_url }}
88 | runs-on: ubuntu-latest
89 | needs: build
90 | steps:
91 | - name: Deploy to GitHub Pages
92 | id: deployment
93 | uses: actions/deploy-pages@v4
94 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | **/.git
2 | **/node_modules
3 | **/package.json
4 | **/package-lock.json
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "editorconfig": true,
3 | "singleQuote": false,
4 | "jsxSingleQuote": false,
5 | "trailingComma": "all",
6 | "quoteProps": "as-needed"
7 | }
8 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "scss.lint.unknownAtRules": "ignore",
3 | "css.lint.unknownAtRules": "ignore",
4 | "files.associations": {
5 | "*.css": "tailwindcss"
6 | },
7 | "css.customData": [".vscode/tailwind.json"]
8 | }
9 |
--------------------------------------------------------------------------------
/.vscode/tailwind.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1.1,
3 | "atDirectives": [
4 | {
5 | "name": "@tailwind",
6 | "description": "Use the `@tailwind` directive to insert Tailwind's `base`, `components`, `utilities` and `screens` styles into your CSS.",
7 | "references": [
8 | {
9 | "name": "Tailwind Documentation",
10 | "url": "https://tailwindcss.com/docs/functions-and-directives#tailwind"
11 | }
12 | ]
13 | },
14 | {
15 | "name": "@apply",
16 | "description": "Use the `@apply` directive to inline any existing utility classes into your own custom CSS. This is useful when you find a common utility pattern in your HTML that you’d like to extract to a new component.",
17 | "references": [
18 | {
19 | "name": "Tailwind Documentation",
20 | "url": "https://tailwindcss.com/docs/functions-and-directives#apply"
21 | }
22 | ]
23 | },
24 | {
25 | "name": "@responsive",
26 | "description": "You can generate responsive variants of your own classes by wrapping their definitions in the `@responsive` directive:\n```css\n@responsive {\n .alert {\n background-color: #E53E3E;\n }\n}\n```\n",
27 | "references": [
28 | {
29 | "name": "Tailwind Documentation",
30 | "url": "https://tailwindcss.com/docs/functions-and-directives#responsive"
31 | }
32 | ]
33 | },
34 | {
35 | "name": "@screen",
36 | "description": "The `@screen` directive allows you to create media queries that reference your breakpoints by **name** instead of duplicating their values in your own CSS:\n```css\n@screen sm {\n /* ... */\n}\n```\n…gets transformed into this:\n```css\n@media (min-width: 640px) {\n /* ... */\n}\n```\n",
37 | "references": [
38 | {
39 | "name": "Tailwind Documentation",
40 | "url": "https://tailwindcss.com/docs/functions-and-directives#screen"
41 | }
42 | ]
43 | },
44 | {
45 | "name": "@variants",
46 | "description": "Generate `hover`, `focus`, `active` and other **variants** of your own utilities by wrapping their definitions in the `@variants` directive:\n```css\n@variants hover, focus {\n .btn-brand {\n background-color: #3182CE;\n }\n}\n```\n",
47 | "references": [
48 | {
49 | "name": "Tailwind Documentation",
50 | "url": "https://tailwindcss.com/docs/functions-and-directives#variants"
51 | }
52 | ]
53 | }
54 | ]
55 | }
56 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AI SEO
2 |
3 |
4 |
5 | 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).
6 |
7 | ## Features
8 |
9 | - Modern, dark design
10 | - Animated with Framer Motion
11 | - Responsive layout
12 | - SEO-optimized
13 | - Cross-Browser Compatibility
14 | - Accessibility
15 | - Easy to Customize
16 |
17 | ## Getting Started
18 |
19 | First, To install the project dependencies, run:
20 |
21 | ```bash
22 | npm install
23 | # or
24 | yarn install
25 | # or
26 | pnpm install
27 | # or
28 | bun install
29 | ```
30 |
31 | ## Usage
32 |
33 | After installation, you can start the development server:
34 |
35 | ```bash
36 | npm run dev
37 | # or
38 | yarn dev
39 | # or
40 | pnpm dev
41 | # or
42 | bun dev
43 | ```
44 |
45 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
46 |
47 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
48 |
49 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load DM_Sans, a custom Google Font.
50 |
51 | ## Design
52 |
53 | You can view the design for this project on Figma: [AI Startup Landing Page](https://www.figma.com/design/XpZQogjrnUSWYceMccagvu/AI-Startup-Landing-Page?node-id=4007-684&t=7L89agO6vZJulren-1)
54 |
55 | ## Contributing
56 |
57 | Contributions are welcome! Please read the [contributing guidelines](CONTRIBUTING.md) first.
58 |
59 | ## License
60 |
61 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
62 |
--------------------------------------------------------------------------------
/Screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/faridvatani/ai-seo/cbe2a5060e5b442024be778e067c70d075ab8b39/Screenshot.png
--------------------------------------------------------------------------------
/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 |
3 | const nextConfig = {
4 | output: "standalone",
5 | webpack(config) {
6 | // Grab the existing rule that handles SVG imports
7 | const fileLoaderRule = config.module.rules.find((rule) =>
8 | rule.test?.test?.(".svg"),
9 | );
10 |
11 | config.module.rules.push(
12 | // Reapply the existing rule, but only for svg imports ending in ?url
13 | {
14 | ...fileLoaderRule,
15 | test: /\.svg$/i,
16 | resourceQuery: /url/, // *.svg?url
17 | },
18 | // Convert all other *.svg imports to React components
19 | {
20 | test: /\.svg$/i,
21 | issuer: fileLoaderRule.issuer,
22 | resourceQuery: { not: [...fileLoaderRule.resourceQuery.not, /url/] }, // exclude if *.svg?url
23 | use: ["@svgr/webpack"],
24 | },
25 | );
26 |
27 | // Modify the file loader rule to ignore *.svg, since we have it handled now.
28 | fileLoaderRule.exclude = /\.svg$/i;
29 |
30 | return config;
31 | },
32 | };
33 |
34 | export default nextConfig;
35 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ai-seo",
3 | "version": "1.0.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 | "@dotlottie/react-player": "^1.6.19",
13 | "@lottiefiles/dotlottie-react": "^0.8.7",
14 | "framer-motion": "^11.3.28",
15 | "next": "14.2.5",
16 | "react": "^18",
17 | "react-dom": "^18",
18 | "sharp": "^0.33.4"
19 | },
20 | "devDependencies": {
21 | "@svgr/webpack": "^8.1.0",
22 | "@types/node": "^20",
23 | "@types/react": "^18",
24 | "@types/react-dom": "^18",
25 | "eslint": "^8",
26 | "eslint-config-next": "14.2.5",
27 | "postcss": "^8",
28 | "tailwind-merge": "^2.5.2",
29 | "tailwindcss": "^3.4.1",
30 | "typescript": "^5"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/public/assets/lottie/click.lottie:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/faridvatani/ai-seo/cbe2a5060e5b442024be778e067c70d075ab8b39/public/assets/lottie/click.lottie
--------------------------------------------------------------------------------
/public/assets/lottie/stars.lottie:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/faridvatani/ai-seo/cbe2a5060e5b442024be778e067c70d075ab8b39/public/assets/lottie/stars.lottie
--------------------------------------------------------------------------------
/public/assets/lottie/vroom.lottie:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/faridvatani/ai-seo/cbe2a5060e5b442024be778e067c70d075ab8b39/public/assets/lottie/vroom.lottie
--------------------------------------------------------------------------------
/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/faridvatani/ai-seo/cbe2a5060e5b442024be778e067c70d075ab8b39/src/app/favicon.ico
--------------------------------------------------------------------------------
/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import type { Metadata } from "next";
3 | import { Inter } from "next/font/google";
4 | import { twMerge } from "tailwind-merge";
5 | import "@/styles/globals.css";
6 |
7 | const inter = Inter({ subsets: ["latin"] });
8 |
9 | export const metadata: Metadata = {
10 | title: "AI SEO | AI Startup Landing Page",
11 | description: "A landing page for an AI startup",
12 | };
13 |
14 | export default function RootLayout({
15 | children,
16 | }: Readonly<{
17 | children: React.ReactNode;
18 | }>) {
19 | return (
20 |
21 |
25 | {children}
26 |
27 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/src/app/page.tsx:
--------------------------------------------------------------------------------
1 | import { CallToAction } from "@/sections/CallToAction";
2 | import { Features } from "@/sections/Features";
3 | import { Footer } from "@/sections/Footer";
4 | import { Header } from "@/sections/Header";
5 | import { Hero } from "@/sections/Hero";
6 | import { LogoTicker } from "@/sections/LogoTicker";
7 | import { Testimonials } from "@/sections/Testimonials";
8 |
9 | export default function Home() {
10 | return (
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/src/assets/avatar-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/faridvatani/ai-seo/cbe2a5060e5b442024be778e067c70d075ab8b39/src/assets/avatar-1.png
--------------------------------------------------------------------------------
/src/assets/avatar-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/faridvatani/ai-seo/cbe2a5060e5b442024be778e067c70d075ab8b39/src/assets/avatar-2.png
--------------------------------------------------------------------------------
/src/assets/avatar-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/faridvatani/ai-seo/cbe2a5060e5b442024be778e067c70d075ab8b39/src/assets/avatar-3.png
--------------------------------------------------------------------------------
/src/assets/avatar-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/faridvatani/ai-seo/cbe2a5060e5b442024be778e067c70d075ab8b39/src/assets/avatar-4.png
--------------------------------------------------------------------------------
/src/assets/grid-lines.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/faridvatani/ai-seo/cbe2a5060e5b442024be778e067c70d075ab8b39/src/assets/grid-lines.png
--------------------------------------------------------------------------------
/src/assets/icon-click.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icon-guage.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icon-menu.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/icon-stars.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Avatar1 } from "@/assets/avatar-1.png";
2 | export { default as Avatar2 } from "@/assets/avatar-2.png";
3 | export { default as Avatar3 } from "@/assets/avatar-3.png";
4 | export { default as Avatar4 } from "@/assets/avatar-4.png";
5 | export { default as GridLines } from "@/assets/grid-lines.png";
6 | export { default as ClickIcon } from "@/assets/icon-click.svg";
7 | export { default as GuageIcon } from "@/assets/icon-guage.svg";
8 | export { default as MenuIcon } from "@/assets/icon-menu.svg";
9 | export { default as StarsIcon } from "@/assets/icon-stars.svg";
10 | export { default as AcmeLogo } from "@/assets/logo-acme.png";
11 | export { default as ApexLogo } from "@/assets/logo-apex.png";
12 | export { default as CelestialLogo } from "@/assets/logo-celestial.png";
13 | export { default as EchoLogo } from "@/assets/logo-echo.png";
14 | export { default as PulseLogo } from "@/assets/logo-pulse.png";
15 | export { default as QuantumLogo } from "@/assets/logo-quantum.png";
16 | export { default as Logo } from "@/assets/logo.svg";
17 | export { default as ProductImage } from "@/assets/product-image.png";
18 | export { default as SocialInstagram } from "@/assets/social-instagram.svg";
19 | export { default as SocialX } from "@/assets/social-x.svg";
20 | export { default as SocialYoutube } from "@/assets/social-youtube.svg";
21 | export { default as StarsImage } from "@/assets/stars.png";
22 |
--------------------------------------------------------------------------------
/src/assets/logo-acme.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/faridvatani/ai-seo/cbe2a5060e5b442024be778e067c70d075ab8b39/src/assets/logo-acme.png
--------------------------------------------------------------------------------
/src/assets/logo-apex.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/faridvatani/ai-seo/cbe2a5060e5b442024be778e067c70d075ab8b39/src/assets/logo-apex.png
--------------------------------------------------------------------------------
/src/assets/logo-celestial.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/faridvatani/ai-seo/cbe2a5060e5b442024be778e067c70d075ab8b39/src/assets/logo-celestial.png
--------------------------------------------------------------------------------
/src/assets/logo-echo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/faridvatani/ai-seo/cbe2a5060e5b442024be778e067c70d075ab8b39/src/assets/logo-echo.png
--------------------------------------------------------------------------------
/src/assets/logo-pulse.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/faridvatani/ai-seo/cbe2a5060e5b442024be778e067c70d075ab8b39/src/assets/logo-pulse.png
--------------------------------------------------------------------------------
/src/assets/logo-quantum.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/faridvatani/ai-seo/cbe2a5060e5b442024be778e067c70d075ab8b39/src/assets/logo-quantum.png
--------------------------------------------------------------------------------
/src/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/product-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/faridvatani/ai-seo/cbe2a5060e5b442024be778e067c70d075ab8b39/src/assets/product-image.png
--------------------------------------------------------------------------------
/src/assets/social-instagram.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/src/assets/social-x.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/social-youtube.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/stars.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/faridvatani/ai-seo/cbe2a5060e5b442024be778e067c70d075ab8b39/src/assets/stars.png
--------------------------------------------------------------------------------
/src/components/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/faridvatani/ai-seo/cbe2a5060e5b442024be778e067c70d075ab8b39/src/components/.gitkeep
--------------------------------------------------------------------------------
/src/components/Button.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC, PropsWithChildren } from "react";
2 | import { twMerge } from "tailwind-merge";
3 |
4 | interface ButtonProps {
5 | classname?: string;
6 | }
7 |
8 | export const Button: FC> = ({
9 | children,
10 | classname,
11 | }) => {
12 | return (
13 |
27 | );
28 | };
29 |
--------------------------------------------------------------------------------
/src/components/FeatureTab.tsx:
--------------------------------------------------------------------------------
1 | import { useRef, useEffect } from "react";
2 | import {
3 | DotLottieCommonPlayer,
4 | DotLottiePlayer,
5 | } from "@dotlottie/react-player";
6 | import {
7 | animate,
8 | motion,
9 | useMotionTemplate,
10 | useMotionValue,
11 | ValueAnimationTransition,
12 | } from "framer-motion";
13 | import { ComponentPropsWithoutRef } from "react";
14 | import { featuresTabs } from "@/data/data";
15 |
16 | const FeatureTab = (
17 | props: (typeof featuresTabs)[number] &
18 | ComponentPropsWithoutRef<"div"> & { selected: boolean },
19 | ) => {
20 | const tabRef = useRef(null);
21 | const dotLottieRef = useRef(null);
22 | const xPercentage = useMotionValue(0);
23 | const yPercentage = useMotionValue(0);
24 | const maskImage = useMotionTemplate`radial-gradient(80px 80px at ${xPercentage}% ${yPercentage}%,black,transparent)`;
25 |
26 | useEffect(() => {
27 | if (!tabRef.current || !props.selected) {
28 | return;
29 | }
30 | xPercentage.set(0);
31 | yPercentage.set(0);
32 |
33 | const { height, width } = tabRef.current.getBoundingClientRect();
34 | const circumference = height * 2 + width * 2;
35 |
36 | const times = [
37 | 0,
38 | width / circumference,
39 | (width + height) / circumference,
40 | (width * 2 - height) / circumference,
41 | 1,
42 | ];
43 |
44 | const options: ValueAnimationTransition = {
45 | times,
46 | duration: 4,
47 | repeat: Infinity,
48 | ease: "linear",
49 | repeatType: "loop",
50 | };
51 |
52 | animate(xPercentage, [0, 100, 100, 0, 0], options);
53 | animate(yPercentage, [0, 0, 100, 100, 0], options);
54 | }, [props.selected, xPercentage, yPercentage]);
55 |
56 | const handleTabHover = () => {
57 | if (dotLottieRef.current === null) {
58 | return;
59 | }
60 | dotLottieRef.current.seek(0);
61 | dotLottieRef.current?.play();
62 | };
63 |
64 | return (
65 |
71 | {props.selected && (
72 |
78 | )}
79 |
80 |
86 |
87 |
{props.title}
88 | {props.isNew && (
89 |
90 | new
91 |
92 | )}
93 |
94 | );
95 | };
96 |
97 | export default FeatureTab;
98 |
--------------------------------------------------------------------------------
/src/data/data.ts:
--------------------------------------------------------------------------------
1 | import { Avatar1, Avatar2, Avatar3, Avatar4 } from "@/assets";
2 |
3 | export const featuresTabs = [
4 | {
5 | icon: "./assets/lottie/vroom.lottie",
6 | title: "User-friendly dashboard",
7 | isNew: false,
8 | backgroundPositionX: 0,
9 | backgroundPositionY: 0,
10 | backgroundSizeX: 150,
11 | },
12 | {
13 | icon: "./assets/lottie/click.lottie",
14 | title: "One-click optimization",
15 | isNew: false,
16 | backgroundPositionX: 98,
17 | backgroundPositionY: 100,
18 | backgroundSizeX: 135,
19 | },
20 | {
21 | icon: "./assets/lottie/stars.lottie",
22 | title: "Smart keyword generator",
23 | isNew: true,
24 | backgroundPositionX: 100,
25 | backgroundPositionY: 27,
26 | backgroundSizeX: 177,
27 | },
28 | ];
29 |
30 | export const testimonials = [
31 | {
32 | text: "“This product has completely transformed how I manage my projects and deadlines”",
33 | name: "Sophia Perez",
34 | title: "Director @ Quantum",
35 | avatarImg: Avatar1,
36 | },
37 | {
38 | text: "“These AI tools have completely revolutionized our SEO entire strategy overnight”",
39 | name: "Jamie Lee",
40 | title: "Founder @ Pulse",
41 | avatarImg: Avatar2,
42 | },
43 | {
44 | text: "“The user interface is so intuitive and easy to use, it has saved us countless hours”",
45 | name: "Alisa Hester",
46 | title: "Product @ Innovate",
47 | avatarImg: Avatar3,
48 | },
49 | {
50 | text: "“Our team's productivity has increased significantly since we started using this tool”",
51 | name: "Alec Whitten",
52 | title: "CTO @ Tech Solutions",
53 | avatarImg: Avatar4,
54 | },
55 | ];
56 |
--------------------------------------------------------------------------------
/src/hooks/useRelativeMousePosition.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, RefObject } from "react";
2 | import { useMotionValue } from "framer-motion";
3 |
4 | const useRelativeMousePosition = (to: RefObject) => {
5 | const mouseX = useMotionValue(0);
6 | const mouseY = useMotionValue(0);
7 |
8 | const updateMousePosition = (event: MouseEvent) => {
9 | if (!to.current) {
10 | return;
11 | }
12 | const { top, left } = to.current.getBoundingClientRect();
13 | mouseX.set(event.x - left);
14 | mouseY.set(event.y - top);
15 | };
16 |
17 | useEffect(() => {
18 | window.addEventListener("mousemove", updateMousePosition);
19 |
20 | return () => {
21 | window.removeEventListener("mousemove", updateMousePosition);
22 | };
23 | }, []);
24 |
25 | return [mouseX, mouseY];
26 | };
27 |
28 | export default useRelativeMousePosition;
29 |
--------------------------------------------------------------------------------
/src/sections/CallToAction.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { Button } from "@/components/Button";
3 | import { StarsImage, GridLines } from "@/assets";
4 | import {
5 | motion,
6 | useMotionTemplate,
7 | useScroll,
8 | useTransform,
9 | } from "framer-motion";
10 | import { useRef } from "react";
11 | import useRelativeMousePosition from "@/hooks/useRelativeMousePosition";
12 |
13 | export const CallToAction = () => {
14 | const sectionRef = useRef(null);
15 | const borderedDivRef = useRef(null);
16 | const { scrollYProgress } = useScroll({
17 | target: sectionRef,
18 | offset: ["start end", "end start"],
19 | });
20 |
21 | const backgroundPositionY = useTransform(
22 | scrollYProgress,
23 | [0, 1],
24 | [-300, 300],
25 | );
26 |
27 | const [mouseX, mouseY] = useRelativeMousePosition(borderedDivRef);
28 | const maskImage = useMotionTemplate`radial-gradient(50% 50% at ${mouseX}px ${mouseY}px, black, transparent)`;
29 |
30 | return (
31 |
32 |
33 |
49 |
55 |
62 |
63 |
64 | AI-driven SEO for everyone.
65 |
66 |
67 | Achieve clear, impactful results without the complexity.
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | );
77 | };
78 |
--------------------------------------------------------------------------------
/src/sections/Features.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { ProductImage } from "@/assets";
3 | import { featuresTabs } from "@/data/data";
4 | import {
5 | animate,
6 | motion,
7 | useMotionTemplate,
8 | useMotionValue,
9 | ValueAnimationTransition,
10 | } from "framer-motion";
11 | import { useState } from "react";
12 | import FeatureTab from "@/components/FeatureTab";
13 |
14 | export const Features = () => {
15 | const [currentTab, setCurrentTab] = useState(0);
16 | const backgroundPositionX = useMotionValue(
17 | featuresTabs[0].backgroundPositionX,
18 | );
19 | const backgroundPositionY = useMotionValue(
20 | featuresTabs[0].backgroundPositionY,
21 | );
22 | const backgroundSizeX = useMotionValue(featuresTabs[0].backgroundSizeX);
23 |
24 | const backgroundPosition = useMotionTemplate`${backgroundPositionX}% ${backgroundPositionY}%`;
25 | const backgroundSize = useMotionTemplate`${backgroundSizeX}% auto`;
26 |
27 | const handleSelectTab = (tabIndex: number) => {
28 | setCurrentTab(tabIndex);
29 |
30 | const animateOptions: ValueAnimationTransition = {
31 | duration: 2,
32 | ease: "easeInOut",
33 | };
34 |
35 | animate(
36 | backgroundSizeX,
37 | [backgroundSizeX.get(), 100, featuresTabs[tabIndex].backgroundSizeX],
38 | animateOptions,
39 | );
40 |
41 | animate(
42 | backgroundPositionX,
43 | [backgroundPositionX.get(), featuresTabs[tabIndex].backgroundPositionX],
44 | animateOptions,
45 | );
46 |
47 | animate(
48 | backgroundPositionY,
49 | [backgroundPositionY.get(), featuresTabs[tabIndex].backgroundPositionY],
50 | animateOptions,
51 | );
52 | };
53 |
54 | return (
55 |
56 |
57 |
58 | Elevate your SEO efforts.
59 |
60 |
61 | From small startups to large enterprises, our AI-driven tool has
62 | revolutionized the way businesses approach SEO.
63 |
64 |
65 | {featuresTabs.map((tab, tabIndex) => (
66 | handleSelectTab(tabIndex)}
70 | selected={currentTab === tabIndex}
71 | />
72 | ))}
73 |
74 |
75 |
83 |
84 |
85 |
86 | );
87 | };
88 |
--------------------------------------------------------------------------------
/src/sections/Footer.tsx:
--------------------------------------------------------------------------------
1 | import { Logo, SocialX, SocialInstagram, SocialYoutube } from "@/assets";
2 | import Link from "next/link";
3 |
4 | const navItems = [
5 | { href: "#", title: "Features" },
6 | { href: "#", title: "Developers" },
7 | { href: "#", title: "Company" },
8 | { href: "#", title: "Blog" },
9 | { href: "#", title: "Changelog" },
10 | ];
11 |
12 | export const Footer = () => {
13 | return (
14 |
40 | );
41 | };
42 |
--------------------------------------------------------------------------------
/src/sections/Header.tsx:
--------------------------------------------------------------------------------
1 | import { Logo, MenuIcon } from "@/assets";
2 | import { Button } from "@/components/Button";
3 | import Link from "next/link";
4 |
5 | const navItems = [
6 | { href: "#", title: "Features" },
7 | { href: "#", title: "Developers" },
8 | { href: "#", title: "Pricing" },
9 | { href: "#", title: "Changelog" },
10 | ];
11 |
12 | export const Header = () => {
13 | return (
14 |
15 |
16 |
17 |
18 |
19 |
24 |
25 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | );
45 | };
46 |
--------------------------------------------------------------------------------
/src/sections/Hero.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { Button } from "@/components/Button";
3 | import { StarsImage } from "@/assets";
4 | import { motion, useScroll, useTransform } from "framer-motion";
5 | import { useRef } from "react";
6 |
7 | export const Hero = () => {
8 | const sectionRef = useRef(null);
9 | const { scrollYProgress } = useScroll({
10 | target: sectionRef,
11 | offset: ["start end", "end start"],
12 | });
13 |
14 | const backgroundPositionY = useTransform(
15 | scrollYProgress,
16 | [0, 1],
17 | [-300, 300],
18 | );
19 |
20 | return (
21 |
34 |
35 | {/* Start Planet */}
36 |
37 | {/* End Planet */}
38 | {/* Start Ring 1 */}
39 |
54 |
55 |
56 |
59 |
60 | {/* End Ring 1 */}
61 | {/* Start Ring 2 */}
62 |
77 | {/* End Ring 2 */}
78 | {/* Start Ring 3 */}
79 |
94 |
95 |
96 |
97 | {/* End Ring 3 */}
98 |
99 |
100 | AI SEO
101 |
102 |
103 | Elevate your site's visibility effortlessly with AI, where smart
104 | technology meets user-friendly SEO tools.
105 |
106 |
107 |
108 |
109 |
110 |
111 | );
112 | };
113 |
--------------------------------------------------------------------------------
/src/sections/LogoTicker.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import {
3 | AcmeLogo,
4 | ApexLogo,
5 | CelestialLogo,
6 | QuantumLogo,
7 | PulseLogo,
8 | EchoLogo,
9 | } from "@/assets";
10 | import { motion } from "framer-motion";
11 | import Image from "next/image";
12 |
13 | const logos = [
14 | AcmeLogo,
15 | PulseLogo,
16 | EchoLogo,
17 | CelestialLogo,
18 | ApexLogo,
19 | QuantumLogo,
20 | ];
21 |
22 | export const LogoTicker = () => {
23 | return (
24 |
25 |
26 |
27 |
28 |
Trusted by top innovative teams
29 |
30 |
31 |
41 | {[...logos, ...logos].map((logo, index) => (
42 |
50 | ))}
51 |
52 |
53 |
54 |
55 |
56 | );
57 | };
58 |
--------------------------------------------------------------------------------
/src/sections/Testimonials.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { testimonials } from "@/data/data";
3 | import { motion } from "framer-motion";
4 | import Image from "next/image";
5 |
6 | export const Testimonials = () => {
7 | return (
8 |
9 |
10 |
11 | Beyond Expectations.
12 |
13 |
14 | Our revolutionary AI SEO tools have transformed our clients'
15 | strategies.
16 |
17 |
18 |
32 | {[...testimonials, ...testimonials].map((testimonial, index) => (
33 |
37 |
38 | {testimonial.text}
39 |
40 |
41 |
42 |
47 |
48 |
49 | {testimonial.name}
50 |
51 | {testimonial.title}
52 |
53 |
54 |
55 |
56 | ))}
57 |
58 |
59 |
60 |
61 | );
62 | };
63 |
--------------------------------------------------------------------------------
/src/styles/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss";
2 |
3 | const config: Config = {
4 | content: [
5 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./src/sections/**/*.{js,ts,jsx,tsx,mdx}",
7 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
8 | ],
9 | theme: {
10 | container: {
11 | center: true,
12 | padding: {
13 | DEFAULT: "20px",
14 | lg: "80px",
15 | },
16 | screens: {
17 | sm: "375px",
18 | md: "768px",
19 | lg: "1200px",
20 | },
21 | },
22 | screens: {
23 | sm: "375px",
24 | md: "768px",
25 | lg: "1200px",
26 | },
27 | extend: {},
28 | },
29 | plugins: [],
30 | };
31 | export default config;
32 |
--------------------------------------------------------------------------------
/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 | "@/*": ["./src/*"]
22 | }
23 | },
24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
25 | "exclude": ["node_modules"]
26 | }
27 |
--------------------------------------------------------------------------------