├── .eslintrc.json
├── .gitignore
├── .prettierrc.cjs
├── README.md
├── app
├── (auth)
│ ├── login
│ │ └── page.tsx
│ └── signup
│ │ └── page.tsx
├── (marketing)
│ ├── layout.tsx
│ └── page.tsx
├── layout.tsx
└── provider.tsx
├── components
├── announcement-banner
│ ├── announcement-banner.tsx
│ └── index.ts
├── button-link
│ ├── button-link.tsx
│ └── index.ts
├── faq
│ ├── faq.tsx
│ └── index.ts
├── features
│ ├── features.tsx
│ └── index.ts
├── gradients
│ └── background-gradient.tsx
├── hero
│ ├── hero.tsx
│ └── index.ts
├── highlights
│ ├── highlights.tsx
│ └── index.ts
├── layout
│ ├── footer.tsx
│ ├── header.tsx
│ ├── index.ts
│ ├── logo.tsx
│ ├── marketing-layout.tsx
│ ├── navigation.tsx
│ └── theme-toggle.tsx
├── logos
│ ├── chakra.tsx
│ ├── index.ts
│ ├── next.tsx
│ └── react.tsx
├── mobile-nav
│ ├── index.ts
│ └── mobile-nav.tsx
├── motion
│ ├── box.tsx
│ ├── fall-in-place.tsx
│ ├── float.tsx
│ └── page-transition.tsx
├── nav-link
│ ├── index.ts
│ └── nav-link.tsx
├── pricing
│ └── pricing.tsx
├── section
│ ├── index.ts
│ ├── section-title.tsx
│ └── section.tsx
├── seo
│ ├── index.ts
│ └── seo.tsx
├── testimonials
│ ├── index.ts
│ ├── testimonial.tsx
│ └── testimonials.tsx
└── typography
│ └── index.tsx
├── data
├── appulse.tsx
├── config.tsx
├── faq.tsx
├── logo.tsx
├── pricing.tsx
└── testimonials.tsx
├── hooks
├── use-route-changed.ts
└── use-scrollspy.ts
├── next-env.d.ts
├── next.config.mjs
├── package.json
├── pnpm-lock.yaml
├── posts
└── post-01.mdx
├── public
└── static
│ ├── favicons
│ ├── android-icon-144x144.png
│ ├── android-icon-192x192.png
│ ├── android-icon-36x36.png
│ ├── android-icon-48x48.png
│ ├── android-icon-72x72.png
│ ├── android-icon-96x96.png
│ ├── apple-icon-114x114.png
│ ├── apple-icon-120x120.png
│ ├── apple-icon-144x144.png
│ ├── apple-icon-152x152.png
│ ├── apple-icon-180x180.png
│ ├── apple-icon-57x57.png
│ ├── apple-icon-60x60.png
│ ├── apple-icon-72x72.png
│ ├── apple-icon-76x76.png
│ ├── apple-icon-precomposed.png
│ ├── apple-icon.png
│ ├── browserconfig.xml
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── favicon-96x96.png
│ ├── favicon.ico
│ ├── manifest.json
│ ├── ms-icon-144x144.png
│ ├── ms-icon-150x150.png
│ ├── ms-icon-310x310.png
│ └── ms-icon-70x70.png
│ ├── images
│ ├── avatar.jpg
│ ├── avatar2.jpg
│ ├── avatar3.jpg
│ └── eelco.jpg
│ └── screenshots
│ ├── billing.png
│ ├── dashboard.png
│ ├── landingspage.png
│ └── list.png
├── theme
├── components
│ ├── button.ts
│ ├── cta.ts
│ ├── features.ts
│ ├── index.ts
│ ├── section-title.ts
│ └── section.ts
├── foundations
│ └── typography.ts
└── index.ts
└── tsconfig.json
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals",
3 | "rules": {
4 | "import/no-anonymous-default-export": "off"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
3 | .next
--------------------------------------------------------------------------------
/.prettierrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | semi: false,
3 | trailingComma: 'all',
4 | singleQuote: true,
5 | printWidth: 80,
6 | importOrder: ['^react$', '^react-dom$', '^#.(.*)$', '^[./]'],
7 | importOrderSeparation: true,
8 | importOrderSortSpecifiers: true,
9 | importOrderGroupNamespaceSpecifiers: true,
10 | plugins: ['@trivago/prettier-plugin-sort-imports'],
11 | }
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Saas UI - Next.js - landing page.
2 |
3 | This is a free Next.js landing page template based on https://saas-ui.dev.
4 | Feel free to submit any feature requests. If you use this template please share what you've built [on Twitter](https://twitter.com/saas_js) 🚀.
5 |
6 | **[View demo](https://saas-ui-nextjs-landing-page.netlify.app/)**
7 |
8 | ## Tech
9 |
10 | - Next.js (App router)
11 | - Chakra UI
12 | - Saas UI
13 | - Typescript
14 |
15 | ## Features
16 |
17 | - Feature blocks
18 | - Testimonials
19 | - Pricing tables
20 | - Log in and Sign up pages
21 | - FAQ
22 |
23 | ## Getting Started
24 |
25 | First, clone this repo and run `pnpm i`
26 |
27 | To start the app run:
28 |
29 | ```bash
30 | pnpm dev
31 | ```
32 |
33 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
34 |
35 | You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
36 |
37 | ## Configuration
38 |
39 | Configuration files to edit basic site information, add testimonials, faq and pricing table can be found in `/data`.
40 |
41 | ## Learn More
42 |
43 | Find out more about Saas UI.
44 |
45 | - [Saas UI Documentation](https://saas-ui.dev/docs).
46 |
47 | To learn more about Next.js, take a look at the following resources:
48 |
49 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
50 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
51 |
52 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
53 |
54 | ## Deploy on Vercel
55 |
56 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
57 |
58 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
59 |
60 | ## License
61 |
62 | MIT
63 |
--------------------------------------------------------------------------------
/app/(auth)/login/page.tsx:
--------------------------------------------------------------------------------
1 | import { Center } from '@chakra-ui/react'
2 | import { Auth } from '@saas-ui/auth'
3 | import { Link } from '@saas-ui/react'
4 | import { BackgroundGradient } from 'components/gradients/background-gradient'
5 | import { PageTransition } from 'components/motion/page-transition'
6 | import { Section } from 'components/section'
7 | import { NextPage } from 'next'
8 | import { FaGithub, FaGoogle } from 'react-icons/fa'
9 |
10 | const providers = {
11 | google: {
12 | name: 'Google',
13 | icon: FaGoogle,
14 | },
15 | github: {
16 | name: 'Github',
17 | icon: FaGithub,
18 | variant: 'solid',
19 | },
20 | }
21 |
22 | const Login: NextPage = () => {
23 | return (
24 |
25 |
26 |
27 |
28 |
29 | Sign up}
33 | />
34 |
35 |
36 |
37 | )
38 | }
39 |
40 | export default Login
41 |
--------------------------------------------------------------------------------
/app/(auth)/signup/page.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Center, Stack, Text } from '@chakra-ui/react'
2 | import { Auth } from '@saas-ui/auth'
3 | import { Link } from '@saas-ui/react'
4 | import { NextPage } from 'next'
5 | import NextLink from 'next/link'
6 | import { FaGithub, FaGoogle } from 'react-icons/fa'
7 |
8 | import { Features } from '#components/features'
9 | import { BackgroundGradient } from '#components/gradients/background-gradient'
10 | import { PageTransition } from '#components/motion/page-transition'
11 | import { Section } from '#components/section'
12 | import siteConfig from '#data/config'
13 |
14 | const providers = {
15 | google: {
16 | name: 'Google',
17 | icon: FaGoogle,
18 | },
19 | github: {
20 | name: 'Github',
21 | icon: FaGithub,
22 | variant: 'solid',
23 | },
24 | }
25 |
26 | const Login: NextPage = () => {
27 | return (
28 |
29 |
40 |
41 |
47 |
48 |
49 |
55 |
56 | ({
65 | iconPosition: 'left',
66 | variant: 'left-icon',
67 |
68 | ...feature,
69 | }))}
70 | />
71 |
72 |
73 |
74 | Log in}
79 | >
80 |
81 | By signing up you agree to our{' '}
82 |
83 | Terms of Service
84 | {' '}
85 | and{' '}
86 |
87 | Privacy Policy
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | )
97 | }
98 |
99 | export default Login
100 |
--------------------------------------------------------------------------------
/app/(marketing)/layout.tsx:
--------------------------------------------------------------------------------
1 | import { MarketingLayout } from '#components/layout'
2 |
3 | export default function Layout(props: { children: React.ReactNode }) {
4 | return {props.children}
5 | }
6 |
--------------------------------------------------------------------------------
/app/(marketing)/page.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import {
4 | Box,
5 | ButtonGroup,
6 | Container,
7 | Flex,
8 | HStack,
9 | Heading,
10 | Icon,
11 | IconButton,
12 | Stack,
13 | Tag,
14 | Text,
15 | VStack,
16 | Wrap,
17 | useClipboard,
18 | } from '@chakra-ui/react'
19 | import { Br, Link } from '@saas-ui/react'
20 | import type { Metadata, NextPage } from 'next'
21 | import Image from 'next/image'
22 | import {
23 | FiArrowRight,
24 | FiBox,
25 | FiCheck,
26 | FiCode,
27 | FiCopy,
28 | FiFlag,
29 | FiGrid,
30 | FiLock,
31 | FiSearch,
32 | FiSliders,
33 | FiSmile,
34 | FiTerminal,
35 | FiThumbsUp,
36 | FiToggleLeft,
37 | FiTrendingUp,
38 | FiUserPlus,
39 | } from 'react-icons/fi'
40 |
41 | import * as React from 'react'
42 |
43 | import { ButtonLink } from '#components/button-link/button-link'
44 | import { Faq } from '#components/faq'
45 | import { Features } from '#components/features'
46 | import { BackgroundGradient } from '#components/gradients/background-gradient'
47 | import { Hero } from '#components/hero'
48 | import {
49 | Highlights,
50 | HighlightsItem,
51 | HighlightsTestimonialItem,
52 | } from '#components/highlights'
53 | import { ChakraLogo, NextjsLogo } from '#components/logos'
54 | import { FallInPlace } from '#components/motion/fall-in-place'
55 | import { Pricing } from '#components/pricing/pricing'
56 | import { Testimonial, Testimonials } from '#components/testimonials'
57 | import { Em } from '#components/typography'
58 | import faq from '#data/faq'
59 | import pricing from '#data/pricing'
60 | import testimonials from '#data/testimonials'
61 |
62 | export const meta: Metadata = {
63 | title: 'Saas UI Landingspage',
64 | description: 'Free SaaS landingspage starter kit',
65 | }
66 |
67 | const Home: NextPage = () => {
68 | return (
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | )
83 | }
84 |
85 | const HeroSection: React.FC = () => {
86 | return (
87 |
88 |
89 |
90 |
91 |
97 | Build beautiful
98 |
software faster
99 |
100 | }
101 | description={
102 |
103 | Saas UI is a React component library
104 |
that doesn't get in your way and helps you
{' '}
105 | build intuitive SaaS products with speed.
106 |
107 | }
108 | >
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 | Sign Up
117 |
118 |
133 | }
134 | >
135 | View demo
136 |
137 |
138 |
139 |
140 |
149 |
150 |
151 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
206 |
207 | )
208 | }
209 |
210 | const HighlightsSection = () => {
211 | const { value, onCopy, hasCopied } = useClipboard('yarn add @saas-ui/react')
212 |
213 | return (
214 |
215 |
216 |
217 |
218 | Get started for free with 30+ open source components.
219 | Including authentication screens with Clerk, Supabase and Magic.
220 | Fully functional forms with React Hook Form. Data tables with React
221 | Table.
222 |
223 |
224 |
235 |
236 |
237 | yarn add
238 | {' '}
239 |
240 | @saas-ui/react
241 |
242 |
243 | : }
245 | aria-label="Copy install command"
246 | onClick={onCopy}
247 | variant="ghost"
248 | ms="4"
249 | isRound
250 | color="white"
251 | />
252 |
253 |
254 |
255 |
256 |
257 | We don't like to re-invent the wheel, neither should you. We
258 | selected the most productive and established tools in the scene and
259 | build Saas UI on top of it.
260 |
261 |
262 |
268 | “Saas UI helped us set up a beautiful modern UI in no time. It saved us
269 | hundreds of hours in development time and allowed us to focus on
270 | business logic for our specific use-case from the start.”
271 |
272 |
276 |
277 | We took care of all your basic frontend needs, so you can start
278 | building functionality that makes your product unique.
279 |
280 |
281 | {[
282 | 'authentication',
283 | 'navigation',
284 | 'crud',
285 | 'settings',
286 | 'multi-tenancy',
287 | 'layouts',
288 | 'billing',
289 | 'a11y testing',
290 | 'server-side rendering',
291 | 'documentation',
292 | 'onboarding',
293 | 'storybooks',
294 | 'theming',
295 | 'upselling',
296 | 'unit testing',
297 | 'feature flags',
298 | 'responsiveness',
299 | ].map((value) => (
300 |
307 | {value}
308 |
309 | ))}
310 |
311 |
312 |
313 | )
314 | }
315 |
316 | const FeaturesSection = () => {
317 | return (
318 |
327 | Not your standard
328 |
dashboard template.
329 |
330 | }
331 | description={
332 | <>
333 | Saas UI Pro includes everything you need to build modern frontends.
334 |
335 | Use it as a template for your next product or foundation for your
336 | design system.
337 | >
338 | }
339 | align="left"
340 | columns={[1, 2, 3]}
341 | iconSize={4}
342 | features={[
343 | {
344 | title: '#components.',
345 | icon: FiBox,
346 | description:
347 | 'All premium components are available on a private NPM registery, no more copy pasting and always up-to-date.',
348 | variant: 'inline',
349 | },
350 | {
351 | title: 'Starterkits.',
352 | icon: FiLock,
353 | description:
354 | 'Example apps in Next.JS, Electron. Including authentication, billing, example pages, everything you need to get started FAST.',
355 | variant: 'inline',
356 | },
357 | {
358 | title: 'Documentation.',
359 | icon: FiSearch,
360 | description:
361 | 'Extensively documented, including storybooks, best practices, use-cases and examples.',
362 | variant: 'inline',
363 | },
364 | {
365 | title: 'Onboarding.',
366 | icon: FiUserPlus,
367 | description:
368 | 'Add user onboarding flows, like tours, hints and inline documentation without breaking a sweat.',
369 | variant: 'inline',
370 | },
371 | {
372 | title: 'Feature flags.',
373 | icon: FiFlag,
374 | description:
375 | "Implement feature toggles for your billing plans with easy to use hooks. Connect Flagsmith, or other remote config services once you're ready.",
376 | variant: 'inline',
377 | },
378 | {
379 | title: 'Upselling.',
380 | icon: FiTrendingUp,
381 | description:
382 | '#components and hooks for upgrade flows designed to make upgrading inside your app frictionless.',
383 | variant: 'inline',
384 | },
385 | {
386 | title: 'Themes.',
387 | icon: FiToggleLeft,
388 | description:
389 | 'Includes multiple themes with darkmode support, always have the perfect starting point for your next project.',
390 | variant: 'inline',
391 | },
392 | {
393 | title: 'Generators.',
394 | icon: FiTerminal,
395 | description:
396 | 'Extend your design system while maintaininig code quality and consistency with built-in generators.',
397 | variant: 'inline',
398 | },
399 | {
400 | title: 'Monorepo.',
401 | icon: FiCode,
402 | description: (
403 | <>
404 | All code is available as packages in a high-performance{' '}
405 | Turborepo, you have full
406 | control to modify and adjust it to your workflow.
407 | >
408 | ),
409 | variant: 'inline',
410 | },
411 | ]}
412 | />
413 | )
414 | }
415 |
416 | const TestimonialsSection = () => {
417 | const columns = React.useMemo(() => {
418 | return testimonials.items.reduce>(
419 | (columns, t, i) => {
420 | columns[i % 3].push(t)
421 |
422 | return columns
423 | },
424 | [[], [], []],
425 | )
426 | }, [])
427 |
428 | return (
429 |
434 | <>
435 | {columns.map((column, i) => (
436 |
437 | {column.map((t, i) => (
438 |
439 | ))}
440 |
441 | ))}
442 | >
443 |
444 | )
445 | }
446 |
447 | const PricingSection = () => {
448 | return (
449 |
450 |
451 | VAT may be applicable depending on your location.
452 |
453 |
454 | )
455 | }
456 |
457 | const FaqSection = () => {
458 | return
459 | }
460 |
461 | export default Home
462 |
--------------------------------------------------------------------------------
/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import { ColorModeScript, theme } from '@chakra-ui/react'
2 |
3 | import { Provider } from './provider'
4 |
5 | export default function Layout(props: { children: React.ReactNode }) {
6 | const colorMode = theme.config.initialColorMode
7 |
8 | return (
9 |
10 |
11 |
16 |
22 |
28 |
29 |
30 |
31 |
32 | {props.children}
33 |
34 |
35 | )
36 | }
37 |
--------------------------------------------------------------------------------
/app/provider.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { AuthProvider } from '@saas-ui/auth'
4 | import { SaasProvider } from '@saas-ui/react'
5 |
6 | import { theme } from '#theme'
7 |
8 | export function Provider(props: { children: React.ReactNode }) {
9 | return (
10 |
11 | {props.children}
12 |
13 | )
14 | }
15 |
--------------------------------------------------------------------------------
/components/announcement-banner/announcement-banner.tsx:
--------------------------------------------------------------------------------
1 | import NextLink from "next/link";
2 | import {
3 | Box,
4 | Container,
5 | Flex,
6 | HStack,
7 | Icon,
8 | LinkBox,
9 | LinkOverlay,
10 | useColorModeValue,
11 | Button,
12 | } from "@chakra-ui/react";
13 | import {
14 | Banner,
15 | BannerActions,
16 | BannerContent,
17 | BannerDescription,
18 | BannerTitle,
19 | } from "@saas-ui/react";
20 | import { FiArrowRight } from "react-icons/fi";
21 | import { FallInPlace } from "../motion/fall-in-place";
22 |
23 | export interface AnnouncementBannerProps {
24 | title: string;
25 | description: string;
26 | href: string;
27 | action?: string;
28 | }
29 |
30 | export const AnnouncementBanner: React.FC = (
31 | props
32 | ) => {
33 | const { title, description, href, action } = props;
34 | if (!title) {
35 | return null;
36 | }
37 |
38 | return (
39 |
40 |
41 |
42 |
43 |
84 |
85 |
86 | {title}
87 |
88 |
92 |
93 | {action && (
94 |
95 |
109 | }
110 | >
111 | Read more
112 |
113 |
114 | )}
115 |
116 |
117 |
118 |
119 |
120 |
121 | );
122 | };
123 |
--------------------------------------------------------------------------------
/components/announcement-banner/index.ts:
--------------------------------------------------------------------------------
1 | export * from './announcement-banner'
2 |
--------------------------------------------------------------------------------
/components/button-link/button-link.tsx:
--------------------------------------------------------------------------------
1 | import { Button, ButtonProps } from '@chakra-ui/react'
2 | import NextLink, { LinkProps } from 'next/link'
3 |
4 | export type ButtonLinkProps = LinkProps & ButtonProps
5 |
6 | export const ButtonLink: React.FC = ({
7 | href,
8 | children,
9 | ...props
10 | }) => {
11 | return (
12 |
13 |
14 |
15 | )
16 | }
17 |
--------------------------------------------------------------------------------
/components/button-link/index.ts:
--------------------------------------------------------------------------------
1 | export * from './button-link'
2 |
--------------------------------------------------------------------------------
/components/faq/faq.tsx:
--------------------------------------------------------------------------------
1 | import { chakra, SimpleGrid } from '@chakra-ui/react'
2 | import { Section, SectionProps, SectionTitle } from 'components/section'
3 |
4 | interface FaqProps extends Omit {
5 | title?: React.ReactNode
6 | description?: React.ReactNode
7 | items: { q: React.ReactNode; a: React.ReactNode }[]
8 | }
9 |
10 | export const Faq: React.FC = (props) => {
11 | const {
12 | title = 'Frequently asked questions',
13 | description,
14 | items = [],
15 | } = props
16 | return (
17 |
18 |
19 |
20 |
21 | {items?.map(({ q, a }, i) => {
22 | return
23 | })}
24 |
25 |
26 | )
27 | }
28 |
29 | export interface FaqItemProps {
30 | question: React.ReactNode
31 | answer: React.ReactNode
32 | }
33 |
34 | const FaqItem: React.FC = ({ question, answer }) => {
35 | return (
36 |
37 |
38 | {question}
39 |
40 | {answer}
41 |
42 | )
43 | }
44 |
--------------------------------------------------------------------------------
/components/faq/index.ts:
--------------------------------------------------------------------------------
1 | export * from './faq'
2 |
--------------------------------------------------------------------------------
/components/features/features.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import {
3 | Box,
4 | Stack,
5 | VStack,
6 | SimpleGrid,
7 | Heading,
8 | Text,
9 | Icon,
10 | Circle,
11 | ResponsiveValue,
12 | useMultiStyleConfig,
13 | ThemingProps,
14 | SystemProps,
15 | } from '@chakra-ui/react'
16 |
17 | import { Section, SectionTitle, SectionTitleProps } from 'components/section'
18 |
19 | const Revealer = ({ children }: any) => {
20 | return children
21 | }
22 |
23 | export interface FeaturesProps
24 | extends Omit,
25 | ThemingProps<'Features'> {
26 | title?: React.ReactNode
27 | description?: React.ReactNode
28 | features: Array
29 | columns?: ResponsiveValue
30 | spacing?: string | number
31 | aside?: React.ReactChild
32 | reveal?: React.FC
33 | iconSize?: SystemProps['boxSize']
34 | innerWidth?: SystemProps['maxW']
35 | }
36 |
37 | export interface FeatureProps {
38 | title?: React.ReactNode
39 | description?: React.ReactNode
40 | icon?: any
41 | iconPosition?: 'left' | 'top'
42 | iconSize?: SystemProps['boxSize']
43 | ip?: 'left' | 'top'
44 | variant?: string
45 | delay?: number
46 | }
47 |
48 | export const Feature: React.FC = (props) => {
49 | const {
50 | title,
51 | description,
52 | icon,
53 | iconPosition,
54 | iconSize = 8,
55 | ip,
56 | variant,
57 | } = props
58 | const styles = useMultiStyleConfig('Feature', { variant })
59 |
60 | const pos = iconPosition || ip
61 | const direction = pos === 'left' ? 'row' : 'column'
62 |
63 | return (
64 |
65 | {icon && (
66 |
67 |
68 |
69 | )}
70 |
71 | {title}
72 | {description}
73 |
74 |
75 | )
76 | }
77 |
78 | export const Features: React.FC = (props) => {
79 | const {
80 | title,
81 | description,
82 | features,
83 | columns = [1, 2, 3],
84 | spacing = 8,
85 | align: alignProp = 'center',
86 | iconSize = 8,
87 | aside,
88 | reveal: Wrap = Revealer,
89 | ...rest
90 | } = props
91 |
92 | const align = !!aside ? 'left' : alignProp
93 |
94 | const ip = align === 'left' ? 'left' : 'top'
95 |
96 | return (
97 |
98 |
99 |
100 | {(title || description) && (
101 |
102 |
107 |
108 | )}
109 |
110 | {features.map((feature, i) => {
111 | return (
112 |
113 |
114 |
115 | )
116 | })}
117 |
118 |
119 | {aside && (
120 |
121 | {aside}
122 |
123 | )}
124 |
125 |
126 | )
127 | }
128 |
--------------------------------------------------------------------------------
/components/features/index.ts:
--------------------------------------------------------------------------------
1 | export * from './features'
2 |
--------------------------------------------------------------------------------
/components/gradients/background-gradient.tsx:
--------------------------------------------------------------------------------
1 | import { Box, useTheme, useColorModeValue } from '@chakra-ui/react'
2 |
3 | export const BackgroundGradient = ({ hideOverlay, ...props }: any) => {
4 | const theme = useTheme()
5 | const colors = [
6 | theme.colors.primary['800'],
7 | theme.colors.secondary['500'],
8 | theme.colors.cyan['500'],
9 | theme.colors.teal['500'],
10 | ]
11 |
12 | let fallbackBackground = `radial-gradient(at top left, ${colors[0]} 30%, transparent 80%), radial-gradient(at bottom, ${colors[1]} 0%, transparent 60%), radial-gradient(at bottom left, var(--chakra-colors-cyan-500) 0%, transparent 50%),
13 | radial-gradient(at top right, ${colors[3]}, transparent), radial-gradient(at bottom right, ${colors[0]} 0%, transparent 50%);`
14 |
15 | let gradientOverlay = `linear-gradient(0deg, var(--chakra-colors-${useColorModeValue(
16 | 'white',
17 | 'gray-900'
18 | )}) 60%, rgba(0, 0, 0, 0) 100%);`
19 |
20 | return (
21 |
35 |
44 |
45 | )
46 | }
47 |
--------------------------------------------------------------------------------
/components/hero/hero.tsx:
--------------------------------------------------------------------------------
1 | import { Container, Flex, FlexProps, Text, VStack } from '@chakra-ui/react'
2 |
3 | interface HeroProps extends Omit {
4 | title: string | React.ReactNode
5 | description?: string | React.ReactNode
6 | }
7 |
8 | export const Hero = ({ title, description, children, ...rest }: HeroProps) => {
9 | return (
10 |
11 |
12 |
13 |
14 | {title}
15 |
16 |
23 | {description}
24 |
25 |
26 | {children}
27 |
28 |
29 | )
30 | }
31 |
--------------------------------------------------------------------------------
/components/hero/index.ts:
--------------------------------------------------------------------------------
1 | export * from './hero'
2 |
--------------------------------------------------------------------------------
/components/highlights/highlights.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Box,
3 | Card,
4 | CardProps,
5 | Grid,
6 | GridItem,
7 | GridItemProps,
8 | Heading,
9 | useTheme,
10 | } from '@chakra-ui/react'
11 | import { transparentize } from '@chakra-ui/theme-tools'
12 |
13 | import { Section, SectionProps } from '#components/section'
14 | import { Testimonial, TestimonialProps } from '#components/testimonials'
15 |
16 | export interface HighlightBoxProps
17 | extends GridItemProps,
18 | Omit {}
19 |
20 | export const HighlightsItem: React.FC = (props) => {
21 | const { children, title, ...rest } = props
22 | return (
23 |
36 | {title && (
37 |
38 | {title}
39 |
40 | )}
41 | {children}
42 |
43 | )
44 | }
45 |
46 | export const HighlightsTestimonialItem: React.FC<
47 | HighlightBoxProps & TestimonialProps & { gradient: [string, string] }
48 | > = (props) => {
49 | const {
50 | name,
51 | description,
52 | avatar,
53 | children,
54 | gradient = ['primary.500', 'secondary.500'],
55 | ...rest
56 | } = props
57 | const theme = useTheme()
58 | return (
59 |
65 |
77 |
81 | {description}
82 |
83 | }
84 | avatar={avatar}
85 | border="0"
86 | bg="transparent"
87 | boxShadow="none"
88 | color="white"
89 | position="relative"
90 | >
91 | {children}
92 |
93 |
94 | )
95 | }
96 |
97 | export const Highlights: React.FC = (props) => {
98 | const { children, ...rest } = props
99 |
100 | return (
101 |
107 |
112 | {children}
113 |
114 |
115 | )
116 | }
117 |
--------------------------------------------------------------------------------
/components/highlights/index.ts:
--------------------------------------------------------------------------------
1 | export * from './highlights'
2 |
--------------------------------------------------------------------------------
/components/layout/footer.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Box,
3 | BoxProps,
4 | Container,
5 | Flex,
6 | HStack,
7 | SimpleGrid,
8 | Stack,
9 | Text,
10 | } from '@chakra-ui/react'
11 | import { Link, LinkProps } from '@saas-ui/react'
12 |
13 | import siteConfig from '#data/config'
14 |
15 | export interface FooterProps extends BoxProps {
16 | columns?: number
17 | }
18 |
19 | export const Footer: React.FC = (props) => {
20 | const { columns = 2, ...rest } = props
21 | return (
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | {siteConfig.seo.description}
32 |
33 |
34 | {siteConfig.footer.copyright}
35 |
36 |
37 | {siteConfig.footer?.links?.map(({ href, label }) => (
38 |
39 | {label}
40 |
41 | ))}
42 |
43 |
44 |
45 |
46 | )
47 | }
48 |
49 | export interface CopyrightProps {
50 | title?: React.ReactNode
51 | children: React.ReactNode
52 | }
53 |
54 | export const Copyright: React.FC = ({
55 | title,
56 | children,
57 | }: CopyrightProps) => {
58 | let content
59 | if (title && !children) {
60 | content = `© ${new Date().getFullYear()} - ${title}`
61 | }
62 | return (
63 |
64 | {content || children}
65 |
66 | )
67 | }
68 |
69 | export const FooterLink: React.FC = (props) => {
70 | const { children, ...rest } = props
71 | return (
72 |
82 | {children}
83 |
84 | )
85 | }
86 |
--------------------------------------------------------------------------------
/components/layout/header.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Box,
3 | BoxProps,
4 | Container,
5 | Flex,
6 | useColorModeValue,
7 | } from '@chakra-ui/react'
8 | import { useScroll } from 'framer-motion'
9 |
10 | import * as React from 'react'
11 |
12 | import { Logo } from './logo'
13 | import Navigation from './navigation'
14 |
15 | export interface HeaderProps extends Omit {}
16 |
17 | export const Header = (props: HeaderProps) => {
18 | const ref = React.useRef(null)
19 | const [y, setY] = React.useState(0)
20 | const { height = 0 } = ref.current?.getBoundingClientRect() ?? {}
21 |
22 | const { scrollY } = useScroll()
23 | React.useEffect(() => {
24 | return scrollY.on('change', () => setY(scrollY.get()))
25 | }, [scrollY])
26 |
27 | const bg = useColorModeValue('whiteAlpha.700', 'rgba(29, 32, 37, 0.7)')
28 |
29 | return (
30 | height ? bg : ''}
42 | boxShadow={y > height ? 'md' : ''}
43 | borderBottomWidth={y > height ? '1px' : ''}
44 | {...props}
45 | >
46 |
47 |
48 | {
50 | if (window.location.pathname === '/') {
51 | e.preventDefault()
52 |
53 | window.scrollTo({
54 | top: 0,
55 | behavior: 'smooth',
56 | })
57 | }
58 | }}
59 | />
60 |
61 |
62 |
63 |
64 | )
65 | }
66 |
--------------------------------------------------------------------------------
/components/layout/index.ts:
--------------------------------------------------------------------------------
1 | export { MarketingLayout } from './marketing-layout'
2 |
--------------------------------------------------------------------------------
/components/layout/logo.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Flex, Heading, VisuallyHidden } from '@chakra-ui/react'
2 | import { Link } from '@saas-ui/react'
3 |
4 | import * as React from 'react'
5 |
6 | import siteConfig from '#data/config'
7 |
8 | export interface LogoProps {
9 | href?: string
10 | onClick?: (e: React.MouseEvent) => void
11 | }
12 |
13 | export const Logo = ({ href = '/', onClick }: LogoProps) => {
14 | let logo
15 | if (siteConfig.logo) {
16 | logo =
17 | } else {
18 | logo = (
19 |
20 | {siteConfig.seo?.title}
21 |
22 | )
23 | }
24 |
25 | return (
26 |
27 |
34 | {logo}
35 | {siteConfig.seo?.title}
36 |
37 |
38 | )
39 | }
40 |
--------------------------------------------------------------------------------
/components/layout/marketing-layout.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { Box, SkipNavContent, SkipNavLink } from '@chakra-ui/react'
4 |
5 | import { ReactNode } from 'react'
6 |
7 | import {
8 | AnnouncementBanner,
9 | AnnouncementBannerProps,
10 | } from '../announcement-banner'
11 | import { Footer, FooterProps } from './footer'
12 | import { Header, HeaderProps } from './header'
13 |
14 | interface LayoutProps {
15 | children: ReactNode
16 | announcementProps?: AnnouncementBannerProps
17 | headerProps?: HeaderProps
18 | footerProps?: FooterProps
19 | }
20 |
21 | export const MarketingLayout: React.FC = (props) => {
22 | const { children, announcementProps, headerProps, footerProps } = props
23 | return (
24 |
25 | Skip to content
26 | {announcementProps ? : null}
27 |
28 |
29 |
30 | {children}
31 |
32 |
33 |
34 | )
35 | }
36 |
--------------------------------------------------------------------------------
/components/layout/navigation.tsx:
--------------------------------------------------------------------------------
1 | import { HStack } from '@chakra-ui/react'
2 | import { useDisclosure, useUpdateEffect } from '@chakra-ui/react'
3 | import { useScrollSpy } from 'hooks/use-scrollspy'
4 | import { usePathname, useRouter } from 'next/navigation'
5 |
6 | import * as React from 'react'
7 |
8 | import { MobileNavButton } from '#components/mobile-nav'
9 | import { MobileNavContent } from '#components/mobile-nav'
10 | import { NavLink } from '#components/nav-link'
11 | import siteConfig from '#data/config'
12 |
13 | import ThemeToggle from './theme-toggle'
14 |
15 | const Navigation: React.FC = () => {
16 | const mobileNav = useDisclosure()
17 | const router = useRouter()
18 | const path = usePathname()
19 | const activeId = useScrollSpy(
20 | siteConfig.header.links
21 | .filter(({ id }) => id)
22 | .map(({ id }) => `[id="${id}"]`),
23 | {
24 | threshold: 0.75,
25 | },
26 | )
27 |
28 | const mobileNavBtnRef = React.useRef()
29 |
30 | useUpdateEffect(() => {
31 | mobileNavBtnRef.current?.focus()
32 | }, [mobileNav.isOpen])
33 |
34 | return (
35 |
36 | {siteConfig.header.links.map(({ href, id, ...props }, i) => {
37 | return (
38 |
50 | {props.label}
51 |
52 | )
53 | })}
54 |
55 |
56 |
57 |
62 |
63 |
64 |
65 | )
66 | }
67 |
68 | export default Navigation
69 |
--------------------------------------------------------------------------------
/components/layout/theme-toggle.tsx:
--------------------------------------------------------------------------------
1 | import { IconButton, useColorMode } from '@chakra-ui/react'
2 | import { FiMoon, FiSun } from 'react-icons/fi'
3 |
4 | const ThemeToggle = () => {
5 | const { colorMode, toggleColorMode } = useColorMode()
6 | return (
7 | : }
11 | borderRadius="md"
12 | onClick={toggleColorMode}
13 | />
14 | )
15 | }
16 |
17 | export default ThemeToggle
18 |
--------------------------------------------------------------------------------
/components/logos/chakra.tsx:
--------------------------------------------------------------------------------
1 | import { useColorModeValue } from '@chakra-ui/react'
2 |
3 | export const ChakraLogo = (props) => {
4 | return (
5 |
21 | )
22 | }
23 |
--------------------------------------------------------------------------------
/components/logos/index.ts:
--------------------------------------------------------------------------------
1 | export * from './chakra'
2 | export * from './next'
3 | export * from './react'
4 |
--------------------------------------------------------------------------------
/components/logos/next.tsx:
--------------------------------------------------------------------------------
1 | import { useColorModeValue } from '@chakra-ui/react'
2 |
3 | export const NextjsLogo = ({ ...rest }) => {
4 | return (
5 |
12 | )
13 | }
14 |
--------------------------------------------------------------------------------
/components/logos/react.tsx:
--------------------------------------------------------------------------------
1 | import { useColorModeValue } from '@chakra-ui/react'
2 | import * as React from 'react'
3 |
4 | export const ReactLogo = (props) => {
5 | return (
6 |
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/components/mobile-nav/index.ts:
--------------------------------------------------------------------------------
1 | export * from './mobile-nav'
2 |
--------------------------------------------------------------------------------
/components/mobile-nav/mobile-nav.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Box,
3 | CloseButton,
4 | Flex,
5 | HStack,
6 | IconButton,
7 | IconButtonProps,
8 | LinkProps,
9 | Stack,
10 | useBreakpointValue,
11 | useColorModeValue,
12 | useUpdateEffect,
13 | } from '@chakra-ui/react'
14 | import { Link } from '@saas-ui/react'
15 | import useRouteChanged from 'hooks/use-route-changed'
16 | import { usePathname } from 'next/navigation'
17 | import { AiOutlineMenu } from 'react-icons/ai'
18 | import { RemoveScroll } from 'react-remove-scroll'
19 |
20 | import * as React from 'react'
21 |
22 | import { Logo } from '#components/layout/logo'
23 | import siteConfig from '#data/config'
24 |
25 | interface NavLinkProps extends LinkProps {
26 | label: string
27 | href?: string
28 | isActive?: boolean
29 | }
30 |
31 | function NavLink({ href, children, isActive, ...rest }: NavLinkProps) {
32 | const pathname = usePathname()
33 | const bgActiveHoverColor = useColorModeValue('gray.100', 'whiteAlpha.100')
34 |
35 | const [, group] = href?.split('/') || []
36 | isActive = isActive ?? pathname?.includes(group)
37 |
38 | return (
39 |
56 | {children}
57 |
58 | )
59 | }
60 |
61 | interface MobileNavContentProps {
62 | isOpen?: boolean
63 | onClose?: () => void
64 | }
65 |
66 | export function MobileNavContent(props: MobileNavContentProps) {
67 | const { isOpen, onClose = () => {} } = props
68 | const closeBtnRef = React.useRef(null)
69 | const pathname = usePathname()
70 | const bgColor = useColorModeValue('whiteAlpha.900', 'blackAlpha.900')
71 |
72 | useRouteChanged(onClose)
73 | console.log({ isOpen })
74 | /**
75 | * Scenario: Menu is open on mobile, and user resizes to desktop/tablet viewport.
76 | * Result: We'll close the menu
77 | */
78 | const showOnBreakpoint = useBreakpointValue({ base: true, lg: false })
79 |
80 | React.useEffect(() => {
81 | if (showOnBreakpoint == false) {
82 | onClose()
83 | }
84 | }, [showOnBreakpoint, onClose])
85 |
86 | useUpdateEffect(() => {
87 | if (isOpen) {
88 | requestAnimationFrame(() => {
89 | closeBtnRef.current?.focus()
90 | })
91 | }
92 | }, [isOpen])
93 |
94 | return (
95 | <>
96 | {isOpen && (
97 |
98 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 | {siteConfig.header.links.map(
119 | ({ href, id, label, ...props }, i) => {
120 | return (
121 |
126 | {label}
127 |
128 | )
129 | },
130 | )}
131 |
132 |
133 |
134 |
135 | )}
136 | >
137 | )
138 | }
139 |
140 | export const MobileNavButton = React.forwardRef(
141 | (props: IconButtonProps, ref: React.Ref) => {
142 | return (
143 | }
150 | {...props}
151 | aria-label="Open menu"
152 | />
153 | )
154 | },
155 | )
156 |
157 | MobileNavButton.displayName = 'MobileNavButton'
158 |
--------------------------------------------------------------------------------
/components/motion/box.tsx:
--------------------------------------------------------------------------------
1 | import { ChakraProps, chakra } from '@chakra-ui/react'
2 | import { HTMLMotionProps, motion } from 'framer-motion'
3 |
4 | export interface MotionBoxProps
5 | extends Omit, 'children' | 'style'>,
6 | Omit {
7 | children?: React.ReactNode
8 | }
9 |
10 | /* @ts-expect-error */
11 | export const MotionBox = motion.create(chakra.div)
12 |
--------------------------------------------------------------------------------
/components/motion/fall-in-place.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import { MotionBox, MotionBoxProps } from './box'
4 |
5 | export const FallInPlace: React.FC = (
6 | props,
7 | ) => {
8 | const { children, delay = 0.2, ...rest } = props
9 | return (
10 |
21 | {children}
22 |
23 | )
24 | }
25 |
--------------------------------------------------------------------------------
/components/motion/float.tsx:
--------------------------------------------------------------------------------
1 | import { MotionBox, MotionBoxProps } from './box'
2 | import React from 'react'
3 |
4 | export const Float: React.FC<
5 | MotionBoxProps & { delay?: number; steps?: number[] }
6 | > = (props) => {
7 | const { children, delay = 0.2, steps = [10, -10, 10], ...rest } = props
8 | return (
9 |
22 | {children}
23 |
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/components/motion/page-transition.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { HTMLMotionProps } from 'framer-motion'
3 |
4 | import { MotionBox, MotionBoxProps } from './box'
5 |
6 | export const PageTransition: React.FC = (props) => (
7 |
12 | )
13 |
--------------------------------------------------------------------------------
/components/nav-link/index.ts:
--------------------------------------------------------------------------------
1 | export * from './nav-link'
2 |
--------------------------------------------------------------------------------
/components/nav-link/nav-link.tsx:
--------------------------------------------------------------------------------
1 | import { forwardRef, Button, ButtonProps } from "@chakra-ui/react";
2 |
3 | import Link from "next/link";
4 |
5 | export interface NavLinkProps extends ButtonProps {
6 | isActive?: boolean;
7 | href?: string;
8 | id?: string;
9 | }
10 |
11 | export const NavLink = forwardRef((props, ref) => {
12 | const { href, type, isActive, ...rest } = props;
13 |
14 | return (
15 |
25 | );
26 | });
27 |
28 | NavLink.displayName = "NavLink";
29 |
--------------------------------------------------------------------------------
/components/pricing/pricing.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Box,
3 | HStack,
4 | Heading,
5 | Icon,
6 | SimpleGrid,
7 | StackProps,
8 | Text,
9 | VStack,
10 | } from '@chakra-ui/react'
11 | import { FiCheck } from 'react-icons/fi'
12 |
13 | import React from 'react'
14 |
15 | import {
16 | ButtonLink,
17 | ButtonLinkProps,
18 | } from '#components/button-link/button-link'
19 | import { BackgroundGradient } from '#components/gradients/background-gradient'
20 | import { Section, SectionProps, SectionTitle } from '#components/section'
21 |
22 | export interface PricingPlan {
23 | id: string
24 | title: React.ReactNode
25 | description: React.ReactNode
26 | price: React.ReactNode
27 | features: Array
28 | action: ButtonLinkProps & { label?: string }
29 | isRecommended?: boolean
30 | }
31 |
32 | export interface PricingProps extends SectionProps {
33 | description: React.ReactNode
34 | plans: Array
35 | }
36 |
37 | export const Pricing: React.FC = (props) => {
38 | const { children, plans, title, description, ...rest } = props
39 |
40 | return (
41 |
42 |
43 |
44 |
45 |
46 |
47 | {plans?.map((plan) => (
48 |
65 |
66 | {plan.features.map((feature, i) =>
67 | feature ? (
68 |
69 | ) : (
70 |
71 | ),
72 | )}
73 |
74 |
75 | {plan.action.label || 'Sign Up'}
76 |
77 |
78 | ))}
79 |
80 |
81 | {children}
82 |
83 |
84 | )
85 | }
86 |
87 | const PricingFeatures: React.FC> = ({
88 | children,
89 | }) => {
90 | return (
91 |
98 | {children}
99 |
100 | )
101 | }
102 |
103 | export interface PricingFeatureProps {
104 | title: React.ReactNode
105 | iconColor?: string
106 | }
107 |
108 | const PricingFeature: React.FC = (props) => {
109 | const { title, iconColor = 'primary.500' } = props
110 | return (
111 |
112 |
113 |
114 | {title}
115 |
116 |
117 | )
118 | }
119 |
120 | export interface PricingBoxProps extends Omit {
121 | title: React.ReactNode
122 | description: React.ReactNode
123 | price: React.ReactNode
124 | }
125 |
126 | const PricingBox: React.FC = (props) => {
127 | const { title, description, price, children, ...rest } = props
128 | return (
129 |
144 |
145 | {title}
146 |
147 | {description}
148 |
149 | {price}
150 |
151 |
152 | {children}
153 |
154 |
155 | )
156 | }
157 |
--------------------------------------------------------------------------------
/components/section/index.ts:
--------------------------------------------------------------------------------
1 | export * from './section'
2 | export * from './section-title'
3 |
--------------------------------------------------------------------------------
/components/section/section-title.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | VStack,
3 | Heading,
4 | Box,
5 | StackProps,
6 | useMultiStyleConfig,
7 | } from '@chakra-ui/react'
8 |
9 | export interface SectionTitleProps extends Omit {
10 | title: React.ReactNode
11 | description?: React.ReactNode
12 | align?: 'left' | 'center'
13 | variant?: string
14 | }
15 |
16 | export const SectionTitle: React.FC = (props) => {
17 | const { title, description, align, variant, ...rest } = props
18 | const styles = useMultiStyleConfig('SectionTitle', { variant })
19 |
20 | return (
21 |
27 |
28 | {title}
29 |
30 | {description && (
31 |
32 | {description}
33 |
34 | )}
35 |
36 | )
37 | }
38 |
--------------------------------------------------------------------------------
/components/section/section.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | chakra,
3 | useStyleConfig,
4 | omitThemingProps,
5 | Container,
6 | ThemingProps,
7 | StyleProps,
8 | HTMLChakraProps,
9 | } from '@chakra-ui/react'
10 |
11 | export interface SectionProps
12 | extends HTMLChakraProps<'div'>,
13 | ThemingProps<'Section'> {
14 | children: React.ReactNode
15 | innerWidth?: StyleProps['width']
16 | }
17 |
18 | export const Section: React.FC = (props) => {
19 | const { children, innerWidth = 'container.lg', className, ...rest } = props
20 | const styles = useStyleConfig('Section', rest)
21 |
22 | const ownProps = omitThemingProps(rest)
23 |
24 | return (
25 |
26 |
27 | {children}
28 |
29 |
30 | )
31 | }
32 |
--------------------------------------------------------------------------------
/components/seo/index.ts:
--------------------------------------------------------------------------------
1 | export * from './seo'
2 |
--------------------------------------------------------------------------------
/components/seo/seo.tsx:
--------------------------------------------------------------------------------
1 | import { NextSeo, NextSeoProps } from 'next-seo'
2 |
3 | import React from 'react'
4 |
5 | import siteConfig from '#data/config'
6 |
7 | export interface SEOProps extends NextSeoProps {}
8 |
9 | export const SEO = ({ title, description, ...props }: SEOProps) => (
10 |
18 | )
19 |
--------------------------------------------------------------------------------
/components/testimonials/index.ts:
--------------------------------------------------------------------------------
1 | export * from './testimonial'
2 | export * from './testimonials'
3 |
--------------------------------------------------------------------------------
/components/testimonials/testimonial.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Avatar,
3 | Card,
4 | CardBody,
5 | CardHeader,
6 | CardProps,
7 | Heading,
8 | Stack,
9 | Text,
10 | } from "@chakra-ui/react";
11 | import { Link } from "@saas-ui/react";
12 | import { FaTwitter } from "react-icons/fa";
13 |
14 | export interface TestimonialProps extends CardProps {
15 | name: string;
16 | description: React.ReactNode;
17 | avatar: string;
18 | href?: string;
19 | children?: React.ReactNode;
20 | }
21 |
22 | export const Testimonial = ({
23 | name,
24 | description,
25 | avatar,
26 | href,
27 | children,
28 | ...rest
29 | }: TestimonialProps) => {
30 | return (
31 |
32 |
33 |
34 |
35 | {name}
36 |
37 | {description}
38 |
39 |
40 |
41 |
42 | {children}
43 |
44 | {href && (
45 |
46 |
47 |
48 | )}
49 |
50 |
51 | );
52 | };
53 |
--------------------------------------------------------------------------------
/components/testimonials/testimonials.tsx:
--------------------------------------------------------------------------------
1 | import { ResponsiveValue, SimpleGrid, Stack } from '@chakra-ui/react'
2 | import {
3 | Section,
4 | SectionProps,
5 | SectionTitle,
6 | SectionTitleProps,
7 | } from 'components/section'
8 | import { Testimonial } from './testimonial'
9 |
10 | export interface TestimonialsProps
11 | extends Omit,
12 | Pick {
13 | columns?: ResponsiveValue
14 | }
15 |
16 | export const Testimonials: React.FC = (props) => {
17 | const { children, title, columns = [1, null, 2], ...rest } = props
18 | return (
19 |
20 |
21 |
22 | {children}
23 |
24 |
25 | )
26 | }
27 |
--------------------------------------------------------------------------------
/components/typography/index.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | chakra,
3 | HTMLChakraProps,
4 | Text,
5 | TextProps,
6 | useColorModeValue,
7 | } from '@chakra-ui/react'
8 |
9 | export const Em: React.FC> = ({ children, ...props }) => {
10 | return (
11 |
17 | {children}
18 |
19 | )
20 | }
21 |
22 | // @todo make this configurable
23 | export const Br: React.FC> = (props) => {
24 | return (
25 |
26 |
27 |
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/data/appulse.tsx:
--------------------------------------------------------------------------------
1 | import { useColorModeValue } from '@chakra-ui/react'
2 |
3 | export const Logo = () => {
4 | return (
5 |
49 | )
50 | }
51 |
--------------------------------------------------------------------------------
/data/config.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from '@chakra-ui/react'
2 | import { Link } from '@saas-ui/react'
3 | import { NextSeoProps } from 'next-seo'
4 | import { FaGithub, FaTwitter } from 'react-icons/fa'
5 | import { FiCheck } from 'react-icons/fi'
6 | import { Logo } from './logo'
7 |
8 | const siteConfig = {
9 | logo: Logo,
10 | seo: {
11 | title: 'Saas UI',
12 | description: 'The React component library for startups',
13 | } as NextSeoProps,
14 | termsUrl: '#',
15 | privacyUrl: '#',
16 | header: {
17 | links: [
18 | {
19 | id: 'features',
20 | label: 'Features',
21 | },
22 | {
23 | id: 'pricing',
24 | label: 'Pricing',
25 | },
26 | {
27 | id: 'faq',
28 | label: 'FAQ',
29 | },
30 | {
31 | label: 'Login',
32 | href: '/login',
33 | },
34 | {
35 | label: 'Sign Up',
36 | href: '/signup',
37 | variant: 'primary',
38 | },
39 | ],
40 | },
41 | footer: {
42 | copyright: (
43 | <>
44 | Built by{' '}
45 | Eelco Wiersma
46 | >
47 | ),
48 | links: [
49 | {
50 | href: 'mailto:hello@saas-ui.dev',
51 | label: 'Contact',
52 | },
53 | {
54 | href: 'https://twitter.com/saas_js',
55 | label: ,
56 | },
57 | {
58 | href: 'https://github.com/saas-js/saas-ui',
59 | label: ,
60 | },
61 | ],
62 | },
63 | signup: {
64 | title: 'Start building with Saas UI',
65 | features: [
66 | {
67 | icon: FiCheck,
68 | title: 'Accessible',
69 | description: 'All components strictly follow WAI-ARIA standards.',
70 | },
71 | {
72 | icon: FiCheck,
73 | title: 'Themable',
74 | description:
75 | 'Fully customize all components to your brand with theme support and style props.',
76 | },
77 | {
78 | icon: FiCheck,
79 | title: 'Composable',
80 | description:
81 | 'Compose components to fit your needs and mix them together to create new ones.',
82 | },
83 | {
84 | icon: FiCheck,
85 | title: 'Productive',
86 | description:
87 | 'Designed to reduce boilerplate and fully typed, build your product at speed.',
88 | },
89 | ],
90 | },
91 | }
92 |
93 | export default siteConfig
94 |
--------------------------------------------------------------------------------
/data/faq.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 |
3 | const faq = {
4 | title: 'Frequently asked questions',
5 | // description: '',
6 | items: [
7 | {
8 | q: 'How many products can I use Saas UI Pro for?',
9 | a: (
10 | <>
11 | The single license can be used for one commercial application or SaaS
12 | product and unlimited internal tools. You can buy as many licenses as
13 | you need.
The unlimited license does not have any restrictions.
14 | >
15 | ),
16 | },
17 | {
18 | q: 'Can I use Saas UI Pro for client work?',
19 | a: "Yes, that's totally up to you, as long as it fits the license you purchase.",
20 | },
21 | {
22 | q: 'Can I use Saas UI Pro for Open Source projects?',
23 | a: 'No currently not. A large part of Saas UI is already released under MIT license. We try to give back to the community as much as possible.',
24 | },
25 | {
26 | q: 'Does Saas UI include Figma, Sketch or other design files?',
27 | a: 'No, Saas UI does not include any design assets. Maintaining design resources costs a lot of extra effort. We believe small teams can move much faster by designing directly in code, with help of Storybooks.',
28 | },
29 | ],
30 | }
31 |
32 | export default faq
33 |
--------------------------------------------------------------------------------
/data/logo.tsx:
--------------------------------------------------------------------------------
1 | import { chakra, HTMLChakraProps, useColorModeValue } from '@chakra-ui/react'
2 |
3 | export const Logo: React.FC> = (props) => {
4 | const color = useColorModeValue('#231f20', '#fff')
5 | return (
6 |
11 |
15 |
19 |
23 |
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/data/pricing.tsx:
--------------------------------------------------------------------------------
1 | import { HStack, Text } from '@chakra-ui/react'
2 |
3 | export default {
4 | title: 'Pricing for every stage',
5 | description:
6 | 'Pay once and get life-time access to our high quality components.',
7 | plans: [
8 | {
9 | id: 'oss',
10 | title: 'Open Source',
11 | description: 'Basic compoents, perfect to get started.',
12 | price: 'Free',
13 | features: [
14 | {
15 | title: 'MIT License',
16 | },
17 | {
18 | title: 'Authentication (Clerk/Supabase/Magic)',
19 | },
20 | {
21 | title: 'Form (react-hook-form)',
22 | },
23 | {
24 | title: 'Modals manager',
25 | },
26 | {
27 | title: 'Hotkeys',
28 | },
29 | {
30 | title: 'Web3 components',
31 | },
32 | {
33 | title: 'And much more...',
34 | },
35 | ],
36 | action: {
37 | href: '#',
38 | },
39 | },
40 | {
41 | id: 'bootstrap',
42 | title: 'Bootstrap',
43 | description: 'Complete frontend stack for bootstrappers and small teams.',
44 | price: 'Free',
45 | isRecommended: true,
46 | features: [
47 | {
48 | title: 'One project',
49 | },
50 | {
51 | title: 'One developer',
52 | },
53 | {
54 | title: 'Advanced components',
55 | },
56 | {
57 | title: 'Multiple themes',
58 | },
59 | {
60 | title: 'Next.js and Electron boilerplates',
61 | },
62 | {
63 | title: 'Private discord community',
64 | },
65 | {
66 | title: '1 year of updates',
67 | },
68 | null,
69 | {
70 | title: 'Private beta access',
71 | iconColor: 'green.500',
72 | },
73 | ],
74 | action: {
75 | href: 'https://appulse.gumroad.com/l/saas-ui-pro-pre-order?variant=Single%20license',
76 | },
77 | },
78 | {
79 | id: 'startup',
80 | title: 'Startup',
81 | description: 'Unlimited license for growing teams.',
82 | price: (
83 |
84 |
85 | €999,-
86 |
87 | €499,-
88 |
89 | ),
90 | features: [
91 | {
92 | title: 'Unlimited projects',
93 | },
94 | {
95 | title: 'Unlimited developers',
96 | },
97 | {
98 | title: '1 year of updates',
99 | },
100 | {
101 | title: 'Everything from Bootstrap',
102 | },
103 | null,
104 | {
105 | title: 'Private beta access',
106 | iconColor: 'green.500',
107 | },
108 | ],
109 | action: {
110 | href: 'https://appulse.gumroad.com/l/saas-ui-pro-pre-order?variant=Unlimited%20license',
111 | },
112 | },
113 | ],
114 | }
115 |
--------------------------------------------------------------------------------
/data/testimonials.tsx:
--------------------------------------------------------------------------------
1 | export default {
2 | title: 'Loved by tech people',
3 | items: [
4 | {
5 | name: 'Eelco Wiersma',
6 | description: 'Founder Saas UI',
7 | avatar:
8 | 'https://pbs.twimg.com/profile_images/1470742164024008706/k-eXHtu0_normal.jpg',
9 | children: (
10 | <>
11 | “With SaaS UI I am able to produce high quality dashboard apps and
12 | internal tools without a lot of design skills. The ROI really is
13 | amazing.”
14 | >
15 | ),
16 | },
17 | {
18 | name: 'Caroline Yahaya',
19 | description: 'Fullstack Developer',
20 | avatar: '/static/images/avatar2.jpg',
21 | children: (
22 | <>
23 | “Saas UI allows me to build beautiful and functional UI super fast.
24 | The components are very well thought out and the Next.js starter kit
25 | saved me at least 100 hours of work.”
26 | >
27 | ),
28 | },
29 | {
30 | name: 'Alberto Vazquez',
31 | description: 'Frontend Engineer',
32 | avatar: '/static/images/avatar3.jpg',
33 | children: (
34 | <>
35 | “Saas UI gave us a perfect starting point for our project, having the
36 | storybooks already set up with beautifully crafted components and
37 | fully mocked pages was a treat.”
38 | >
39 | ),
40 | },
41 | ],
42 | }
43 |
--------------------------------------------------------------------------------
/hooks/use-route-changed.ts:
--------------------------------------------------------------------------------
1 | import { usePathname } from 'next/navigation'
2 |
3 | import { useEffect, useRef } from 'react'
4 |
5 | const useRouteChanged = (fn: () => void) => {
6 | const pathname = usePathname()
7 |
8 | const lastPathname = useRef(pathname)
9 |
10 | useEffect(() => {
11 | if (lastPathname.current === null) {
12 | return
13 | }
14 |
15 | if (pathname !== lastPathname.current) {
16 | fn()
17 | }
18 | }, [pathname, fn])
19 | }
20 |
21 | export default useRouteChanged
22 |
--------------------------------------------------------------------------------
/hooks/use-scrollspy.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 |
3 | export function useScrollSpy(
4 | selectors: string[],
5 | options?: IntersectionObserverInit
6 | ) {
7 | const [activeId, setActiveId] = React.useState()
8 | const observer = React.useRef(null)
9 | React.useEffect(() => {
10 | const elements = selectors.map((selector) =>
11 | document.querySelector(selector)
12 | )
13 | observer.current?.disconnect()
14 | observer.current = new IntersectionObserver((entries) => {
15 | entries.forEach((entry) => {
16 | if (entry?.isIntersecting) {
17 | setActiveId(entry.target.getAttribute('id'))
18 | }
19 | })
20 | }, options)
21 | elements.forEach((el) => {
22 | if (el) observer.current?.observe(el)
23 | })
24 | return () => observer.current?.disconnect()
25 | }, [selectors, options])
26 |
27 | return activeId
28 | }
29 |
--------------------------------------------------------------------------------
/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 | ///
4 |
5 | // NOTE: This file should not be edited
6 | // see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
7 |
--------------------------------------------------------------------------------
/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | reactStrictMode: true,
4 | webpack(config) {
5 | config.module.rules.push({
6 | test: /\.svg$/,
7 | use: [
8 | {
9 | loader: '@svgr/webpack',
10 | options: {
11 | svgoConfig: {
12 | plugins: [
13 | {
14 | name: 'removeViewBox',
15 | active: false,
16 | },
17 | ],
18 | },
19 | },
20 | },
21 | ],
22 | })
23 | return config
24 | },
25 | }
26 |
27 | export default nextConfig
28 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "saas-ui-nextjs-landing-page",
3 | "version": "0.1.0",
4 | "private": true,
5 | "type": "module",
6 | "scripts": {
7 | "dev": "next dev",
8 | "build": "next build",
9 | "start": "next start",
10 | "lint": "next lint"
11 | },
12 | "dependencies": {
13 | "@chakra-ui/react": "^2.10.1",
14 | "@chakra-ui/theme-tools": "^2.2.5",
15 | "@emotion/react": "^11.13.3",
16 | "@emotion/styled": "^11.13.0",
17 | "@fontsource-variable/inter": "^5.1.0",
18 | "@saas-ui/auth": "^3.4.0",
19 | "@saas-ui/react": "^2.9.0",
20 | "@svgr/webpack": "^8.1.0",
21 | "date-fns": "^4.1.0",
22 | "framer-motion": "^11.11.1",
23 | "next": "^14.2.14",
24 | "react": "18.3.1",
25 | "react-dom": "18.3.1",
26 | "react-icons": "^5.3.0",
27 | "react-remove-scroll": "^2.6.0",
28 | "velite": "^0.1.1"
29 | },
30 | "devDependencies": {
31 | "@babel/core": "^7.25.7",
32 | "@trivago/prettier-plugin-sort-imports": "^4.3.0",
33 | "@types/node": "22.7.4",
34 | "@types/react": "18.3.11",
35 | "@types/react-dom": "18.3.0",
36 | "eslint": "9.12.0",
37 | "eslint-config-next": "14.2.14",
38 | "prettier": "^3.3.3",
39 | "typescript": "5.6.2"
40 | },
41 | "packageManager": "pnpm@8.15.9+sha512.499434c9d8fdd1a2794ebf4552b3b25c0a633abcee5bb15e7b5de90f32f47b513aca98cd5cfd001c31f0db454bc3804edccd578501e4ca293a6816166bbd9f81"
42 | }
43 |
--------------------------------------------------------------------------------
/posts/post-01.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Lorem Ipsum
3 | date: 2021-12-24
4 | ---
5 |
6 | Ullamco et nostrud magna commodo nostrud occaecat quis pariatur id ipsum. Ipsum
7 | consequat enim id excepteur consequat nostrud esse esse fugiat dolore.
8 | Reprehenderit occaecat exercitation non cupidatat in eiusmod laborum ex eu
9 | fugiat aute culpa pariatur. Irure elit proident consequat veniam minim ipsum ex
10 | pariatur.
11 |
12 | Mollit nisi cillum exercitation minim officia velit laborum non Lorem
13 | adipisicing dolore. Labore commodo consectetur commodo velit adipisicing irure
14 | dolore dolor reprehenderit aliquip. Reprehenderit cillum mollit eiusmod
15 | excepteur elit ipsum aute pariatur in. Cupidatat ex culpa velit culpa ad non
16 | labore exercitation irure laborum.
17 |
--------------------------------------------------------------------------------
/public/static/favicons/android-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/favicons/android-icon-144x144.png
--------------------------------------------------------------------------------
/public/static/favicons/android-icon-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/favicons/android-icon-192x192.png
--------------------------------------------------------------------------------
/public/static/favicons/android-icon-36x36.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/favicons/android-icon-36x36.png
--------------------------------------------------------------------------------
/public/static/favicons/android-icon-48x48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/favicons/android-icon-48x48.png
--------------------------------------------------------------------------------
/public/static/favicons/android-icon-72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/favicons/android-icon-72x72.png
--------------------------------------------------------------------------------
/public/static/favicons/android-icon-96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/favicons/android-icon-96x96.png
--------------------------------------------------------------------------------
/public/static/favicons/apple-icon-114x114.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/favicons/apple-icon-114x114.png
--------------------------------------------------------------------------------
/public/static/favicons/apple-icon-120x120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/favicons/apple-icon-120x120.png
--------------------------------------------------------------------------------
/public/static/favicons/apple-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/favicons/apple-icon-144x144.png
--------------------------------------------------------------------------------
/public/static/favicons/apple-icon-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/favicons/apple-icon-152x152.png
--------------------------------------------------------------------------------
/public/static/favicons/apple-icon-180x180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/favicons/apple-icon-180x180.png
--------------------------------------------------------------------------------
/public/static/favicons/apple-icon-57x57.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/favicons/apple-icon-57x57.png
--------------------------------------------------------------------------------
/public/static/favicons/apple-icon-60x60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/favicons/apple-icon-60x60.png
--------------------------------------------------------------------------------
/public/static/favicons/apple-icon-72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/favicons/apple-icon-72x72.png
--------------------------------------------------------------------------------
/public/static/favicons/apple-icon-76x76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/favicons/apple-icon-76x76.png
--------------------------------------------------------------------------------
/public/static/favicons/apple-icon-precomposed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/favicons/apple-icon-precomposed.png
--------------------------------------------------------------------------------
/public/static/favicons/apple-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/favicons/apple-icon.png
--------------------------------------------------------------------------------
/public/static/favicons/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 | #ffffff
--------------------------------------------------------------------------------
/public/static/favicons/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/favicons/favicon-16x16.png
--------------------------------------------------------------------------------
/public/static/favicons/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/favicons/favicon-32x32.png
--------------------------------------------------------------------------------
/public/static/favicons/favicon-96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/favicons/favicon-96x96.png
--------------------------------------------------------------------------------
/public/static/favicons/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/favicons/favicon.ico
--------------------------------------------------------------------------------
/public/static/favicons/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "App",
3 | "icons": [
4 | {
5 | "src": "\/android-icon-36x36.png",
6 | "sizes": "36x36",
7 | "type": "image\/png",
8 | "density": "0.75"
9 | },
10 | {
11 | "src": "\/android-icon-48x48.png",
12 | "sizes": "48x48",
13 | "type": "image\/png",
14 | "density": "1.0"
15 | },
16 | {
17 | "src": "\/android-icon-72x72.png",
18 | "sizes": "72x72",
19 | "type": "image\/png",
20 | "density": "1.5"
21 | },
22 | {
23 | "src": "\/android-icon-96x96.png",
24 | "sizes": "96x96",
25 | "type": "image\/png",
26 | "density": "2.0"
27 | },
28 | {
29 | "src": "\/android-icon-144x144.png",
30 | "sizes": "144x144",
31 | "type": "image\/png",
32 | "density": "3.0"
33 | },
34 | {
35 | "src": "\/android-icon-192x192.png",
36 | "sizes": "192x192",
37 | "type": "image\/png",
38 | "density": "4.0"
39 | }
40 | ]
41 | }
--------------------------------------------------------------------------------
/public/static/favicons/ms-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/favicons/ms-icon-144x144.png
--------------------------------------------------------------------------------
/public/static/favicons/ms-icon-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/favicons/ms-icon-150x150.png
--------------------------------------------------------------------------------
/public/static/favicons/ms-icon-310x310.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/favicons/ms-icon-310x310.png
--------------------------------------------------------------------------------
/public/static/favicons/ms-icon-70x70.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/favicons/ms-icon-70x70.png
--------------------------------------------------------------------------------
/public/static/images/avatar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/images/avatar.jpg
--------------------------------------------------------------------------------
/public/static/images/avatar2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/images/avatar2.jpg
--------------------------------------------------------------------------------
/public/static/images/avatar3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/images/avatar3.jpg
--------------------------------------------------------------------------------
/public/static/images/eelco.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/images/eelco.jpg
--------------------------------------------------------------------------------
/public/static/screenshots/billing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/screenshots/billing.png
--------------------------------------------------------------------------------
/public/static/screenshots/dashboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/screenshots/dashboard.png
--------------------------------------------------------------------------------
/public/static/screenshots/landingspage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/screenshots/landingspage.png
--------------------------------------------------------------------------------
/public/static/screenshots/list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saas-js/saas-ui-nextjs-landing-page/9f11869655ed24df0892cde951bb0519e5fda4ac/public/static/screenshots/list.png
--------------------------------------------------------------------------------
/theme/components/button.ts:
--------------------------------------------------------------------------------
1 | import { mode } from '@chakra-ui/theme-tools'
2 |
3 | type Dict = Record
4 |
5 | export default {
6 | variants: {
7 | 'nav-link': (props: Dict) => {
8 | const { isActive } = props
9 |
10 | const hoverColor = mode('gray.900', 'white')(props)
11 | return {
12 | outline: 'none',
13 | fontWeight: '500',
14 | color: isActive
15 | ? hoverColor
16 | : mode('gray.700', 'whiteAlpha.700')(props),
17 | transition: 'color .2s ease-in',
18 | _hover: {
19 | textDecoration: 'none',
20 | color: hoverColor,
21 | },
22 | }
23 | },
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/theme/components/cta.ts:
--------------------------------------------------------------------------------
1 | const CTA = {
2 | parts: ['wrapper', 'title', 'action', 'secondaryAction'],
3 | baseStyle: {
4 | wrapper: {
5 | pt: 28,
6 | pb: 28,
7 | },
8 | action: {
9 | colorScheme: 'primary',
10 | },
11 | secondaryAction: {
12 | colorScheme: 'primary',
13 | variant: 'ghost',
14 | },
15 | },
16 | variants: {
17 | subtle: {},
18 | solid: {
19 | wrapper: {
20 | bg: 'primary.400',
21 | },
22 | secondaryAction: {
23 | colorScheme: 'white',
24 | },
25 | },
26 | light: ({ colorMode }: any) => ({
27 | wrapper: {
28 | bg: colorMode === 'dark' ? 'gray.700' : 'gray.100',
29 | },
30 | }),
31 | },
32 | defaultProps: {
33 | variant: 'subtle',
34 | },
35 | }
36 |
37 | export default CTA
38 |
--------------------------------------------------------------------------------
/theme/components/features.ts:
--------------------------------------------------------------------------------
1 | import { transparentize, mode } from '@chakra-ui/theme-tools'
2 |
3 | const Features = {
4 | parts: ['container', 'title', 'description', 'icon'],
5 | baseStyle: {
6 | container: {
7 | align: 'flex-start',
8 | direction: 'row',
9 | },
10 | title: {
11 | as: 'h4',
12 | size: 'md',
13 | fontWeight: 'medium',
14 | mb: 2,
15 | },
16 | description: {
17 | fontSize: 'md',
18 | color: 'gray.400',
19 | },
20 | icon: {
21 | mb: 3,
22 | mr: 3,
23 | p: 2,
24 | bg: 'primary.400',
25 | color: 'white',
26 | float: 'left',
27 | },
28 | },
29 | variants: {
30 | subtle: {},
31 | solid: {
32 | container: {
33 | bg: 'primary.400',
34 | },
35 | secondaryAction: {
36 | colorScheme: 'white',
37 | },
38 | },
39 | light: ({ colorMode }: any) => ({
40 | container: {
41 | bg: colorMode === 'dark' ? 'gray.700' : 'gray.100',
42 | },
43 | }),
44 | },
45 | defaultProps: {
46 | variant: 'subtle',
47 | },
48 | }
49 |
50 | export const Feature = {
51 | parts: ['container', 'title', 'description', 'icon'],
52 | baseStyle: (props: any) => ({
53 | container: {
54 | alignItems: 'flex-start',
55 | flexDirection: 'column',
56 | },
57 | title: {
58 | as: 'h4',
59 | fontSize: 'lg',
60 | fontWeight: '700',
61 | mb: 2,
62 | },
63 | description: {
64 | fontSize: 'lg',
65 | fontWeight: 'normal',
66 | color: mode('gray.500', 'gray.400')(props),
67 | },
68 | icon: {
69 | mb: 4,
70 | mr: 4,
71 | p: 2,
72 | bg: mode(
73 | 'primary.100',
74 | transparentize('primary.500', 0.2)(props.theme)
75 | )(props),
76 | color: mode('primary.700', 'primary.400')(props),
77 | float: 'left',
78 | },
79 | }),
80 | variants: {
81 | default: {},
82 | 'left-icon': {
83 | container: {
84 | flexDirection: 'row',
85 | },
86 | },
87 | center: {
88 | container: {
89 | alignItems: 'center',
90 | },
91 | title: {
92 | textAlign: 'center',
93 | },
94 | description: {
95 | textAlign: 'center',
96 | },
97 | },
98 | inline: {
99 | container: {
100 | flexDirection: 'row',
101 | },
102 | title: {
103 | display: 'inline-block',
104 | mr: 1,
105 | mb: 0,
106 | },
107 | description: {
108 | display: 'inline',
109 | },
110 | icon: {
111 | mt: 4,
112 | },
113 | },
114 | light: ({ colorMode }: any) => ({
115 | wrapper: {
116 | bg: colorMode === 'dark' ? 'gray.700' : 'gray.100',
117 | },
118 | }),
119 | },
120 | defaultProps: {
121 | variant: 'default',
122 | },
123 | }
124 |
125 | export default Features
126 |
--------------------------------------------------------------------------------
/theme/components/index.ts:
--------------------------------------------------------------------------------
1 | import { default as Button } from './button'
2 | import { default as CTA } from './cta'
3 | import { default as Features, Feature } from './features'
4 | import { default as SectionTitle } from './section-title'
5 | import { default as Section } from './section'
6 |
7 | export default { Button, CTA, Features, Feature, SectionTitle, Section }
8 |
--------------------------------------------------------------------------------
/theme/components/section-title.ts:
--------------------------------------------------------------------------------
1 | import { mode } from '@chakra-ui/theme-tools'
2 |
3 | const SectionTitle = {
4 | parts: ['wrapper', 'title', 'description'],
5 | baseStyle: {
6 | wrapper: {
7 | spacing: [2, null, 3],
8 | mb: '10',
9 | textAlign: ['left', null, 'center'],
10 | },
11 | title: {
12 | width: '100%',
13 | },
14 | description: {
15 | fontWeight: 'normal',
16 | },
17 | },
18 | variants: {
19 | default: (props: any) => ({
20 | title: {},
21 | description: {
22 | color: mode('gray.500', 'gray.400')(props),
23 | },
24 | }),
25 | dark: {
26 | title: {
27 | color: 'gray.800',
28 | },
29 | description: {
30 | color: 'gray.700',
31 | },
32 | },
33 | light: (props: any) => ({
34 | title: {
35 | color: 'white',
36 | },
37 | description: {
38 | color: 'gray.200',
39 | },
40 | }),
41 | },
42 | defaultProps: {
43 | variant: 'default',
44 | size: 'xl',
45 | },
46 | sizes: {
47 | lg: {
48 | title: {
49 | size: '2xl',
50 | },
51 | description: {
52 | fontSize: 'xl',
53 | },
54 | },
55 | xl: {
56 | wrapper: {
57 | mb: 14,
58 | spacing: [2, null, 3],
59 | },
60 | title: {
61 | fontSize: { base: '2xl', lg: '4xl' },
62 | },
63 | description: {
64 | fontSize: { base: 'xl', lg: '2xl' },
65 | },
66 | },
67 | },
68 | }
69 |
70 | export default SectionTitle
71 |
--------------------------------------------------------------------------------
/theme/components/section.ts:
--------------------------------------------------------------------------------
1 | const Section = {
2 | baseStyle: {
3 | pt: 28,
4 | pb: 28,
5 | px: [4, null],
6 | },
7 | variants: {
8 | subtle: {},
9 | solid: {
10 | bg: 'primary.400',
11 | },
12 | alternate: ({ colorMode }: any) => ({
13 | bg: colorMode === 'dark' ? 'gray.800' : 'gray.50',
14 | }),
15 | },
16 | defaultProps: {
17 | variant: 'subtle',
18 | },
19 | }
20 |
21 | export default Section
22 |
--------------------------------------------------------------------------------
/theme/foundations/typography.ts:
--------------------------------------------------------------------------------
1 | export const fontSizes = {
2 | xs: '0.75rem',
3 | sm: '0.8125rem',
4 | md: '0.875rem',
5 | lg: '1',
6 | xl: '1.125rem',
7 | '2xl': '1.25rem',
8 | '3xl': '1.5rem',
9 | '4xl': '1.875rem',
10 | '5xl': '2',
11 | '6xl': '3.5rem',
12 | '7xl': '3rem',
13 | '8xl': '4rem',
14 | '9xl': '5rem',
15 | '10xl': '6rem',
16 | }
17 |
--------------------------------------------------------------------------------
/theme/index.ts:
--------------------------------------------------------------------------------
1 | import { extendTheme } from '@chakra-ui/react'
2 | import '@fontsource-variable/inter'
3 | import { theme as baseTheme } from '@saas-ui/react'
4 |
5 | import components from './components'
6 | import { fontSizes } from './foundations/typography'
7 |
8 | export const theme = extendTheme(
9 | {
10 | config: {
11 | initialColorMode: 'dark',
12 | useSystemColorMode: false,
13 | },
14 | styles: {
15 | global: (props: any) => ({
16 | body: {
17 | color: 'gray.900',
18 | bg: 'white',
19 | fontSize: 'lg',
20 | _dark: {
21 | color: 'white',
22 | bg: 'gray.900',
23 | },
24 | },
25 | }),
26 | },
27 | fonts: {
28 | heading: 'Inter Variable, Inter, sans-serif',
29 | body: 'Inter Variable, Inter, sans-serif',
30 | },
31 | fontSizes,
32 | components,
33 | },
34 | baseTheme,
35 | )
36 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "noImplicitAny": false,
10 | "noEmit": true,
11 | "esModuleInterop": true,
12 | "module": "esnext",
13 | "moduleResolution": "node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "jsx": "preserve",
17 | "incremental": true,
18 | "baseUrl": ".",
19 | "paths": {
20 | "#components/*": ["./components/*"],
21 | "#hooks/*": ["./hooks/*"],
22 | "#data/*": ["./data/*"],
23 | "#theme": ["./theme"]
24 | },
25 | "plugins": [
26 | {
27 | "name": "next"
28 | }
29 | ]
30 | },
31 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
32 | "exclude": ["node_modules"]
33 | }
34 |
--------------------------------------------------------------------------------