├── .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 |
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 |
21 |
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 |
57 | {label}
58 |
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 | div>
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 | setSelectedTab("withSpendIn")}
28 | className={`flex whitespace-nowrap px-6 py-4 items-center justify-center ${selectedTab === "withSpendIn"
29 | ? selectedTabStyle
30 | : unselectedTabStyle
31 | } rounded-full`}
32 | >
33 | With Spend.In
34 |
35 | setSelectedTab("withOutSpendIn")}
37 | className={`flex whitespace-nowrap px-6 py-4 items-center justify-center ${selectedTab === "withOutSpendIn"
38 | ? selectedTabStyle
39 | : unselectedTabStyle
40 | } rounded-full`}>
41 | Without Spend.In
42 |
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 |
27 |
28 |
29 |
30 |
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 |
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 |
44 |
50 |
51 |
56 |
60 |
61 |
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 |
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 |
7 |
8 |
9 |
10 |
Your Logo
11 |
12 |
13 |
14 |
19 | Developers ▾
20 |
21 |
22 |
e.currentTarget.classList.add('invisible', 'opacity-0')}
25 | onMouseEnter={e => e.currentTarget.classList.remove('invisible', 'opacity-0')}
26 | aria-label="submenu"
27 | >
28 |
29 | ConnectKit
30 |
31 |
32 | API
33 |
34 |
35 |
36 |
37 |
38 |
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 |
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 |
46 | {feature.description}
47 |
48 | ))
49 | }
50 |
51 |
52 |
Get Started
55 |
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 |
17 |
18 |
19 |
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 |
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 |
--------------------------------------------------------------------------------