├── .editorconfig ├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── .prettierignore ├── .vscode └── settings.json ├── README.md ├── app ├── favicon.ico ├── layout.tsx └── page.tsx ├── components.json ├── components ├── button.tsx ├── cards-section.tsx ├── container.tsx ├── content-wrapper.tsx ├── download-section.tsx ├── footer-list.tsx ├── footer.tsx ├── hero-section.tsx ├── how-it-works.tsx ├── icons.tsx ├── leading-title.tsx ├── list-item.tsx ├── logo.tsx ├── main-nav.tsx ├── navbar.tsx ├── price-card.tsx ├── price-section.tsx ├── switch.tsx ├── testimonial-card.tsx └── testimonial-section.tsx ├── lib ├── animations.ts ├── content.ts └── utils.ts ├── next-env.d.ts ├── next.config.mjs ├── package-lock.json ├── package.json ├── postcss.config.js ├── prettier.config.js ├── public ├── avatar-1.png ├── avatar-2.png ├── avatar-3.png ├── card-image-1.png ├── card-image-1.svg ├── card-image-2.svg ├── card-image-3.svg ├── checkmark.svg ├── cross.svg ├── download.png ├── favicon.ico ├── image.png ├── next.svg ├── plans │ ├── free.svg │ ├── pro.svg │ └── ultimate.svg ├── thirteen.svg ├── vercel.svg ├── w.png ├── with-spendin.svg └── without-spendin.svg ├── styles └── globals.css ├── tailwind.config.js ├── tsconfig.json └── tsconfig.tsbuildinfo /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 2 8 | indent_style = space 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/* 2 | .cache 3 | public 4 | node_modules 5 | *.esm.js 6 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/eslintrc", 3 | "root": true, 4 | "extends": [ 5 | "next/core-web-vitals", 6 | "prettier", 7 | "plugin:tailwindcss/recommended" 8 | ], 9 | "plugins": ["tailwindcss"], 10 | "rules": { 11 | "@next/next/no-html-link-for-pages": "off", 12 | "react/jsx-key": "off", 13 | "tailwindcss/no-custom-classname": "off" 14 | }, 15 | "settings": { 16 | "tailwindcss": { 17 | "callees": ["cn"], 18 | "config": "tailwind.config.js" 19 | }, 20 | "next": { 21 | "rootDir": ["./"] 22 | } 23 | }, 24 | "overrides": [ 25 | { 26 | "files": ["*.ts", "*.tsx"], 27 | "parser": "@typescript-eslint/parser" 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /.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 | 8 | # testing 9 | coverage 10 | 11 | # next.js 12 | .next/ 13 | out/ 14 | build 15 | 16 | # misc 17 | .DS_Store 18 | *.pem 19 | 20 | # debug 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | .pnpm-debug.log* 25 | 26 | # local env files 27 | .env.local 28 | .env.development.local 29 | .env.test.local 30 | .env.production.local 31 | 32 | # turbo 33 | .turbo 34 | 35 | .contentlayer 36 | .env -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | cache 2 | .cache 3 | package.json 4 | package-lock.json 5 | public 6 | CHANGELOG.md 7 | .yarn 8 | dist 9 | node_modules 10 | .next 11 | build 12 | .contentlayer -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "../../node_modules/.pnpm/typescript@4.9.5/node_modules/typescript/lib", 3 | "typescript.enablePromptUseWorkspaceTsdk": true 4 | } 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Next.js Landing Page Starter Kit 2 | 3 | A starter kit for building landing pages using Next.js 14, Tailwind CSS with TypeScript, and Framer Motion. 4 | 5 | ## Features 6 | 7 | - [Next.js 14 with App Router](http://localhost:3000) 8 | - [Tailwind CSS with well-structured config](https://tailwindcss.com) 9 | - [TypeScript](https://www.typescriptlang.org) 10 | - [Framer Motion](https://framer.com/motion) 11 | - [Class Variance Authority](https://cva.style/docs) 12 | 13 | ## Usage 14 | 15 | To get started with this starter kit, you can clone the repository and install the dependencies: 16 | 17 | ```bash 18 | git clone https://github.com/eronred/landing-starter-kit-nextjs-tailwind.git 19 | npm install 20 | ``` 21 | 22 | Then, you can start the development server: 23 | 24 | ```bash 25 | npm run dev 26 | ``` 27 | 28 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 29 | 30 | ## Contributing 31 | 32 | Contributions are welcome! Please feel free to submit a pull request. 33 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eronred/landing-starter-kit-nextjs-tailwind/b145b97098612dcc0bcea98076b75507e58193e5/app/favicon.ico -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import "@/styles/globals.css" 2 | import { cn } from "@/lib/utils" 3 | import { MainNav } from "@/components/main-nav" 4 | import { Plus_Jakarta_Sans } from 'next/font/google' 5 | import type { Metadata } from 'next' 6 | 7 | export const metadata: Metadata = { 8 | title: "Spend.In", 9 | description: "Spend.In is a platform that helps you track your business expenses and manage your finances.", 10 | } 11 | 12 | interface RootLayoutProps { 13 | children: React.ReactNode 14 | } 15 | 16 | const jakartaFont = Plus_Jakarta_Sans({ subsets: ['latin'] }) 17 | 18 | 19 | export default function RootLayout({ children }: RootLayoutProps) { 20 | return ( 21 | <> 22 | 24 | 25 | 29 |
30 | 31 |
{children}
32 |
33 | 34 | 35 | 36 | ) 37 | } 38 | -------------------------------------------------------------------------------- /app/page.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import Footer from "@/components/footer" 3 | import HeroSection from "@/components/hero-section" 4 | import TestimonialSection from "@/components/testimonial-section" 5 | import DownloadSection from "@/components/download-section" 6 | import CardsSection from "@/components/cards-section" 7 | import PriceSection from "@/components/price-section" 8 | import HowItWorks from "@/components/how-it-works" 9 | 10 | export default function IndexPage() { 11 | 12 | return ( 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 |
22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "tailwind": { 5 | "config": "tailwind.config.js", 6 | "css": "app/globals.css", 7 | "baseColor": "slate", 8 | "cssVariables": true 9 | }, 10 | "rsc": false, 11 | "aliases": { 12 | "utils": "@/lib/utils", 13 | "components": "@/components" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /components/button.tsx: -------------------------------------------------------------------------------- 1 | import { cva, VariantProps } from "class-variance-authority"; 2 | import Link from "next/link"; 3 | 4 | type ButtonBaseProps = VariantProps 5 | 6 | type ButtonProps = ButtonBaseProps & { 7 | label: string; 8 | href?: string; 9 | className?: string; 10 | }; 11 | 12 | 13 | const buttonClasses = cva( 14 | "w-full flex flex-row justify-center items-center text-center whitespace-nowrap sm:w-fit sm:px-8 py-3 text-lg rounded-full disabled:cursor-not-allowed disabled:opacity-50 focus:scale-95 transition-transform", 15 | { 16 | variants: { 17 | variant: { 18 | primary: [ 19 | " bg-primary text-white hover:bg-opacity-80 transition-colors", 20 | ], 21 | secondary: [ 22 | "bg-secondary text-text-light hover:bg-opacity-80 transition-colors", 23 | ], 24 | }, 25 | size: { 26 | small: "text-xs px-3 h-7", 27 | medium: "text-sm px-4 h-8", 28 | nav: "text-[16px] px-4 h-10", 29 | large: "text-md px-6 h-12", 30 | }, 31 | }, 32 | defaultVariants: { 33 | variant: "primary", 34 | size: "medium", 35 | }, 36 | }); 37 | 38 | export const Button = ({ label, href, variant, size, ...props }: ButtonProps) => { 39 | const classes = buttonClasses({ variant, size, className: props.className }); 40 | 41 | if (href) { 42 | return ( 43 | 48 | {label} 49 | 50 | ); 51 | } 52 | 53 | return ( 54 | 59 | ); 60 | }; 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /components/cards-section.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Container from './container' 3 | import LeadingTitle from './leading-title' 4 | import { motion } from 'framer-motion' 5 | 6 | const cards = [ 7 | { 8 | title: "Automatic Invoice Payment", 9 | description: 10 | "No need to pay manually, we provide automatic invoice payment service! Set a payment schedule and you're done, it's that easy!", 11 | image: "/card-image-1.svg", 12 | }, 13 | { 14 | title: "Clear payment history", 15 | description: 16 | "Still writing manual expenses? Our platform breaks down every expense you log down to the millisecond!", 17 | image: "/card-image-2.svg", 18 | }, 19 | { 20 | title: "Use of multi-card payments", 21 | description: 22 | "Have more than 1 bank account or credit/debit card? Our platform is already integrated with many banks around the world, for easier payments!", 23 | image: "/card-image-3.svg", 24 | }, 25 | ] 26 | 27 | export default function CardsSection() { 28 | return ( 29 |
30 | 31 | 35 |
36 |
37 | 38 |

39 | Easy, Simple, Affordable 40 |

41 |
42 |

43 | Our platform helps your business in managing expenses. These are 44 | some of the reasons why you should use our platform in managing 45 | business finances. 46 |

47 |
48 |
51 | {cards.map((_, index) => ( 52 |
53 |
54 |
55 | 60 |
61 | 62 |
63 |
64 |

65 | {_.title} 66 |

67 |

68 | {_.description} 69 |

70 |
71 |
72 | ))} 73 |
74 |
75 |
76 |
) 77 | } 78 | -------------------------------------------------------------------------------- /components/container.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | interface ContainerProps { 4 | children: React.ReactNode 5 | bgColor?: string 6 | } 7 | 8 | 9 | export default function Container({ children, bgColor }: ContainerProps) { 10 | return ( 11 |
14 |
15 | {children} 16 |
17 | 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /components/content-wrapper.tsx: -------------------------------------------------------------------------------- 1 | 2 | import { useState } from 'react'; 3 | import ListItem from './list-item'; 4 | 5 | 6 | interface Features { 7 | with: string; 8 | without: string; 9 | } 10 | 11 | interface ContentWrapperProps { 12 | value: string; 13 | features: Features[]; 14 | } 15 | 16 | 17 | const ContentWrapper = ({ value = "withSpendIn", features }: ContentWrapperProps) => { 18 | 19 | const [selectedTab, setSelectedTab] = useState(value) 20 | const selectedTabStyle: string = 'bg-primary text-white' 21 | const unselectedTabStyle: string = 'bg-background-light text-black' 22 | return ( 23 |
24 |
25 |
26 | 35 | 43 |
44 |
45 |

46 | Taking too long to tidy up administrative files makes 47 | it unproductive 48 |

49 | <> 50 | {selectedTab === "withSpendIn" 51 | ? features.map((_, index) => ( 52 | 57 | )) 58 | : features.map((_, index) => ( 59 | 64 | ))} 65 | 66 |
67 |
68 |
69 | {selectedTab === "withSpendIn" ? ( 70 | 74 | 75 | ) : ( 76 | 77 | )} 78 |
79 |
80 | ); 81 | }; 82 | 83 | 84 | export default ContentWrapper; -------------------------------------------------------------------------------- /components/download-section.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Container from './container' 3 | import { Button } from './button' 4 | import LeadingTitle from './leading-title' 5 | import { backgroundColor } from '@/lib/utils' 6 | 7 | export default function DownloadSection() { 8 | return ( 9 |
10 | 11 |
12 |
13 |
14 | 15 |

16 | Start Track Your Business 17 | Expenses Today 18 |

19 |

20 | Are you ready to make your business more organized? Download Spend.In now! 21 |

22 |
28 |
29 |
30 | google-play 33 |
34 |
35 |
36 |
37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /components/footer-list.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link'; 2 | import React from 'react' 3 | 4 | interface UrlsProps { 5 | url: string; 6 | label: string; 7 | } 8 | 9 | interface FooterSectionProps { 10 | title: string; 11 | urls: UrlsProps[]; 12 | } 13 | 14 | export default function FooterSection( 15 | { title, urls }: FooterSectionProps 16 | ) { 17 | return ( 18 |
19 |

{title}

20 |
    21 | {urls.map((url, index) => ( 22 | 26 | {url.label} 27 | 28 | ))} 29 |
30 |
31 | ) 32 | } 33 | 34 | -------------------------------------------------------------------------------- /components/footer.tsx: -------------------------------------------------------------------------------- 1 | import { footerContent } from "@/lib/content"; 2 | import Container from "./container"; 3 | import FooterSection from "./footer-list"; 4 | import Logo from "./logo"; 5 | 6 | 7 | const Footer = () => { 8 | return ( 9 |
10 | 11 |
12 |
13 |
14 |
15 | 16 |

17 | Data visualization, and expense management for your business. 18 |

19 |
20 |
21 | { 22 | footerContent.map((section, index) => ( 23 | 24 | )) 25 | } 26 |
27 |
28 |
29 |
© EA 2024
30 | 35 |
36 |
37 |
38 |
39 |
40 | ); 41 | }; 42 | 43 | export default Footer; 44 | -------------------------------------------------------------------------------- /components/hero-section.tsx: -------------------------------------------------------------------------------- 1 | import { motion } from 'framer-motion' 2 | import React from 'react' 3 | import Container from './container' 4 | import { Button } from "./button" 5 | import { fadeInAnimationByIndex, moveInAnimationByIndex } from '@/lib/animations' 6 | 7 | export default function HeroSection() { 8 | 9 | return ( 10 |
13 | 14 |
15 | 21 | All your business expenses in one place 22 | 23 | 29 | Your one-stop finance empower platform. 30 | Manage all your business expenses with our supafast app. 31 | 32 | 37 | 38 |
62 |
63 |
) 64 | } 65 | -------------------------------------------------------------------------------- /components/how-it-works.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import React, { useState } from 'react' 4 | import Container from './container' 5 | import ContentWrapper from './content-wrapper' 6 | import LeadingTitle from './leading-title' 7 | 8 | export default function HowItWorks() { 9 | 10 | const spendInFeatures = [ 11 | { 12 | with: "Analyze your business cost easily with group transaction thorugh tagging feature.", 13 | without: 14 | "Complex recording process due to every administrative file in a different place.", 15 | }, 16 | { 17 | with: "Add more than one card for payment. Integrated with more than 50+ payment method.", 18 | without: 19 | "Need more effort to pay manually one by one invoice because there is no payment accommodation.", 20 | }, 21 | { 22 | with: "Arrange your business expenses by date, name, etc., with just one click.", 23 | without: 24 | "Manual data arranging needs a long time because the different months/years are not in the same place.", 25 | }, 26 | ] 27 | const [selectedTab] = useState("withSpendIn") 28 | 29 | 30 | return ( 31 |
32 | 33 |
35 |
36 |
37 | 39 |

40 | Reduce Time in Doing Manual Work Managing Expenses 41 |

42 |
43 |
44 | 48 |
49 |
50 |
51 | ) 52 | } 53 | -------------------------------------------------------------------------------- /components/icons.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | LucideProps, 3 | Moon, 4 | SunMedium, 5 | Twitter, 6 | type Icon as LucideIcon, 7 | } from "lucide-react" 8 | 9 | export type Icon = LucideIcon 10 | 11 | export const Icons = { 12 | sun: SunMedium, 13 | moon: Moon, 14 | twitter: Twitter, 15 | logo: (props: LucideProps) => ( 16 | 17 | 21 | 22 | ), 23 | gitHub: (props: LucideProps) => ( 24 | 25 | 29 | 30 | ), 31 | } 32 | -------------------------------------------------------------------------------- /components/leading-title.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | interface LeadingTitleProps { 4 | label: string 5 | } 6 | 7 | export default function LeadingTitle( 8 | { label }: LeadingTitleProps 9 | ) { 10 | return ( 11 |
12 | {label} 13 |
) 14 | } 15 | -------------------------------------------------------------------------------- /components/list-item.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | interface ListItemProps { 4 | type: "with" | "without" 5 | label: string 6 | } 7 | 8 | export default function ListItem({ type, label }: ListItemProps) { 9 | return ( 10 |
11 | 15 |

16 | {label} 17 |

18 |
19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /components/logo.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link' 2 | import React from 'react' 3 | 4 | export default function Logo() { 5 | return ( 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | Spend.In 20 | 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /components/main-nav.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import Link from "next/link" 5 | import { AnimatePresence, motion } from "framer-motion"; 6 | import { useState } from "react" 7 | import Logo from "./logo" 8 | import { Button } from "./button"; 9 | 10 | const DropdownItem = ({ item }: any) => { 11 | const [isHovering, setIsHovering] = useState(false); 12 | 13 | const handleItemClick = (event: { preventDefault: () => void; }) => { 14 | if (item.items && item.items.length) { 15 | event.preventDefault(); // Prevent link navigation 16 | setIsHovering(!isHovering); // Toggle visibility of submenu 17 | } 18 | }; 19 | 20 | return ( 21 |
24 | 27 | {item.label} 28 | 29 |
30 | ); 31 | }; 32 | 33 | 34 | const navItems = [ 35 | { 36 | href: '#hero-section', label: 'Products', 37 | }, 38 | { href: '#benefit', label: 'Benefit' }, 39 | { href: '#how-it-works', label: 'How it Works' }, 40 | { href: '#testimonial', label: 'Testimonial' }, 41 | { href: '#pricing', label: 'Pricing' }, 42 | { 43 | href: '#download', label: 'Download' 44 | } 45 | ]; 46 | 47 | export function MainNav() { 48 | const [isMenuOpen, setIsMenuOpen] = React.useState(false); 49 | const closeMenu = () => { 50 | setIsMenuOpen(false); 51 | }; 52 | 53 | return ( 54 |
56 |
57 | 58 | 68 | 78 |
79 |
92 | {isMenuOpen && ( 93 | 117 | )} 118 |
119 |
120 | ) 121 | } 122 | 123 | -------------------------------------------------------------------------------- /components/navbar.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import Link from 'next/link'; 3 | 4 | export default function Navbar() { 5 | return ( 6 | 39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /components/price-card.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | interface featureProps { 4 | description: string; 5 | isIncluded: boolean; 6 | } 7 | 8 | interface PriceCardProps { 9 | icon: string; 10 | title: string; 11 | description: string; 12 | priceMonthly: string; 13 | priceYearly: string; 14 | features: featureProps[]; 15 | isYearly?: boolean; 16 | isPopular?: boolean; 17 | onClick?: () => void; 18 | } 19 | 20 | export default function PriceCard( 21 | { icon, title, description, priceMonthly, priceYearly, isYearly, features, onClick }: PriceCardProps 22 | ) { 23 | return ( 24 |
25 |
26 |
27 | icon 28 |

{title}

29 |
30 |
31 |

32 | {isYearly ? priceYearly : priceMonthly} 33 |

34 |

35 | {isYearly ? '/year' : '/month'} 36 |

37 |
38 |
39 |

40 | {description}

41 |
42 |
    43 | {features?.map((feature, index) => ( 44 |
  • 45 | icon 46 |

    {feature.description}

    47 |
  • 48 | )) 49 | } 50 |
51 |
52 | 56 |
57 | ) 58 | } 59 | -------------------------------------------------------------------------------- /components/price-section.tsx: -------------------------------------------------------------------------------- 1 | import { motion } from 'framer-motion' 2 | import React from 'react' 3 | import Container from './container' 4 | import PriceCard from './price-card' 5 | import Switch from './switch' 6 | import { fadeInAnimationByIndex } from '@/lib/animations' 7 | 8 | 9 | const priceCards = [ 10 | { 11 | icon: "/plans/free.svg", 12 | title: "Free", 13 | description: 14 | "For small business that just started and need to manage their expenses.", 15 | priceMonthly: "$0", 16 | priceYearly: "$0", 17 | features: [ 18 | { 19 | description: "Up to 10 users", 20 | isIncluded: true, 21 | }, 22 | { 23 | description: "Basic support", 24 | isIncluded: true, 25 | }, 26 | { 27 | description: "5 workspace", 28 | isIncluded: true, 29 | }, 30 | { 31 | description: "Sync accross device", 32 | isIncluded: false, 33 | }, 34 | { 35 | description: "Admin tools", 36 | isIncluded: false, 37 | }, 38 | { 39 | description: "100+ integrations", 40 | isIncluded: false, 41 | }, 42 | ], 43 | }, 44 | { 45 | icon: "/plans/pro.svg", 46 | title: "Pro", 47 | description: 48 | "For medium business that need more features to manage their expenses.", 49 | priceMonthly: "$12", 50 | priceYearly: "$79.99", 51 | features: [ 52 | { 53 | description: "Everything in Free Plan", 54 | isIncluded: true, 55 | }, 56 | { 57 | description: "Priority support", 58 | isIncluded: true, 59 | }, 60 | { 61 | description: "Advanced report", 62 | isIncluded: true, 63 | }, 64 | { 65 | description: "Sync accross device", 66 | isIncluded: true, 67 | }, 68 | { 69 | description: "Admin tools", 70 | isIncluded: true, 71 | }, 72 | { 73 | description: "100+ integrations", 74 | isIncluded: true, 75 | }, 76 | ], 77 | }, 78 | { 79 | icon: "/plans/ultimate.svg", 80 | title: "Ultimate", 81 | description: 82 | "For big business that need all features to manage their expenses.", 83 | priceMonthly: "$33", 84 | priceYearly: "$169.99", features: [ 85 | { 86 | description: "Everything in Pro Plan", 87 | isIncluded: true, 88 | }, 89 | { 90 | description: "Daily performance reports", 91 | isIncluded: true, 92 | }, 93 | { 94 | description: "Dedicated assistant", 95 | isIncluded: true, 96 | }, 97 | { 98 | description: "Artificial intelligence", 99 | isIncluded: true, 100 | }, 101 | { 102 | description: "Marketing tools & automations", 103 | isIncluded: true, 104 | }, 105 | { 106 | description: "Advanced security", 107 | isIncluded: true, 108 | }, 109 | ], 110 | }, 111 | ] 112 | 113 | 114 | 115 | export default function PriceSection() { 116 | 117 | const [isToggled, setToggled] = React.useState(false) 118 | 119 | 120 | return ( 121 |
122 | 123 |
124 |
125 |
126 |

127 | Ready to Get Started? 128 |

129 | 135 | Choose a plan that suits your business needs 136 | 137 |
138 |
139 |
140 |

Monthly

141 | setToggled(!isToggled)} /> 142 |

Yearly

143 |
145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 |
157 |
158 |
159 | Save 10% 160 |
161 |
162 |
163 | { 164 | priceCards.map((priceCard, index) => ( 165 | 175 | )) 176 | } 177 |
178 |
179 |
180 |
181 |
) 182 | } 183 | -------------------------------------------------------------------------------- /components/switch.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | interface SwitchProps { 4 | isToggled: boolean; 5 | setToggled: React.Dispatch>; 6 | } 7 | 8 | const Switch = ( 9 | { isToggled, setToggled }: SwitchProps 10 | ) => { 11 | const handleChange = (event: React.ChangeEvent) => { 12 | setToggled(event.target.checked); 13 | }; 14 | 15 | return ( 16 | 20 | ); 21 | }; 22 | 23 | export default Switch; -------------------------------------------------------------------------------- /components/testimonial-card.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | interface TestimonialCardProps { 3 | avatar: string 4 | name: string 5 | position: string 6 | content: string 7 | title: string 8 | } 9 | 10 | export default function TestimonialCard({ 11 | avatar, 12 | name, 13 | position, 14 | content, 15 | title, 16 | }: TestimonialCardProps) { 17 | return ( 18 |
19 |
20 |
21 | {title} 22 |
23 |

24 | {content} 25 |

26 |
27 |
28 |
29 | avatar 34 |
35 |
{name}
36 |

37 | {position} 38 |

39 |
40 |
41 |
42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /components/testimonial-section.tsx: -------------------------------------------------------------------------------- 1 | import { motion } from 'framer-motion' 2 | import React from 'react' 3 | import Container from './container' 4 | import TestimonialCard from './testimonial-card' 5 | import LeadingTitle from './leading-title' 6 | import { fadeInAnimationByIndex } from '@/lib/animations' 7 | import { testimonialData } from '@/lib/content' 8 | 9 | 10 | 11 | 12 | export default function TestimonialSection() { 13 | return ( 14 |
15 | 16 |
17 |
18 |
19 | 20 |

21 | Our User Kind Words 22 |

23 | 29 | Here are some testimonials from our user after using Spend.In to manage their business expenses. 30 | 31 |
32 |
34 | { 35 | testimonialData.map((testimonial, index) => ( 36 | 44 | )) 45 | } 46 |
47 |
48 |
49 |
50 |
51 | 52 | ) 53 | } 54 | -------------------------------------------------------------------------------- /lib/animations.ts: -------------------------------------------------------------------------------- 1 | export const fadeInAnimationByIndex = { 2 | initial: { y: 20, opacity: 0 }, 3 | animate: (i: number) => ({ 4 | y: 0, 5 | opacity: 1, 6 | transition: { delay: i * 0.03 }, 7 | }), 8 | } 9 | 10 | export const moveInAnimationByIndex = { 11 | initial: { y: 20, scale: 0.9, opacity: 0 }, 12 | animate: (i: number) => ({ 13 | y: 0, 14 | scale: 1, 15 | opacity: 1, 16 | transition: { delay: i * 0.03 }, 17 | }), 18 | } -------------------------------------------------------------------------------- /lib/content.ts: -------------------------------------------------------------------------------- 1 | interface Testimonial { 2 | avatar: string 3 | name: string 4 | position: string 5 | content: string 6 | title: string 7 | } 8 | 9 | 10 | export const testimonialData: Testimonial[] = [ 11 | { 12 | avatar: "/avatar-1.png", 13 | name: "John Doe", 14 | position: "CEO at Company", 15 | content: 16 | "It’s just 1 month since I’m using Spend.In to manage my business expenses, but the result is very satisfying! My business finance now more neat than before, thanks to Spend.In!", 17 | title: "It’s just incredible!", 18 | }, 19 | { 20 | avatar: "/avatar-2.png", 21 | name: "Natasha Romanoff", 22 | position: "Black Widow", 23 | content: 24 | "Never thought that with Spend.In managing my business expenses is so easy! Been using this platform for 3 months and still counting!", 25 | title: "Satisfied User Here!", 26 | }, 27 | { 28 | avatar: "/avatar-3.png", 29 | name: "Moritika Kazuki", 30 | position: "Finance Manager at Mangan", 31 | content: 32 | "“The best”! That’s what I want to say to this platform, didn’t know that there’s a platform to help you manage your business expenses like this! Very recommended to you who have a big business!", 33 | title: "No doubt, Spend.In is the best!", 34 | }, 35 | ] 36 | 37 | 38 | 39 | // Footer content 40 | 41 | interface FooterLink { 42 | label: string; 43 | url: string; 44 | } 45 | 46 | interface FooterSectionProps { 47 | title: string; 48 | urls: FooterLink[]; 49 | } 50 | 51 | 52 | 53 | export const footerContent: FooterSectionProps[] = [ 54 | { 55 | title: "Product", 56 | urls: [ 57 | { label: "Digital Invoice", url: "#" }, 58 | { label: "Insights", url: "#" }, 59 | { label: "Reimbursements", url: "#" }, 60 | ], 61 | }, 62 | { 63 | title: "Company", 64 | urls: [ 65 | { label: "About Us", url: "#" }, 66 | { label: "Newsletters", url: "#" }, 67 | { label: "Our Partners", url: "#" }, 68 | { label: "Careers", url: "#" }, 69 | { label: "Contact Us", url: "#" }, 70 | ], 71 | }, 72 | { 73 | title: "Resources", 74 | urls: [ 75 | { label: "Blog", url: "#" }, 76 | { label: "Pricing", url: "#" }, 77 | { label: "FAQ", url: "#" }, 78 | { label: "Events", url: "#" }, 79 | { label: "Ebooks & Guides", url: "#" }, 80 | ], 81 | }, 82 | { 83 | title: "Follow Us", 84 | urls: [ 85 | { label: "LinkedIn", url: "#" }, 86 | { label: "Twitter", url: "#" }, 87 | { label: "Facebook", url: "#" }, 88 | { label: "Instagram", url: "#" }, 89 | { label: "YouTube", url: "#" }, 90 | ], 91 | } 92 | ]; -------------------------------------------------------------------------------- /lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { clsx, type ClassValue } from "clsx" 2 | import { twMerge } from "tailwind-merge" 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | 8 | export const backgroundColor = '#0D121F'; -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | experimental: { 5 | appDir: true, 6 | }, 7 | } 8 | 9 | export default nextConfig 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "next-template", 3 | "version": "0.0.2", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint", 10 | "lint:fix": "next lint --fix", 11 | "preview": "next build && next start", 12 | "typecheck": "tsc --noEmit", 13 | "format:write": "prettier --write \"**/*.{ts,tsx,mdx}\" --cache", 14 | "format:check": "prettier --check \"**/*.{ts,tsx,mdx}\" --cache" 15 | }, 16 | "dependencies": { 17 | "@radix-ui/react-slot": "^1.0.2", 18 | "class-variance-authority": "^0.4.0", 19 | "classnames": "^2.5.1", 20 | "clsx": "^1.2.1", 21 | "framer-motion": "^11.1.7", 22 | "lucide-react": "0.105.0-alpha.4", 23 | "next": "^13.4.8", 24 | "next-themes": "^0.2.1", 25 | "react": "^18.2.0", 26 | "react-dom": "^18.2.0", 27 | "sharp": "^0.31.3", 28 | "tailwind-merge": "^1.13.2", 29 | "tailwindcss-animate": "^1.0.6" 30 | }, 31 | "devDependencies": { 32 | "@ianvs/prettier-plugin-sort-imports": "^3.7.2", 33 | "@types/node": "^17.0.45", 34 | "@types/react": "^18.2.14", 35 | "@types/react-dom": "^18.2.6", 36 | "@typescript-eslint/parser": "^5.61.0", 37 | "autoprefixer": "^10.4.14", 38 | "eslint": "^8.44.0", 39 | "eslint-config-next": "13.0.0", 40 | "eslint-config-prettier": "^8.8.0", 41 | "eslint-plugin-react": "^7.32.2", 42 | "eslint-plugin-tailwindcss": "^3.13.0", 43 | "postcss": "^8.4.24", 44 | "prettier": "^2.8.8", 45 | "tailwindcss": "^3.3.2", 46 | "typescript": "^4.9.5" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('prettier').Config} */ 2 | module.exports = { 3 | endOfLine: "lf", 4 | semi: false, 5 | singleQuote: false, 6 | tabWidth: 2, 7 | trailingComma: "es5", 8 | importOrder: [ 9 | "^(react/(.*)$)|^(react$)", 10 | "^(next/(.*)$)|^(next$)", 11 | "", 12 | "", 13 | "^types$", 14 | "^@/types/(.*)$", 15 | "^@/config/(.*)$", 16 | "^@/lib/(.*)$", 17 | "^@/hooks/(.*)$", 18 | "^@/components/ui/(.*)$", 19 | "^@/components/(.*)$", 20 | "^@/styles/(.*)$", 21 | "^@/app/(.*)$", 22 | "", 23 | "^[./]", 24 | ], 25 | importOrderSeparation: false, 26 | importOrderSortSpecifiers: true, 27 | importOrderBuiltinModulesToTop: true, 28 | importOrderParserPlugins: ["typescript", "jsx", "decorators-legacy"], 29 | importOrderMergeDuplicateImports: true, 30 | importOrderCombineTypeAndValueImports: true, 31 | plugins: ["@ianvs/prettier-plugin-sort-imports"], 32 | } 33 | -------------------------------------------------------------------------------- /public/avatar-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eronred/landing-starter-kit-nextjs-tailwind/b145b97098612dcc0bcea98076b75507e58193e5/public/avatar-1.png -------------------------------------------------------------------------------- /public/avatar-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eronred/landing-starter-kit-nextjs-tailwind/b145b97098612dcc0bcea98076b75507e58193e5/public/avatar-2.png -------------------------------------------------------------------------------- /public/avatar-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eronred/landing-starter-kit-nextjs-tailwind/b145b97098612dcc0bcea98076b75507e58193e5/public/avatar-3.png -------------------------------------------------------------------------------- /public/card-image-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eronred/landing-starter-kit-nextjs-tailwind/b145b97098612dcc0bcea98076b75507e58193e5/public/card-image-1.png -------------------------------------------------------------------------------- /public/checkmark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/cross.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eronred/landing-starter-kit-nextjs-tailwind/b145b97098612dcc0bcea98076b75507e58193e5/public/download.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eronred/landing-starter-kit-nextjs-tailwind/b145b97098612dcc0bcea98076b75507e58193e5/public/favicon.ico -------------------------------------------------------------------------------- /public/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eronred/landing-starter-kit-nextjs-tailwind/b145b97098612dcc0bcea98076b75507e58193e5/public/image.png -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/plans/free.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/plans/pro.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/plans/ultimate.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/thirteen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eronred/landing-starter-kit-nextjs-tailwind/b145b97098612dcc0bcea98076b75507e58193e5/public/w.png -------------------------------------------------------------------------------- /public/with-spendin.svg: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 15 | 20 | 25 | 30 | 35 | 36 | 40 | 48 | 56 | 60 | 65 | 70 | 75 | 79 | 80 | 84 | 89 | 90 | 91 | 99 | 100 | 101 | 102 | 103 | 109 | 110 | 111 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /public/without-spendin.svg: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 15 | 20 | 25 | 30 | 35 | 36 | 40 | 48 | 56 | 60 | 65 | 70 | 75 | 79 | 80 | 84 | 89 | 90 | 91 | 99 | 100 | 101 | 102 | 103 | 109 | 110 | 111 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /styles/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | html { 6 | scroll-behavior: smooth; 7 | } 8 | 9 | body { 10 | font-family: "Plus Jakarta Sans", sans-serif; 11 | scroll-behavior: smooth; 12 | } 13 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | const { text } = require("stream/consumers") 2 | const { fontFamily } = require("tailwindcss/defaultTheme") 3 | 4 | /** @type {import('tailwindcss').Config} */ 5 | module.exports = { 6 | darkMode: ["class"], 7 | content: ["app/**/*.{ts,tsx}", "components/**/*.{ts,tsx}"], 8 | theme: { 9 | container: { 10 | center: true, 11 | padding: "2rem", 12 | screens: { 13 | "2xl": "1400px", 14 | }, 15 | 16 | }, 17 | extend: { 18 | colors: { 19 | background: "#0d121f", 20 | "background-opacity": "#0d121f61", 21 | "background-light": "#F3F5F7", 22 | primary: "#7C5CFC", 23 | secondary: "#1A202C", 24 | "text-light": "#F3F4F6", 25 | "text-secondary-dark": "#90A3BF", 26 | "text-secondary-light": "#596780", 27 | "text-dark": "#040815", 28 | "transparent-white": "rgba(255, 255, 255, 0.08)", 29 | }, 30 | 31 | animation: { 32 | "accordion-down": "accordion-down 0.2s ease-out", 33 | "accordion-up": "accordion-up 0.2s ease-out", 34 | }, 35 | }, 36 | }, 37 | plugins: [require("tailwindcss-animate"), 38 | ], 39 | } 40 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "noEmit": true, 9 | "incremental": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "baseUrl": ".", 17 | "paths": { 18 | "@/*": ["./*"] 19 | }, 20 | "plugins": [ 21 | { 22 | "name": "next" 23 | } 24 | ], 25 | "strictNullChecks": true 26 | }, 27 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 28 | "exclude": ["node_modules"] 29 | } 30 | --------------------------------------------------------------------------------