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 -

- -
- -
- Next.js Logo -
- - -
- - ) -} 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"],