diff --git a/README.md b/README.md
index a75ac524..b1b17b9f 100644
--- a/README.md
+++ b/README.md
@@ -1,40 +1,56 @@
-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).
+# πΌ νλ€λ§μΌ
-## Getting Started
+> μΌμμ λͺ¨λ 물건μ λ―Ώκ³ κ±°λν μ μλ **μ€κ³ κ±°λ νλ«νΌ**
-First, run the development server:
+- [νλ€λ§μΌ λ°λ‘κ°κΈ°](https://codeit-fe10-pandamarket.vercel.app)
+- μ½λμ μ€νλ¦°νΈ FE-10 μ€νλ¦°νΈ λ―Έμ
+- 2024.08.05.(μ) ~ κ°λ° μ§ν μ€
-```bash
-npm run dev
-# or
-yarn dev
-# or
-pnpm dev
-# or
-bun dev
-```
-
-Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
-
-You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
+![νλ€λ§μΌ μ΄λ―Έμ§](/public/readme_banner.png)
-[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`.
+
-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.
+## κΈ°μ μ€ν
-This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
+- **κ°λ° νκ²½**
+ - Visual Studio Code
+ - Git, Github
+ - Vercel
+- **FE κΈ°μ **
+ - HTML, CSS, CSS Module
+ - JS, TS, React, Next.js
-## Learn More
+
-To learn more about Next.js, take a look at the following resources:
+## λ€μ΄λ° κ·μΉ
-- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
-- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
+- **HTML**
+ - id: camelCase
+ - name: camelCase
+ - class: `camelElement_camelModifier`
+- **CSS**
+ - λ³μ: kebab-case
+- **JS**
+ - μλ³μ: camelCase
+ - νμ
: PascalCase
-You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
+
-## Deploy on Vercel
+## νλ‘μ νΈ κ΅¬μ‘°
-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.
-
-Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
+```
+codeit-fe10-sprint-mission
+ββ .github : GitHub μ€μ
+ββ public : μ μ νμΌ
+β ββ fonts : κΈκΌ΄
+β ββ icons : μμ΄μ½
+β ββ images : μ΄λ―Έμ§
+β ββ meta : λ©νλ°μ΄ν°
+ββ src : μμ€ μ½λ
+ ββ apis : ν΅μ API
+ ββ components : μ»΄ν¬λνΈ
+ β ββ common : κ³΅μ© μ»΄ν¬λνΈ
+ β ββ layout : λ μ΄μμ μ»΄ν¬λνΈ
+ ββ pages : νμ΄μ§
+ ββ styles : μ μ CSS
+```
diff --git a/next.config.js b/next.config.js
index a843cbee..33f36e46 100644
--- a/next.config.js
+++ b/next.config.js
@@ -1,6 +1,15 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
-}
+ images: {
+ remotePatterns: [
+ {
+ protocol: "https",
+ hostname: "sprint-fe-project.s3.ap-northeast-2.amazonaws.com",
+ pathname: "/Sprint_Mission/user/**",
+ },
+ ],
+ },
+};
-module.exports = nextConfig
+module.exports = nextConfig;
diff --git a/pages/_app.tsx b/pages/_app.tsx
deleted file mode 100644
index 021681f4..00000000
--- a/pages/_app.tsx
+++ /dev/null
@@ -1,6 +0,0 @@
-import '@/styles/globals.css'
-import type { AppProps } from 'next/app'
-
-export default function App({ Component, pageProps }: AppProps) {
- return
-}
diff --git a/pages/_document.tsx b/pages/_document.tsx
deleted file mode 100644
index 54e8bf3e..00000000
--- a/pages/_document.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import { Html, Head, Main, NextScript } from 'next/document'
-
-export default function Document() {
- return (
-
-
-
-
-
-
- )
-}
diff --git a/pages/api/hello.ts b/pages/api/hello.ts
deleted file mode 100644
index f8bcc7e5..00000000
--- a/pages/api/hello.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
-import type { NextApiRequest, NextApiResponse } from 'next'
-
-type Data = {
- name: string
-}
-
-export default function handler(
- req: NextApiRequest,
- res: NextApiResponse
-) {
- res.status(200).json({ name: 'John Doe' })
-}
diff --git a/pages/index.tsx b/pages/index.tsx
deleted file mode 100644
index 02c4dee0..00000000
--- a/pages/index.tsx
+++ /dev/null
@@ -1,114 +0,0 @@
-import Head from 'next/head'
-import Image from 'next/image'
-import { Inter } from 'next/font/google'
-import styles from '@/styles/Home.module.css'
-
-const inter = Inter({ subsets: ['latin'] })
-
-export default function Home() {
- return (
- <>
-
- Create Next App
-
-
-
-
-
-
-
- Get started by editing
- pages/index.tsx
-
-
-
-
-
-
-
-
-
-
- >
- )
-}
diff --git a/public/favicon.ico b/public/favicon.ico
deleted file mode 100644
index 718d6fea..00000000
Binary files a/public/favicon.ico and /dev/null differ
diff --git a/public/fonts/PretendardVariable.woff2 b/public/fonts/PretendardVariable.woff2
new file mode 100644
index 00000000..49c54b51
Binary files /dev/null and b/public/fonts/PretendardVariable.woff2 differ
diff --git a/public/icons/heart_inactive.svg b/public/icons/heart_inactive.svg
new file mode 100644
index 00000000..7b994fbe
--- /dev/null
+++ b/public/icons/heart_inactive.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/images/logo_w153x3.png b/public/images/logo_w153x3.png
new file mode 100644
index 00000000..977cd291
Binary files /dev/null and b/public/images/logo_w153x3.png differ
diff --git a/public/images/profile_image_w40x3.png b/public/images/profile_image_w40x3.png
new file mode 100644
index 00000000..86688500
Binary files /dev/null and b/public/images/profile_image_w40x3.png differ
diff --git a/public/meta/favicon.ico b/public/meta/favicon.ico
new file mode 100644
index 00000000..2c61bbdc
Binary files /dev/null and b/public/meta/favicon.ico differ
diff --git a/public/meta/opengraph_image.png b/public/meta/opengraph_image.png
new file mode 100644
index 00000000..7c2cb11d
Binary files /dev/null and b/public/meta/opengraph_image.png differ
diff --git a/public/meta/twitter_image.png b/public/meta/twitter_image.png
new file mode 100644
index 00000000..f53246f1
Binary files /dev/null and b/public/meta/twitter_image.png differ
diff --git a/public/next.svg b/public/next.svg
deleted file mode 100644
index 5174b28c..00000000
--- a/public/next.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/public/readme_banner.png b/public/readme_banner.png
new file mode 100644
index 00000000..8faaa19c
Binary files /dev/null and b/public/readme_banner.png differ
diff --git a/public/vercel.svg b/public/vercel.svg
deleted file mode 100644
index d2f84222..00000000
--- a/public/vercel.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/src/apis/apis.ts b/src/apis/apis.ts
new file mode 100644
index 00000000..f7dd1088
--- /dev/null
+++ b/src/apis/apis.ts
@@ -0,0 +1,32 @@
+import { StringObj, GetArticlesRes, GetArticlesParams } from "./apis.type";
+
+const BASE_URL = "https://panda-market-api.vercel.app/";
+
+const PATH = {
+ ARTICLE: "articles",
+};
+
+async function processResponse(response: Response) {
+ if (!response.ok) throw Error(`${response.status}: ${response.statusText}`);
+
+ return await response.json();
+}
+
+export async function getArticles({
+ page = 1,
+ pageSize = 10,
+ orderBy = "recent",
+ keyword,
+}: GetArticlesParams): Promise {
+ const url = new URL(PATH.ARTICLE, BASE_URL);
+ const paramObj: StringObj = {
+ page: String(page),
+ pageSize: String(pageSize),
+ orderBy,
+ ...(keyword && { keyword }),
+ };
+ url.search = String(new URLSearchParams(paramObj));
+
+ const response = await fetch(url);
+ return processResponse(response);
+}
diff --git a/src/apis/apis.type.ts b/src/apis/apis.type.ts
new file mode 100644
index 00000000..b928b8b1
--- /dev/null
+++ b/src/apis/apis.type.ts
@@ -0,0 +1,29 @@
+export type StringObj = Record;
+
+interface GetListParams {
+ page: number;
+ pageSize: number;
+ orderBy: ItemOrder;
+ keyword?: string;
+}
+interface GetListRes {
+ totalCount: number;
+ list: ItemProps[];
+}
+
+export type ArticleOrderType = "recent" | "like";
+export interface ArticleProps {
+ id: number;
+ title: string;
+ content: string;
+ image: string | null;
+ likeCount: number;
+ createdAt: string;
+ updatedAt: string;
+ writer: {
+ id: number;
+ nickname: string;
+ };
+}
+export type GetArticlesParams = GetListParams;
+export type GetArticlesRes = GetListRes;
diff --git a/src/components/layout/Header.module.css b/src/components/layout/Header.module.css
new file mode 100644
index 00000000..3062c32d
--- /dev/null
+++ b/src/components/layout/Header.module.css
@@ -0,0 +1,64 @@
+.header {
+ position: fixed;
+ inset: 0 0 auto 0;
+ z-index: 1;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 48px;
+ min-width: 375px;
+ height: 70px;
+ padding: 8px calc((100% - 1100px) / 2);
+ border-bottom: 1px solid var(--gray-300);
+ background-color: white;
+}
+@media (max-width: 1199px) {
+ .header {
+ gap: 32px;
+ padding: 8px max(calc((100% - 1100px) / 2), 24px);
+ }
+}
+@media (max-width: 767px) {
+ .header {
+ gap: 8px;
+ padding: 8px 16px;
+ }
+}
+
+.logo {
+ width: 153px;
+ height: 40px;
+ background: url("/images/logo_w153x3.png") no-repeat center right / 153px;
+}
+@media (max-width: 767px) {
+ .logo {
+ width: 84px;
+ background-size: 120px;
+ }
+}
+
+.nav {
+ flex-grow: 1;
+}
+
+.navList {
+ display: flex;
+ gap: 32px;
+ font-size: var(--size-2lg);
+ font-weight: bold;
+ color: var(--gray-600);
+}
+@media (max-width: 767px) {
+ .navList {
+ gap: 8px;
+ font-size: var(--size-lg);
+ }
+}
+
+.navItem_active {
+ color: var(--blue-100);
+}
+
+.profile {
+ width: 40px;
+}
diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx
new file mode 100644
index 00000000..5bbd57c2
--- /dev/null
+++ b/src/components/layout/Header.tsx
@@ -0,0 +1,36 @@
+import Link from "next/link";
+import Image from "next/image";
+import { useRouter } from "next/router";
+import profileImage from "#/images/profile_image_w40x3.png";
+import styles from "./Header.module.css";
+
+export default function Header() {
+ const path = useRouter().pathname;
+
+ return (
+
+
+
+
+
+ );
+}
diff --git a/src/components/layout/Layout.tsx b/src/components/layout/Layout.tsx
new file mode 100644
index 00000000..3294a21b
--- /dev/null
+++ b/src/components/layout/Layout.tsx
@@ -0,0 +1,15 @@
+import { ReactNode } from "react";
+import Header from "./Header";
+
+interface LayoutProps {
+ children: ReactNode;
+}
+
+export default function Layout({ children }: LayoutProps) {
+ return (
+ <>
+
+ {children}
+ >
+ );
+}
diff --git a/src/components/pages/boards/BestItems.module.css b/src/components/pages/boards/BestItems.module.css
new file mode 100644
index 00000000..837e3bf3
--- /dev/null
+++ b/src/components/pages/boards/BestItems.module.css
@@ -0,0 +1,34 @@
+.section {
+ width: 1200px;
+}
+@media (max-width: 1199px) {
+ .section {
+ width: 100%;
+ }
+}
+
+.head {
+ margin-bottom: 16px;
+}
+
+.title {
+ font-size: var(--size-xl);
+ font-weight: bold;
+}
+
+.body {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 24px;
+}
+@media (max-width: 1199px) {
+ .body {
+ grid-template-columns: repeat(2, 1fr);
+ gap: 16px;
+ }
+}
+@media (max-width: 767px) {
+ .body {
+ grid-template-columns: repeat(1, 1fr);
+ }
+}
diff --git a/src/components/pages/boards/BestItems.tsx b/src/components/pages/boards/BestItems.tsx
new file mode 100644
index 00000000..38f4c7f4
--- /dev/null
+++ b/src/components/pages/boards/BestItems.tsx
@@ -0,0 +1,44 @@
+import { useState, useEffect } from "react";
+import { useMediaQuery } from "@/hooks/useMediaQuery";
+import { getArticles } from "@/apis/apis";
+import { GetArticlesParams, GetArticlesRes } from "@/apis/apis.type";
+import { useQuery } from "@/hooks/useQuery";
+import BestItem from "./componnets/BestItem";
+import styles from "./BestItems.module.css";
+
+const pageSizeTable = { PC: 3, TABLET: 2, MOBILE: 1 };
+
+export default function BestItems() {
+ const media = useMediaQuery();
+ const [paramObj, setParamObj] = useState({
+ page: 1,
+ pageSize: pageSizeTable[media],
+ orderBy: "like",
+ });
+ const { isLoading, error, data } = useQuery<
+ GetArticlesParams,
+ GetArticlesRes
+ >(getArticles, paramObj);
+
+ useEffect(() => {
+ setParamObj((prevObj) => ({
+ ...prevObj,
+ pageSize: pageSizeTable[media],
+ }));
+ }, [media]);
+
+ return (
+
+
+
λ² μ€νΈ κ²μκΈ
+
+ {!isLoading && !error && data && (
+
+ {data.list.map((item) => (
+
+ ))}
+
+ )}
+
+ );
+}
diff --git a/src/components/pages/boards/componnets/BestItem.module.css b/src/components/pages/boards/componnets/BestItem.module.css
new file mode 100644
index 00000000..85e3a17f
--- /dev/null
+++ b/src/components/pages/boards/componnets/BestItem.module.css
@@ -0,0 +1,44 @@
+.item {
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+}
+
+.imageWrapper {
+ position: relative;
+ width: 72px;
+ height: 72px;
+ border-radius: 8px;
+ border: 1px solid var(--gray-200);
+}
+
+.image {
+ object-fit: contain;
+}
+
+.content {
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+}
+
+.title {
+ font-size: var(--size-md);
+}
+
+.price {
+ font-size: var(--size-lg);
+ font-weight: bold;
+}
+
+.like {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ font-size: var(--size-xs);
+}
+
+.likeIcon {
+ width: 16px;
+ height: 16px;
+}
diff --git a/src/components/pages/boards/componnets/BestItem.tsx b/src/components/pages/boards/componnets/BestItem.tsx
new file mode 100644
index 00000000..1a82c1a8
--- /dev/null
+++ b/src/components/pages/boards/componnets/BestItem.tsx
@@ -0,0 +1,40 @@
+import Link from "next/link";
+import Image from "next/image";
+import heartIcon from "#/icons/heart_inactive.svg";
+import styles from "./BestItem.module.css";
+import { ArticleProps } from "@/apis/apis.type";
+
+interface ItemProps {
+ data: ArticleProps;
+}
+
+export default function BestItem({ data }: ItemProps) {
+ const {
+ id,
+ title,
+ writer: { nickname },
+ updatedAt,
+ image,
+ likeCount,
+ } = data;
+ return (
+
+
+
+
+
+
{title}
+
{updatedAt}
+
+
+ {likeCount}
+
+
+
+ );
+}
diff --git a/src/hooks/useMediaQuery.ts b/src/hooks/useMediaQuery.ts
new file mode 100644
index 00000000..38c9205c
--- /dev/null
+++ b/src/hooks/useMediaQuery.ts
@@ -0,0 +1,28 @@
+import { useState, useEffect } from "react";
+
+export type MediaType = "PC" | "TABLET" | "MOBILE";
+
+function checkMedia(width: number): MediaType {
+ if (width >= 1200) return "PC";
+ if (width >= 768) return "TABLET";
+ return "MOBILE";
+}
+
+export function useMediaQuery() {
+ const [media, setMedia] = useState("PC");
+
+ useEffect(() => {
+ setMedia(checkMedia(window.innerWidth));
+
+ const handleWindowResize = () => {
+ setMedia(checkMedia(window.innerWidth));
+ };
+
+ window.addEventListener("resize", handleWindowResize);
+ return () => {
+ window.removeEventListener("resize", handleWindowResize);
+ };
+ }, []);
+
+ return media;
+}
diff --git a/src/hooks/useQuery.ts b/src/hooks/useQuery.ts
new file mode 100644
index 00000000..d506cdd1
--- /dev/null
+++ b/src/hooks/useQuery.ts
@@ -0,0 +1,37 @@
+import { useState, useCallback, useEffect } from "react";
+
+export function useQuery(
+ fetchFunc: (paramObj: Params) => Promise,
+ paramObj: Params
+) {
+ const [isLoading, setIsLoading] = useState(true);
+ const [error, setError] = useState(null);
+ const [data, setData] = useState(null);
+
+ const wrappedFunc = useCallback(
+ async (paramObj: Params) => {
+ try {
+ setIsLoading(true);
+ setError(null);
+ const result = await fetchFunc(paramObj);
+ setData(result);
+ } catch (e) {
+ if (e instanceof Error) {
+ setError(e);
+ console.error(e.message);
+ }
+ } finally {
+ setIsLoading(false);
+ }
+ },
+ [fetchFunc]
+ );
+
+ const update = () => wrappedFunc(paramObj);
+
+ useEffect(() => {
+ wrappedFunc(paramObj);
+ }, [wrappedFunc, paramObj]);
+
+ return { isLoading, error, data, update };
+}
diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx
new file mode 100644
index 00000000..f0671c05
--- /dev/null
+++ b/src/pages/_app.tsx
@@ -0,0 +1,35 @@
+import type { AppProps } from "next/app";
+import localFont from "next/font/local";
+import Head from "next/head";
+import Layout from "@/components/layout/Layout";
+import "@/styles/reset.css";
+import "@/styles/variable.css";
+import "@/styles/global.css";
+
+export const pretendard = localFont({
+ src: "../../public/fonts/PretendardVariable.woff2",
+ display: "swap",
+ weight: "45 920",
+});
+
+type MyAppProps = AppProps & {
+ Component: { isNotLayout?: boolean };
+};
+
+export default function App({ Component, pageProps }: MyAppProps) {
+ return (
+ <>
+
+
+
+
+ {Component.isNotLayout ? (
+
+ ) : (
+
+
+
+ )}
+ >
+ );
+}
diff --git a/src/pages/_document.tsx b/src/pages/_document.tsx
new file mode 100644
index 00000000..7656733a
--- /dev/null
+++ b/src/pages/_document.tsx
@@ -0,0 +1,38 @@
+import { Html, Head, Main, NextScript } from "next/document";
+import { pretendard } from "@/pages/_app";
+
+export default function Document() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/pages/boards.module.css b/src/pages/boards.module.css
new file mode 100644
index 00000000..e0b9ff15
--- /dev/null
+++ b/src/pages/boards.module.css
@@ -0,0 +1,21 @@
+.main {
+ display: flex;
+ flex-direction: column;
+ gap: 40px;
+ align-items: center;
+ margin-top: 70px;
+ padding: 24px 0 48px;
+ color: var(--gray-800);
+}
+@media (max-width: 1199px) {
+ .main {
+ gap: 32px;
+ padding: 24px 24px 48px;
+ }
+}
+@media (max-width: 767px) {
+ .main {
+ gap: 24px;
+ padding: 16px 16px 48px;
+ }
+}
diff --git a/src/pages/boards.tsx b/src/pages/boards.tsx
new file mode 100644
index 00000000..520513cb
--- /dev/null
+++ b/src/pages/boards.tsx
@@ -0,0 +1,16 @@
+import Head from "next/head";
+import BestItems from "@/components/pages/boards/BestItems";
+import styles from "./boards.module.css";
+
+export default function Boards() {
+ return (
+ <>
+
+ μμ κ²μν - νλ€λ§μΌ
+
+
+
+
+ >
+ );
+}
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
new file mode 100644
index 00000000..d678968b
--- /dev/null
+++ b/src/pages/index.tsx
@@ -0,0 +1,12 @@
+import Head from "next/head";
+
+export default function Home() {
+ return (
+ <>
+
+ νλ€λ§μΌ
+
+ ννμ΄μ§
+ >
+ );
+}
diff --git a/src/pages/items.tsx b/src/pages/items.tsx
new file mode 100644
index 00000000..b28db839
--- /dev/null
+++ b/src/pages/items.tsx
@@ -0,0 +1,12 @@
+import Head from "next/head";
+
+export default function Items() {
+ return (
+ <>
+
+ μ€κ³ λ§μΌ - νλ€λ§μΌ
+
+ μ€κ³ λ§μΌ
+ >
+ );
+}
diff --git a/src/styles/global.css b/src/styles/global.css
new file mode 100644
index 00000000..59be299c
--- /dev/null
+++ b/src/styles/global.css
@@ -0,0 +1,45 @@
+/* ν
μ€νΈ μμ μλ μ€λ°κΏ μμ μ€μ */
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+span,
+p,
+a,
+li {
+ text-wrap: wrap;
+ word-break: keep-all;
+ overflow-wrap: break-word;
+}
+
+/* address μμ μ€νμΌλ§ μ΄κΈ°ν */
+
+address {
+ font-style: inherit;
+}
+
+/* μΉ μ κ·Όμ± κ³ λ € μμ μν */
+
+.blind {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ margin: -1px;
+ overflow: hidden;
+ clip-path: polygon(0 0, 0 0, 0 0);
+}
+
+/* λ·°ν¬νΈ yμΆ μ€ν¬λ‘€λ° νμ */
+
+body {
+ overflow-y: scroll;
+}
+
+/* νλ©΄μ μ΅μ λλΉ μ ν */
+
+body {
+ min-width: 375px;
+}
diff --git a/src/styles/reset.css b/src/styles/reset.css
new file mode 100644
index 00000000..19ce0f3f
--- /dev/null
+++ b/src/styles/reset.css
@@ -0,0 +1,375 @@
+/* https://unpkg.com/tailwindcss@3.4.14/src/css/preflight.css */
+
+/*
+1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
+2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
+*/
+
+*,
+::before,
+::after {
+ box-sizing: border-box; /* 1 */
+ border-width: 0; /* 2 */
+ border-style: solid; /* 2 */
+ border-color: currentColor; /* 2 */
+}
+
+/*
+1. Use a consistent sensible line-height in all browsers.
+2. Prevent adjustments of font size after orientation changes in iOS.
+3. Use a more readable tab size.
+4. Use the user's configured `sans` font-family by default.
+5. Use the user's configured `sans` font-feature-settings by default.
+6. Use the user's configured `sans` font-variation-settings by default.
+7. Disable tap highlights on iOS
+*/
+
+html,
+:host {
+ line-height: 1.5; /* 1 */
+ -webkit-text-size-adjust: 100%; /* 2 */
+ -moz-tab-size: 4; /* 3 */
+ tab-size: 4; /* 3 */
+ font-family: sans-serif; /* 4 */
+ font-feature-settings: normal; /* 5 */
+ font-variation-settings: normal; /* 6 */
+ -webkit-tap-highlight-color: transparent; /* 7 */
+}
+
+/*
+1. Remove the margin in all browsers.
+2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.
+*/
+
+body {
+ margin: 0; /* 1 */
+ line-height: inherit; /* 2 */
+}
+
+/*
+1. Add the correct height in Firefox.
+2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
+3. Ensure horizontal rules are visible by default.
+*/
+
+hr {
+ height: 0; /* 1 */
+ color: inherit; /* 2 */
+ border-top-width: 1px; /* 3 */
+}
+
+/*
+Remove the default font size and weight for headings.
+*/
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-size: inherit;
+ font-weight: inherit;
+}
+
+/*
+Reset links to optimize for opt-in styling instead of opt-out.
+*/
+
+a {
+ color: inherit;
+ text-decoration: inherit;
+}
+
+/*
+Add the correct font weight in Edge and Safari.
+*/
+
+b,
+strong {
+ font-weight: bolder;
+}
+
+/*
+1. Use the user's configured `mono` font-family by default.
+2. Use the user's configured `mono` font-feature-settings by default.
+3. Use the user's configured `mono` font-variation-settings by default.
+4. Correct the odd `em` font sizing in all browsers.
+*/
+
+code,
+kbd,
+samp,
+pre {
+ font-family: monospace; /* 1 */
+ font-feature-settings: normal; /* 2 */
+ font-variation-settings: normal; /* 3 */
+ font-size: 1em; /* 4 */
+}
+
+/*
+Add the correct font size in all browsers.
+*/
+
+small {
+ font-size: 80%;
+}
+
+/*
+Prevent `sub` and `sup` elements from affecting the line height in all browsers.
+*/
+
+sub,
+sup {
+ font-size: 75%;
+ line-height: 0;
+ position: relative;
+ vertical-align: baseline;
+}
+
+sub {
+ bottom: -0.25em;
+}
+
+sup {
+ top: -0.5em;
+}
+
+/*
+1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
+2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
+3. Remove gaps between table borders by default.
+*/
+
+table {
+ text-indent: 0; /* 1 */
+ border-color: inherit; /* 2 */
+ border-collapse: collapse; /* 3 */
+}
+
+/*
+1. Change the font styles in all browsers.
+2. Remove the margin in Firefox and Safari.
+3. Remove default padding in all browsers.
+*/
+
+button,
+input,
+optgroup,
+select,
+textarea {
+ font-family: inherit; /* 1 */
+ font-feature-settings: inherit; /* 1 */
+ font-variation-settings: inherit; /* 1 */
+ font-size: 100%; /* 1 */
+ font-weight: inherit; /* 1 */
+ line-height: inherit; /* 1 */
+ letter-spacing: inherit; /* 1 */
+ color: inherit; /* 1 */
+ margin: 0; /* 2 */
+ padding: 0; /* 3 */
+}
+
+/*
+Remove the inheritance of text transform in Edge and Firefox.
+*/
+
+button,
+select {
+ text-transform: none;
+}
+
+/*
+1. Correct the inability to style clickable types in iOS and Safari.
+2. Remove default button styles.
+*/
+
+button,
+input:where([type="button"]),
+input:where([type="reset"]),
+input:where([type="submit"]) {
+ -webkit-appearance: button; /* 1 */
+ background-color: transparent; /* 2 */
+ background-image: none; /* 2 */
+}
+
+/*
+Use the modern Firefox focus style for all focusable elements.
+*/
+
+:-moz-focusring {
+ outline: auto;
+}
+
+/*
+Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)
+*/
+
+:-moz-ui-invalid {
+ box-shadow: none;
+}
+
+/*
+Add the correct vertical alignment in Chrome and Firefox.
+*/
+
+progress {
+ vertical-align: baseline;
+}
+
+/*
+Correct the cursor style of increment and decrement buttons in Safari.
+*/
+
+::-webkit-inner-spin-button,
+::-webkit-outer-spin-button {
+ height: auto;
+}
+
+/*
+1. Correct the odd appearance in Chrome and Safari.
+2. Correct the outline style in Safari.
+*/
+
+[type="search"] {
+ -webkit-appearance: textfield; /* 1 */
+ outline-offset: -2px; /* 2 */
+}
+
+/*
+Remove the inner padding in Chrome and Safari on macOS.
+*/
+
+::-webkit-search-decoration {
+ -webkit-appearance: none;
+}
+
+/*
+1. Correct the inability to style clickable types in iOS and Safari.
+2. Change font properties to `inherit` in Safari.
+*/
+
+::-webkit-file-upload-button {
+ -webkit-appearance: button; /* 1 */
+ font: inherit; /* 2 */
+}
+
+/*
+Add the correct display in Chrome and Safari.
+*/
+
+summary {
+ display: list-item;
+}
+
+/*
+Removes the default spacing and border for appropriate elements.
+*/
+
+blockquote,
+dl,
+dd,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+hr,
+figure,
+p,
+pre {
+ margin: 0;
+}
+
+fieldset {
+ margin: 0;
+ padding: 0;
+}
+
+legend {
+ padding: 0;
+}
+
+ol,
+ul,
+menu {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+
+/*
+Reset default styling for dialogs.
+*/
+dialog {
+ padding: 0;
+}
+
+/*
+Prevent resizing textareas horizontally by default.
+*/
+
+textarea {
+ resize: vertical;
+}
+
+/*
+1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)
+2. Set the default placeholder color to the user's configured gray 400 color.
+*/
+
+input::placeholder,
+textarea::placeholder {
+ opacity: 1; /* 1 */
+ color: #9ca3af; /* 2 */
+}
+
+/*
+Set the default cursor for buttons.
+*/
+
+button,
+[role="button"] {
+ cursor: pointer;
+}
+
+/*
+Make sure disabled buttons don't get the pointer cursor.
+*/
+:disabled {
+ cursor: default;
+}
+
+/*
+1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)
+2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)
+ This can trigger a poorly considered lint error in some tools but is included by design.
+*/
+
+img,
+svg,
+video,
+canvas,
+audio,
+iframe,
+embed,
+object {
+ display: block; /* 1 */
+ vertical-align: middle; /* 2 */
+}
+
+/*
+Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)
+*/
+
+img,
+video {
+ max-width: 100%;
+ height: auto;
+}
+
+/* Make elements with the HTML hidden attribute stay hidden by default */
+[hidden]:where(:not([hidden="until-found"])) {
+ display: none;
+}
diff --git a/src/styles/variable.css b/src/styles/variable.css
new file mode 100644
index 00000000..5da0cdb9
--- /dev/null
+++ b/src/styles/variable.css
@@ -0,0 +1,47 @@
+:root {
+ /* Color - Primary */
+ --blue-50: #e6f2ff;
+ --blue-100: #3692ff;
+ --blue-200: #1967d6;
+ --blue-300: #1251aa;
+
+ /* Color - Secondary */
+ --gray-10: #fcfcfc;
+ --gray-50: #f9fafb;
+ --gray-100: #f3f4f6;
+ --gray-200: #e5e7eb;
+ --gray-300: #dfdfdf;
+ --gray-400: #9ca3af;
+ --gray-500: #6b7280;
+ --gray-600: #4b5563;
+ --gray-700: #374151;
+ --gray-800: #1f2937;
+ --gray-900: #111827;
+
+ /* Color - Error */
+ --red: #f74747;
+}
+
+:root {
+ /* Font - Size */
+ --size-4xl: 40px;
+ --size-3xl: 32px;
+ --size-2xl: 24px;
+ --size-xl: 20px;
+ --size-2lg: 18px;
+ --size-lg: 16px;
+ --size-md: 14px;
+ --size-sm: 13px;
+ --size-xs: 12px;
+
+ /* Font - Line Height */
+ --line-4xl: 52px;
+ --line-3xl: 42px;
+ --line-2xl: 32px;
+ --line-xl: 32px;
+ --line-2lg: 26px;
+ --line-lg: 26px;
+ --line-md: 24px;
+ --line-sm: 22px;
+ --line-xs: 18px;
+}
diff --git a/styles/Home.module.css b/styles/Home.module.css
deleted file mode 100644
index 6676d2c6..00000000
--- a/styles/Home.module.css
+++ /dev/null
@@ -1,229 +0,0 @@
-.main {
- display: flex;
- flex-direction: column;
- justify-content: space-between;
- align-items: center;
- padding: 6rem;
- min-height: 100vh;
-}
-
-.description {
- display: inherit;
- justify-content: inherit;
- align-items: inherit;
- font-size: 0.85rem;
- max-width: var(--max-width);
- width: 100%;
- z-index: 2;
- font-family: var(--font-mono);
-}
-
-.description a {
- display: flex;
- justify-content: center;
- align-items: center;
- gap: 0.5rem;
-}
-
-.description p {
- position: relative;
- margin: 0;
- padding: 1rem;
- background-color: rgba(var(--callout-rgb), 0.5);
- border: 1px solid rgba(var(--callout-border-rgb), 0.3);
- border-radius: var(--border-radius);
-}
-
-.code {
- font-weight: 700;
- font-family: var(--font-mono);
-}
-
-.grid {
- display: grid;
- grid-template-columns: repeat(4, minmax(25%, auto));
- max-width: 100%;
- width: var(--max-width);
-}
-
-.card {
- padding: 1rem 1.2rem;
- border-radius: var(--border-radius);
- background: rgba(var(--card-rgb), 0);
- border: 1px solid rgba(var(--card-border-rgb), 0);
- transition: background 200ms, border 200ms;
-}
-
-.card span {
- display: inline-block;
- transition: transform 200ms;
-}
-
-.card h2 {
- font-weight: 600;
- margin-bottom: 0.7rem;
-}
-
-.card p {
- margin: 0;
- opacity: 0.6;
- font-size: 0.9rem;
- line-height: 1.5;
- max-width: 30ch;
-}
-
-.center {
- display: flex;
- justify-content: center;
- align-items: center;
- position: relative;
- padding: 4rem 0;
-}
-
-.center::before {
- background: var(--secondary-glow);
- border-radius: 50%;
- width: 480px;
- height: 360px;
- margin-left: -400px;
-}
-
-.center::after {
- background: var(--primary-glow);
- width: 240px;
- height: 180px;
- z-index: -1;
-}
-
-.center::before,
-.center::after {
- content: '';
- left: 50%;
- position: absolute;
- filter: blur(45px);
- transform: translateZ(0);
-}
-
-.logo {
- position: relative;
-}
-/* Enable hover only on non-touch devices */
-@media (hover: hover) and (pointer: fine) {
- .card:hover {
- background: rgba(var(--card-rgb), 0.1);
- border: 1px solid rgba(var(--card-border-rgb), 0.15);
- }
-
- .card:hover span {
- transform: translateX(4px);
- }
-}
-
-@media (prefers-reduced-motion) {
- .card:hover span {
- transform: none;
- }
-}
-
-/* Mobile */
-@media (max-width: 700px) {
- .content {
- padding: 4rem;
- }
-
- .grid {
- grid-template-columns: 1fr;
- margin-bottom: 120px;
- max-width: 320px;
- text-align: center;
- }
-
- .card {
- padding: 1rem 2.5rem;
- }
-
- .card h2 {
- margin-bottom: 0.5rem;
- }
-
- .center {
- padding: 8rem 0 6rem;
- }
-
- .center::before {
- transform: none;
- height: 300px;
- }
-
- .description {
- font-size: 0.8rem;
- }
-
- .description a {
- padding: 1rem;
- }
-
- .description p,
- .description div {
- display: flex;
- justify-content: center;
- position: fixed;
- width: 100%;
- }
-
- .description p {
- align-items: center;
- inset: 0 0 auto;
- padding: 2rem 1rem 1.4rem;
- border-radius: 0;
- border: none;
- border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25);
- background: linear-gradient(
- to bottom,
- rgba(var(--background-start-rgb), 1),
- rgba(var(--callout-rgb), 0.5)
- );
- background-clip: padding-box;
- backdrop-filter: blur(24px);
- }
-
- .description div {
- align-items: flex-end;
- pointer-events: none;
- inset: auto 0 0;
- padding: 2rem;
- height: 200px;
- background: linear-gradient(
- to bottom,
- transparent 0%,
- rgb(var(--background-end-rgb)) 40%
- );
- z-index: 1;
- }
-}
-
-/* Tablet and Smaller Desktop */
-@media (min-width: 701px) and (max-width: 1120px) {
- .grid {
- grid-template-columns: repeat(2, 50%);
- }
-}
-
-@media (prefers-color-scheme: dark) {
- .vercelLogo {
- filter: invert(1);
- }
-
- .logo {
- filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70);
- }
-}
-
-@keyframes rotate {
- from {
- transform: rotate(360deg);
- }
- to {
- transform: rotate(0deg);
- }
-}
diff --git a/styles/globals.css b/styles/globals.css
deleted file mode 100644
index d4f491e1..00000000
--- a/styles/globals.css
+++ /dev/null
@@ -1,107 +0,0 @@
-:root {
- --max-width: 1100px;
- --border-radius: 12px;
- --font-mono: ui-monospace, Menlo, Monaco, 'Cascadia Mono', 'Segoe UI Mono',
- 'Roboto Mono', 'Oxygen Mono', 'Ubuntu Monospace', 'Source Code Pro',
- 'Fira Mono', 'Droid Sans Mono', 'Courier New', monospace;
-
- --foreground-rgb: 0, 0, 0;
- --background-start-rgb: 214, 219, 220;
- --background-end-rgb: 255, 255, 255;
-
- --primary-glow: conic-gradient(
- from 180deg at 50% 50%,
- #16abff33 0deg,
- #0885ff33 55deg,
- #54d6ff33 120deg,
- #0071ff33 160deg,
- transparent 360deg
- );
- --secondary-glow: radial-gradient(
- rgba(255, 255, 255, 1),
- rgba(255, 255, 255, 0)
- );
-
- --tile-start-rgb: 239, 245, 249;
- --tile-end-rgb: 228, 232, 233;
- --tile-border: conic-gradient(
- #00000080,
- #00000040,
- #00000030,
- #00000020,
- #00000010,
- #00000010,
- #00000080
- );
-
- --callout-rgb: 238, 240, 241;
- --callout-border-rgb: 172, 175, 176;
- --card-rgb: 180, 185, 188;
- --card-border-rgb: 131, 134, 135;
-}
-
-@media (prefers-color-scheme: dark) {
- :root {
- --foreground-rgb: 255, 255, 255;
- --background-start-rgb: 0, 0, 0;
- --background-end-rgb: 0, 0, 0;
-
- --primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0));
- --secondary-glow: linear-gradient(
- to bottom right,
- rgba(1, 65, 255, 0),
- rgba(1, 65, 255, 0),
- rgba(1, 65, 255, 0.3)
- );
-
- --tile-start-rgb: 2, 13, 46;
- --tile-end-rgb: 2, 5, 19;
- --tile-border: conic-gradient(
- #ffffff80,
- #ffffff40,
- #ffffff30,
- #ffffff20,
- #ffffff10,
- #ffffff10,
- #ffffff80
- );
-
- --callout-rgb: 20, 20, 20;
- --callout-border-rgb: 108, 108, 108;
- --card-rgb: 100, 100, 100;
- --card-border-rgb: 200, 200, 200;
- }
-}
-
-* {
- box-sizing: border-box;
- padding: 0;
- margin: 0;
-}
-
-html,
-body {
- max-width: 100vw;
- overflow-x: hidden;
-}
-
-body {
- color: rgb(var(--foreground-rgb));
- background: linear-gradient(
- to bottom,
- transparent,
- rgb(var(--background-end-rgb))
- )
- rgb(var(--background-start-rgb));
-}
-
-a {
- color: inherit;
- text-decoration: none;
-}
-
-@media (prefers-color-scheme: dark) {
- html {
- color-scheme: dark;
- }
-}
diff --git a/tsconfig.json b/tsconfig.json
index 670224f3..5bf94c12 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -14,7 +14,8 @@
"jsx": "preserve",
"incremental": true,
"paths": {
- "@/*": ["./*"]
+ "#/*": ["./public/*"],
+ "@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],