├── public
├── favicon.ico
├── assets
│ └── images
│ │ └── logo.png
├── vercel.svg
├── thirteen.svg
└── next.svg
├── types
├── index.ts
└── propTypes.ts
├── styles
├── globals.css
└── Home.module.css
├── pages
├── products.tsx
├── login.tsx
├── signup.tsx
├── forgot-password.tsx
├── _document.tsx
├── api
│ └── hello.ts
├── index.tsx
└── _app.tsx
├── next.config.js
├── components
├── Form
│ ├── ForgotPasswordElements.ts
│ ├── InputFeildElements.ts
│ ├── ForgotPasswordForm.tsx
│ ├── InputFeild.tsx
│ ├── FormElements.ts
│ ├── LoginForm.tsx
│ └── SignupForm.tsx
├── Button
│ ├── index.tsx
│ └── ButtonElements.ts
├── AppLogoTitle
│ ├── AppLogoTitleElements.ts
│ └── index.tsx
└── Navbar
│ ├── NavLink.tsx
│ ├── NavElements.ts
│ └── index.tsx
├── .gitignore
├── tsconfig.json
├── package.json
├── hooks
└── index.ts
└── README.md
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/esu-coder/nextjs-auth-ui/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/types/index.ts:
--------------------------------------------------------------------------------
1 | export interface WindowSize {
2 | width: number;
3 | height: number;
4 | }
--------------------------------------------------------------------------------
/public/assets/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/esu-coder/nextjs-auth-ui/HEAD/public/assets/images/logo.png
--------------------------------------------------------------------------------
/styles/globals.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | box-sizing: border-box;
4 | }
5 |
6 | a {
7 | text-decoration: none;
8 | color: black;
9 | }
--------------------------------------------------------------------------------
/pages/products.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | type Props = {}
4 |
5 | const products = (props: Props) => {
6 | return (
7 |
products
8 | )
9 | }
10 |
11 | export default products
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | reactStrictMode: true,
4 | compiler: {
5 | styledComponents: true,
6 | },
7 | }
8 |
9 | module.exports = nextConfig
10 |
--------------------------------------------------------------------------------
/pages/login.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import LoginForm from '../components/Form/LoginForm'
3 |
4 | const login = () => {
5 | return (
6 |
7 | )
8 | }
9 |
10 | export default login
--------------------------------------------------------------------------------
/pages/signup.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import SignupForm from '../components/Form/SignupForm'
3 |
4 | const signup = () => {
5 | return (
6 |
7 | )
8 | }
9 |
10 | export default signup
--------------------------------------------------------------------------------
/components/Form/ForgotPasswordElements.ts:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import { Form as ForgotPassForm } from '../Form/FormElements';
3 |
4 | export const Form = styled(ForgotPassForm)`
5 | height: 50%;
6 | `
7 |
8 | export * from '../Form/FormElements'
--------------------------------------------------------------------------------
/pages/forgot-password.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ForgotPasswordForm from '../components/Form/ForgotPasswordForm'
3 |
4 |
5 | const forgotPassword = () => {
6 | return (
7 |
8 | )
9 | }
10 |
11 | export default forgotPassword
--------------------------------------------------------------------------------
/pages/_document.tsx:
--------------------------------------------------------------------------------
1 | import { Html, Head, Main, NextScript } from 'next/document'
2 |
3 | export default function Document() {
4 | return (
5 |
6 |
8 |
9 |
10 |
11 |
12 | )
13 | }
14 |
--------------------------------------------------------------------------------
/components/Button/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { ButtonProps } from '../../types/propTypes'
3 | import { Container } from './ButtonElements'
4 |
5 | const Button = ({ title, type }: ButtonProps) => {
6 | return (
7 |
8 | {title}
9 |
10 | )
11 | }
12 |
13 | export default Button
--------------------------------------------------------------------------------
/pages/api/hello.ts:
--------------------------------------------------------------------------------
1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
2 | import type { NextApiRequest, NextApiResponse } from 'next'
3 |
4 | type Data = {
5 | name: string
6 | }
7 |
8 | export default function handler(
9 | req: NextApiRequest,
10 | res: NextApiResponse
11 | ) {
12 | res.status(200).json({ name: 'John Doe' })
13 | }
14 |
--------------------------------------------------------------------------------
/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import Head from 'next/head'
2 |
3 | export default function Home() {
4 | return (
5 | <>
6 |
7 | Create Next App
8 |
9 |
10 |
11 |
12 |
13 | Home
14 | >
15 | )
16 | }
17 |
--------------------------------------------------------------------------------
/components/Button/ButtonElements.ts:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 |
3 | export const Container = styled.button`
4 | background-color: #23C687;
5 | color: #fff;
6 | font-size: 0.9rem;
7 | padding: 0.8rem;
8 | transition: all 0.5s;
9 | border-radius: 5px;
10 | cursor: pointer;
11 | box-shadow: 0 2px 5px rgba(0, 0, 0, 0.25);
12 | width: 100%;
13 | border: none;
14 |
15 | &:hover {
16 | background-color: #707070;
17 | }
18 | `
--------------------------------------------------------------------------------
/.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 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 | .pnpm-debug.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/types/propTypes.ts:
--------------------------------------------------------------------------------
1 | export interface NavLinkProps {
2 | route: string;
3 | children: React.ReactNode;
4 | color?: string;
5 | large?: boolean;
6 | onClick?: React.MouseEventHandler;
7 | }
8 |
9 | export interface InputProps {
10 | placeholder: string;
11 | icon: React.ReactNode;
12 | type: string;
13 | value: string;
14 | onChange: (event: React.ChangeEvent) => void;
15 | required?: boolean;
16 | }
17 |
18 | export interface ButtonProps {
19 | title: string;
20 | type: 'submit' | 'button' | 'reset';
21 | }
--------------------------------------------------------------------------------
/components/AppLogoTitle/AppLogoTitleElements.ts:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import Image from "next/image";
3 | import Link from "next/link";
4 |
5 | export const Container = styled(Link)`
6 | display: flex;
7 | justify-content: center;
8 | align-items: center;
9 | margin: 1rem 0;
10 | text-decoration: none;
11 | color: #23C687;
12 | `
13 |
14 | export const AppTitle = styled.h2`
15 | font-size: 1.2rem;
16 | `
17 |
18 | export const LogoImage = styled(Image)`
19 | margin-left: 0.8rem;
20 | height: 2.2rem;
21 | width: 2.2rem;
22 | `
--------------------------------------------------------------------------------
/components/AppLogoTitle/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {
3 | AppTitle,
4 | Container,
5 | LogoImage
6 | } from './AppLogoTitleElements'
7 | import LogoImgSrc from '../../public/assets/images/logo.png'
8 |
9 | type Props = {}
10 |
11 | const AppLogoTitle = (props: Props) => {
12 | return (
13 |
14 | OV Travels
15 |
19 |
20 | )
21 | }
22 |
23 | export default AppLogoTitle
--------------------------------------------------------------------------------
/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 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "incremental": true
17 | },
18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
19 | "exclude": ["node_modules"]
20 | }
21 |
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nextjs-auth-ui",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@next/font": "13.1.2",
13 | "@types/node": "18.11.18",
14 | "@types/react": "18.0.26",
15 | "@types/react-dom": "18.0.10",
16 | "next": "13.1.2",
17 | "react": "18.2.0",
18 | "react-dom": "18.2.0",
19 | "react-icons": "^4.7.1",
20 | "styled-components": "^5.3.6",
21 | "typescript": "4.9.4"
22 | },
23 | "devDependencies": {
24 | "@types/styled-components": "^5.1.26"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/components/Navbar/NavLink.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useRouter } from 'next/router'
3 | import { NavLinkProps } from '../../types/propTypes'
4 | import { NavLink as Link } from './NavElements'
5 |
6 | const NavLink = ({ route, children, color, large, onClick }: NavLinkProps) => {
7 | const router = useRouter()
8 | const currentRoute = router.pathname
9 |
10 | return (
11 |
18 | {children}
19 |
20 | )
21 | }
22 |
23 | export default NavLink
--------------------------------------------------------------------------------
/hooks/index.ts:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from "react";
2 | import { WindowSize } from "../types";
3 |
4 | export const useWindowSize = () => {
5 | const [windowSize, setWindowSize] = useState({
6 | width: 0,
7 | height: 0
8 | })
9 |
10 | useEffect(() => {
11 | const handleResize = () => {
12 | setWindowSize({
13 | width: window.innerWidth,
14 | height: window.innerHeight
15 | })
16 | }
17 |
18 | window.addEventListener("resize", handleResize)
19 |
20 | handleResize()
21 |
22 | return () => window.removeEventListener("resize", handleResize)
23 | }, [])
24 |
25 | return windowSize
26 | }
--------------------------------------------------------------------------------
/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import '../styles/globals.css'
2 | import type { AppProps } from 'next/app'
3 | import Navbar from '../components/Navbar'
4 | import { useRouter } from 'next/router'
5 | import { Roboto } from '@next/font/google'
6 | const roboto = Roboto({ weight: '400', subsets: ['latin'] })
7 |
8 | export default function App({ Component, pageProps }: AppProps) {
9 | const router = useRouter()
10 | const { pathname } = router
11 | let showNavbar = true
12 |
13 | if (pathname === '/login' || pathname === '/signup' || pathname === '/forgot-password') {
14 | showNavbar = false
15 | }
16 |
17 | return (
18 |
19 | {showNavbar && }
20 |
21 |
22 | )
23 | }
24 |
--------------------------------------------------------------------------------
/components/Form/InputFeildElements.ts:
--------------------------------------------------------------------------------
1 | import styled, { css } from "styled-components";
2 | import { BsEye, BsEyeSlash } from 'react-icons/bs';
3 |
4 | export const Container = styled.div`
5 | display: flex;
6 | align-items: center;
7 | width: 100%;
8 | position: relative;
9 |
10 | & svg {
11 | position: absolute;
12 | transform: translateX(1rem);
13 | color: rgba(0, 0, 0, 0.65);
14 | }
15 | `
16 |
17 | export const Input = styled.input`
18 | font-size: 0.85rem;
19 | padding: 1rem 3rem;
20 | border: 1px solid rgba(0, 0, 0, 0.25);
21 | border-radius: 4px;
22 | width: 100%;
23 | `
24 |
25 | const EyeIcon = css`
26 | position: absolute;
27 | right: 25px;
28 | color: rgba(0, 0, 0, 0.65);
29 | `
30 |
31 | export const ShowPassIcon = styled(BsEye)`
32 | ${EyeIcon}
33 | `
34 |
35 | export const HidePassIcon = styled(BsEyeSlash)`
36 | ${EyeIcon}
37 | `
38 |
39 |
--------------------------------------------------------------------------------
/public/thirteen.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/components/Form/ForgotPasswordForm.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import { AiOutlineMail } from 'react-icons/ai'
3 | import AppLogoTitle from '../AppLogoTitle'
4 | import Button from '../Button'
5 | import {
6 | Container,
7 | Form,
8 | FormTitle
9 | } from './ForgotPasswordElements'
10 | import InputFeild from './InputFeild'
11 |
12 | const ForgotPasswordForm = () => {
13 | const [email, setEmail] = useState("")
14 |
15 | const handleEmailChange = (event: React.ChangeEvent) => {
16 | setEmail(event.target.value)
17 | }
18 |
19 | const handleSubmit = (event: React.FormEvent) => {
20 | event.preventDefault()
21 | }
22 |
23 | return (
24 |
25 |
26 |
27 |
44 |
45 | )
46 | }
47 |
48 | export default ForgotPasswordForm
--------------------------------------------------------------------------------
/components/Form/InputFeild.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import { InputProps } from '../../types/propTypes'
3 | import {
4 | Container,
5 | HidePassIcon,
6 | Input,
7 | ShowPassIcon
8 | } from './InputFeildElements'
9 |
10 | const InputFeild = ({ placeholder, icon, type, required, value, onChange }: InputProps) => {
11 | const [showPassword, setShowPassword] = useState(false);
12 |
13 | const togglePasswordIcon = () => {
14 | setShowPassword(!showPassword)
15 | }
16 |
17 | const renderPasswordIcon = () => {
18 | if (showPassword) {
19 | return (
20 |
21 | )
22 | }
23 | else {
24 | return (
25 |
26 | )
27 | }
28 | }
29 |
30 | const inputType = type === 'password' && showPassword ? 'text' : type;
31 |
32 | return (
33 |
34 | {icon}
35 |
36 |
43 |
44 | {
45 | type === 'password' &&
46 | renderPasswordIcon()
47 | }
48 |
49 | )
50 | }
51 |
52 | export default InputFeild
--------------------------------------------------------------------------------
/components/Form/FormElements.ts:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 | import NextLink from 'next/link'
3 |
4 | export const Container = styled.div`
5 | display: flex;
6 | flex-direction: column;
7 | justify-content: center;
8 | align-items: center;
9 | height: 100vh;
10 | background-color: #f9f9f9;
11 |
12 | @media (max-width: 768px) {
13 | background-color: #fff;
14 | }
15 | `
16 | export const Form = styled.form`
17 | display: flex;
18 | flex-direction: column;
19 | align-items: center;
20 | justify-content: space-evenly;
21 | padding: 0 1.5rem;
22 | transition: all 0.5s;
23 | background-color: #fff;
24 | width: 25%;
25 | height: 70%;
26 | box-shadow: 0 2px 5px rgba(0, 0, 0, 0.25);
27 | border-radius: 5px;
28 |
29 | @media (max-width: 1024px) {
30 | width: 50%;
31 | }
32 |
33 | @media (max-width: 768px) {
34 | width: 100%;
35 | box-shadow: none;
36 | }
37 | `
38 |
39 | export const FormTitle = styled.h2`
40 | font-size: 1rem;
41 | color: rgba(0, 0, 0, 0.5);
42 | text-align: center;
43 | padding: 1rem;
44 | `
45 |
46 | export const Link = styled(NextLink)`
47 | color: #5eb4c8;
48 | align-self: flex-end;
49 | transition: all 0.5s;
50 | font-size: 0.8rem;
51 |
52 | &:hover {
53 | color: #010606;
54 | }
55 | `
56 |
57 | export const InfoTextContainer = styled.div`
58 | display: flex;
59 | align-items: center;
60 | justify-content: center;
61 | width: 100%;
62 | margin-top: 1rem;
63 | `
64 |
65 | export const InfoText = styled.span`
66 | font-size: 0.8rem;
67 | margin-right: 0.5rem;
68 | `
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | ```
12 |
13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
14 |
15 | You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
16 |
17 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
18 |
19 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
20 |
21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
22 |
23 | ## Learn More
24 |
25 | To learn more about Next.js, take a look at the following resources:
26 |
27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29 |
30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
31 |
32 | ## Deploy on Vercel
33 |
34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35 |
36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
37 |
--------------------------------------------------------------------------------
/components/Navbar/NavElements.ts:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 | import styled from "styled-components";
3 | import { GiHamburgerMenu } from 'react-icons/gi'
4 | import { AiFillCloseCircle } from 'react-icons/ai'
5 |
6 | export const Nav = styled.nav`
7 | height: 80px;
8 | display: flex;
9 | justify-content: space-between;
10 | align-items: center;
11 | box-shadow: 0px 3px 3px 0px rgba(0, 0, 0, 0.25);
12 | `
13 |
14 | export const LogoContainer = styled.div`
15 | padding-left: 1rem;
16 | `
17 |
18 | export const NavLinkContainer = styled.div`
19 | display: flex;
20 | align-items: center;
21 | `
22 |
23 | export const NavLink = styled(Link) <{ large?: boolean }>`
24 | padding: 1rem;
25 | margin-left: 1rem;
26 | font-size: ${props => props.large ? '1.5rem' : '1rem'};
27 | color: ${props => props.color || '#132424'};
28 |
29 | &:hover {
30 | color: #a6a2a2;
31 | }
32 |
33 | &.active {
34 | color: #03a062;
35 | }
36 | `
37 |
38 | export const MenuIcon = styled(GiHamburgerMenu)`
39 | cursor: pointer;
40 | margin: 0 1rem;
41 | `
42 |
43 | export const CloseIcon = styled(AiFillCloseCircle)`
44 | cursor: pointer;
45 | `
46 |
47 | export const OverlayMenu = styled.div`
48 | position: fixed;
49 | top: 0;
50 | left: 0;
51 | width: 100vw;
52 | height: 100vh;
53 | background-color: #132424;
54 |
55 | @media (min-width: 768px) {
56 | display: none;
57 | }
58 | `
59 |
60 | export const MenuLinkContainer = styled.div`
61 | display: flex;
62 | flex-direction: column;
63 | justify-content: center;
64 | align-items: center;
65 | height: 100vh;
66 | `
67 |
68 | export const CloseButtonContainer = styled.div`
69 | width: 100%;
70 | display: flex;
71 | justify-content: flex-end;
72 | padding: 1.5rem;
73 | position: absolute;
74 | `
--------------------------------------------------------------------------------
/components/Form/LoginForm.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import { AiOutlineMail, AiOutlineUnlock } from 'react-icons/ai'
3 | import AppLogoTitle from '../AppLogoTitle'
4 | import Button from '../Button'
5 | import {
6 | Container,
7 | Form,
8 | FormTitle,
9 | InfoText,
10 | InfoTextContainer,
11 | Link
12 | } from './FormElements'
13 | import InputFeild from './InputFeild'
14 |
15 | const LoginForm = () => {
16 | const [email, setEmail] = useState("")
17 | const [password, setPassword] = useState("")
18 |
19 | const handleEmailChange = (event: React.ChangeEvent) => {
20 | setEmail(event.target.value)
21 | }
22 |
23 | const handlePasswordChange = (event: React.ChangeEvent) => {
24 | setPassword(event.target.value)
25 | }
26 |
27 | const handleLogin = (event: React.FormEvent) => {
28 | event.preventDefault()
29 | }
30 |
31 | return (
32 |
33 |
34 |
74 |
75 | )
76 | }
77 |
78 | export default LoginForm
--------------------------------------------------------------------------------
/components/Form/SignupForm.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import { BsPerson } from 'react-icons/bs'
3 | import { AiOutlineMail, AiOutlineUnlock } from 'react-icons/ai'
4 | import { RiLockPasswordLine } from 'react-icons/ri'
5 | import AppLogoTitle from '../AppLogoTitle'
6 | import {
7 | Container,
8 | Form,
9 | FormTitle,
10 | InfoText,
11 | InfoTextContainer,
12 | Link
13 | } from './FormElements'
14 | import InputFeild from './InputFeild'
15 | import Button from '../Button'
16 |
17 |
18 | const SignupForm = () => {
19 | const [fullName, setFullName] = useState("")
20 | const [email, setEmail] = useState("")
21 | const [password, setPassword] = useState("")
22 | const [confirmPassword, setConfirmPassword] = useState("")
23 |
24 | const handleSignup = (event: React.FormEvent) => {
25 | event.preventDefault()
26 | }
27 |
28 | return (
29 |
30 |
31 |
32 |
83 |
84 | )
85 | }
86 |
87 | export default SignupForm
--------------------------------------------------------------------------------
/components/Navbar/index.tsx:
--------------------------------------------------------------------------------
1 | import Image from 'next/image'
2 | import React, { useState } from 'react'
3 | import { useWindowSize } from '../../hooks'
4 | import LogoImage from '../../public/assets/images/logo.png'
5 | import { WindowSize } from '../../types'
6 | import {
7 | CloseButtonContainer,
8 | CloseIcon,
9 | LogoContainer,
10 | MenuIcon,
11 | MenuLinkContainer,
12 | Nav,
13 | NavLinkContainer,
14 | OverlayMenu
15 | } from './NavElements'
16 |
17 | import NavLink from './NavLink'
18 |
19 | const Navbar = () => {
20 |
21 | const size: WindowSize = useWindowSize()
22 | const [showMenu, setShowMenu] = useState(false)
23 |
24 | const openMenu = () => {
25 | setShowMenu(true)
26 | }
27 |
28 | const closeMenu = () => {
29 | setShowMenu(false)
30 | }
31 |
32 | return (
33 |
108 | )
109 | }
110 |
111 | export default Navbar
--------------------------------------------------------------------------------
/styles/Home.module.css:
--------------------------------------------------------------------------------
1 | .main {
2 | display: flex;
3 | flex-direction: column;
4 | justify-content: space-between;
5 | align-items: center;
6 | padding: 6rem;
7 | min-height: 100vh;
8 | }
9 |
10 | .description {
11 | display: inherit;
12 | justify-content: inherit;
13 | align-items: inherit;
14 | font-size: 0.85rem;
15 | max-width: var(--max-width);
16 | width: 100%;
17 | z-index: 2;
18 | font-family: var(--font-mono);
19 | }
20 |
21 | .description a {
22 | display: flex;
23 | justify-content: center;
24 | align-items: center;
25 | gap: 0.5rem;
26 | }
27 |
28 | .description p {
29 | position: relative;
30 | margin: 0;
31 | padding: 1rem;
32 | background-color: rgba(var(--callout-rgb), 0.5);
33 | border: 1px solid rgba(var(--callout-border-rgb), 0.3);
34 | border-radius: var(--border-radius);
35 | }
36 |
37 | .code {
38 | font-weight: 700;
39 | font-family: var(--font-mono);
40 | }
41 |
42 | .grid {
43 | display: grid;
44 | grid-template-columns: repeat(4, minmax(25%, auto));
45 | width: var(--max-width);
46 | max-width: 100%;
47 | }
48 |
49 | .card {
50 | padding: 1rem 1.2rem;
51 | border-radius: var(--border-radius);
52 | background: rgba(var(--card-rgb), 0);
53 | border: 1px solid rgba(var(--card-border-rgb), 0);
54 | transition: background 200ms, border 200ms;
55 | }
56 |
57 | .card span {
58 | display: inline-block;
59 | transition: transform 200ms;
60 | }
61 |
62 | .card h2 {
63 | font-weight: 600;
64 | margin-bottom: 0.7rem;
65 | }
66 |
67 | .card p {
68 | margin: 0;
69 | opacity: 0.6;
70 | font-size: 0.9rem;
71 | line-height: 1.5;
72 | max-width: 30ch;
73 | }
74 |
75 | .center {
76 | display: flex;
77 | justify-content: center;
78 | align-items: center;
79 | position: relative;
80 | padding: 4rem 0;
81 | }
82 |
83 | .center::before {
84 | background: var(--secondary-glow);
85 | border-radius: 50%;
86 | width: 480px;
87 | height: 360px;
88 | margin-left: -400px;
89 | }
90 |
91 | .center::after {
92 | background: var(--primary-glow);
93 | width: 240px;
94 | height: 180px;
95 | z-index: -1;
96 | }
97 |
98 | .center::before,
99 | .center::after {
100 | content: '';
101 | left: 50%;
102 | position: absolute;
103 | filter: blur(45px);
104 | transform: translateZ(0);
105 | }
106 |
107 | .logo,
108 | .thirteen {
109 | position: relative;
110 | }
111 |
112 | .thirteen {
113 | display: flex;
114 | justify-content: center;
115 | align-items: center;
116 | width: 75px;
117 | height: 75px;
118 | padding: 25px 10px;
119 | margin-left: 16px;
120 | transform: translateZ(0);
121 | border-radius: var(--border-radius);
122 | overflow: hidden;
123 | box-shadow: 0px 2px 8px -1px #0000001a;
124 | }
125 |
126 | .thirteen::before,
127 | .thirteen::after {
128 | content: '';
129 | position: absolute;
130 | z-index: -1;
131 | }
132 |
133 | /* Conic Gradient Animation */
134 | .thirteen::before {
135 | animation: 6s rotate linear infinite;
136 | width: 200%;
137 | height: 200%;
138 | background: var(--tile-border);
139 | }
140 |
141 | /* Inner Square */
142 | .thirteen::after {
143 | inset: 0;
144 | padding: 1px;
145 | border-radius: var(--border-radius);
146 | background: linear-gradient(
147 | to bottom right,
148 | rgba(var(--tile-start-rgb), 1),
149 | rgba(var(--tile-end-rgb), 1)
150 | );
151 | background-clip: content-box;
152 | }
153 |
154 | /* Enable hover only on non-touch devices */
155 | @media (hover: hover) and (pointer: fine) {
156 | .card:hover {
157 | background: rgba(var(--card-rgb), 0.1);
158 | border: 1px solid rgba(var(--card-border-rgb), 0.15);
159 | }
160 |
161 | .card:hover span {
162 | transform: translateX(4px);
163 | }
164 | }
165 |
166 | @media (prefers-reduced-motion) {
167 | .thirteen::before {
168 | animation: none;
169 | }
170 |
171 | .card:hover span {
172 | transform: none;
173 | }
174 | }
175 |
176 | /* Mobile */
177 | @media (max-width: 700px) {
178 | .content {
179 | padding: 4rem;
180 | }
181 |
182 | .grid {
183 | grid-template-columns: 1fr;
184 | margin-bottom: 120px;
185 | max-width: 320px;
186 | text-align: center;
187 | }
188 |
189 | .card {
190 | padding: 1rem 2.5rem;
191 | }
192 |
193 | .card h2 {
194 | margin-bottom: 0.5rem;
195 | }
196 |
197 | .center {
198 | padding: 8rem 0 6rem;
199 | }
200 |
201 | .center::before {
202 | transform: none;
203 | height: 300px;
204 | }
205 |
206 | .description {
207 | font-size: 0.8rem;
208 | }
209 |
210 | .description a {
211 | padding: 1rem;
212 | }
213 |
214 | .description p,
215 | .description div {
216 | display: flex;
217 | justify-content: center;
218 | position: fixed;
219 | width: 100%;
220 | }
221 |
222 | .description p {
223 | align-items: center;
224 | inset: 0 0 auto;
225 | padding: 2rem 1rem 1.4rem;
226 | border-radius: 0;
227 | border: none;
228 | border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25);
229 | background: linear-gradient(
230 | to bottom,
231 | rgba(var(--background-start-rgb), 1),
232 | rgba(var(--callout-rgb), 0.5)
233 | );
234 | background-clip: padding-box;
235 | backdrop-filter: blur(24px);
236 | }
237 |
238 | .description div {
239 | align-items: flex-end;
240 | pointer-events: none;
241 | inset: auto 0 0;
242 | padding: 2rem;
243 | height: 200px;
244 | background: linear-gradient(
245 | to bottom,
246 | transparent 0%,
247 | rgb(var(--background-end-rgb)) 40%
248 | );
249 | z-index: 1;
250 | }
251 | }
252 |
253 | /* Tablet and Smaller Desktop */
254 | @media (min-width: 701px) and (max-width: 1120px) {
255 | .grid {
256 | grid-template-columns: repeat(2, 50%);
257 | }
258 | }
259 |
260 | @media (prefers-color-scheme: dark) {
261 | .vercelLogo {
262 | filter: invert(1);
263 | }
264 |
265 | .logo,
266 | .thirteen img {
267 | filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70);
268 | }
269 | }
270 |
271 | @keyframes rotate {
272 | from {
273 | transform: rotate(360deg);
274 | }
275 | to {
276 | transform: rotate(0deg);
277 | }
278 | }
279 |
--------------------------------------------------------------------------------