diff --git a/public/font/ROKAF Slab Serif Bold.ttf b/public/font/ROKAF.Bold.ttf similarity index 100% rename from public/font/ROKAF Slab Serif Bold.ttf rename to public/font/ROKAF.Bold.ttf diff --git a/src/api/comment.js b/src/api/comment.js new file mode 100644 index 00000000..609d6110 --- /dev/null +++ b/src/api/comment.js @@ -0,0 +1,11 @@ +import axios from "axios"; + +const BASE_URL = process.env.REACT_APP_BASE_URL; + +export async function getComments(productId) { + const response = await axios.get( + `${BASE_URL}/products/${productId}/comments?limit=3` + ); + + return response.data.list; +} diff --git a/src/api/products.js b/src/api/products.js index 9700cd4e..4f9bb5c2 100644 --- a/src/api/products.js +++ b/src/api/products.js @@ -10,3 +10,9 @@ export async function getProducts(params) { return response.data; } + +export async function getProductInfo(productId) { + const response = await axios.get(`${BASE_URL}/products/${productId}`); + + return response.data; +} diff --git a/src/assets/icons/calling.svg b/src/assets/icons/calling.svg new file mode 100644 index 00000000..5444cbbb --- /dev/null +++ b/src/assets/icons/calling.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/src/assets/icons/dots.svg b/src/assets/icons/dots.svg new file mode 100644 index 00000000..dd7ed7f5 --- /dev/null +++ b/src/assets/icons/dots.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/icons/emptyHeart.svg b/src/assets/icons/emptyHeart.svg new file mode 100644 index 00000000..172801d4 --- /dev/null +++ b/src/assets/icons/emptyHeart.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/fullHeart.svg b/src/assets/icons/fullHeart.svg new file mode 100644 index 00000000..ca563627 --- /dev/null +++ b/src/assets/icons/fullHeart.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/goBack.svg b/src/assets/icons/goBack.svg new file mode 100644 index 00000000..a8265375 --- /dev/null +++ b/src/assets/icons/goBack.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/components/Comment/Comment.jsx b/src/components/Comment/Comment.jsx new file mode 100644 index 00000000..8d8b98ee --- /dev/null +++ b/src/components/Comment/Comment.jsx @@ -0,0 +1,12 @@ +import * as S from "./Comment.styles"; +import CommentInput from "./CommentInput"; +import CommentList from "./CommentList"; + +export default function Comment() { + return ( + + + + + ); +} diff --git a/src/components/Comment/Comment.styles.jsx b/src/components/Comment/Comment.styles.jsx new file mode 100644 index 00000000..fb28f665 --- /dev/null +++ b/src/components/Comment/Comment.styles.jsx @@ -0,0 +1,9 @@ +import styled from "styled-components"; + +export const Container = styled.div` + max-width: 1200px; + width: 100%; + display: flex; + flex-direction: column; + gap: 24px; +`; diff --git a/src/components/Comment/CommentInput.jsx b/src/components/Comment/CommentInput.jsx new file mode 100644 index 00000000..1b6129ee --- /dev/null +++ b/src/components/Comment/CommentInput.jsx @@ -0,0 +1,26 @@ +import Input from "../common/Input/Input"; +import * as S from "./CommentInput.styles"; +import { useState } from "react"; + +export default function CommentInput() { + const [comment, setComment] = useState(""); + + return ( + + setComment(e.target.value)} + /> + + setComment("")}> + 등록 + + + + ); +} diff --git a/src/components/Comment/CommentInput.styles.jsx b/src/components/Comment/CommentInput.styles.jsx new file mode 100644 index 00000000..85ab6594 --- /dev/null +++ b/src/components/Comment/CommentInput.styles.jsx @@ -0,0 +1,35 @@ +import styled from "styled-components"; +import theme from "../../styles/theme"; + +export const CommentContainer = styled.div` + width: 100%; +`; + +export const RegisterBtn = styled.div` + width: 100%; + display: flex; + justify-content: flex-end; + + button { + width: 74px; + height: 42px; + border-radius: 8px; + font: ${theme.font.H5Bold}; + color: ${theme.color.gray100}; + background-color: ${theme.color.blue}; + margin-top: 24px; + cursor: pointer; + + &:disabled { + background-color: ${theme.color.gray400}; + + &:hover { + background-color: ${theme.color.gray400}; + } + } + + &:hover { + background-color: ${theme.color.blueHover}; + } + } +`; diff --git a/src/components/Comment/CommentList.jsx b/src/components/Comment/CommentList.jsx new file mode 100644 index 00000000..a4852c49 --- /dev/null +++ b/src/components/Comment/CommentList.jsx @@ -0,0 +1,101 @@ +import * as S from "./CommentList.styles"; +import dots from "../../assets/icons/dots.svg"; +import { useState, useEffect } from "react"; +import { getComments } from "../../api/comment"; +import { useParams } from "react-router-dom"; +import User from "../User/User"; +import Input from "../common/Input/Input"; +import NoneComment from "../NoneComment/NoneComment"; + +export default function CommentList() { + const [comments, setComments] = useState([]); + const [commentId, setCommentId] = useState(null); + const [editCommentId, setEditCommentId] = useState(null); + const { productId } = useParams(); + + useEffect(() => { + getComments(productId) + .then((result) => setComments(result)) + .catch((error) => console.error(error)); + }, []); + + function SelectBox({ comment }) { + const options = [ + { + id: 1, + option: "수정하기", + onClick: () => { + setEditCommentId(comment.id); + setCommentId(null); + }, + }, + { + id: 2, + option: "삭제하기", + onClick: () => { + setCommentId(null); + }, + }, + ]; + return ( + + {options.map((s) => ( + + {s.option} + + ))} + + ); + } + + const handleOpenClick = (commentId) => { + setCommentId((prevId) => (prevId === commentId ? null : commentId)); + }; + + if (comments.length === 0) return ; + + return ( + + {comments.map((comment) => ( + + {editCommentId === comment.id ? ( + + ) : ( + + {comment.content} + + handleOpenClick(comment.id)} + /> + {commentId === comment.id && } + + + )} + + + {editCommentId === comment.id && ( + + setEditCommentId(null)}>취소 + setEditCommentId(null)}> + 수정 완료 + + + )} + + + ))} + + ); +} diff --git a/src/components/Comment/CommentList.styles.jsx b/src/components/Comment/CommentList.styles.jsx new file mode 100644 index 00000000..299e9c6d --- /dev/null +++ b/src/components/Comment/CommentList.styles.jsx @@ -0,0 +1,105 @@ +import styled from "styled-components"; +import theme from "../../styles/theme"; + +export const ListContainer = styled.div` + display: flex; + flex-direction: column; + gap: 40px; + + @media (max-width: 375px) { + gap: 16px; + } +`; + +export const Comment = styled.div` + width: 100%; + display: flex; + flex-direction: column; + gap: 24px; + padding-bottom: 12px; + border-bottom: 1px solid ${theme.color.gray200}; +`; + +export const Content = styled.div` + display: flex; + justify-content: space-between; +`; + +export const Text = styled.div` + font: ${theme.font.H7Regular}; + color: ${theme.color.gray800}; +`; + +export const Select = styled.div` + position: relative; +`; + +export const Dots = styled.img` + width: 24px; + height: 24px; + cursor: pointer; +`; + +export const SelectList = styled.ul` + width: 139px; + display: flex; + flex-direction: column; + background-color: ${theme.color.white}; + border: 1px solid ${theme.color.gray300}; + border-radius: 8px; + position: absolute; + right: 0; +`; + +export const SelectItem = styled.li` + width: 100%; + display: flex; + justify-content: center; + align-items: center; + padding: 16px 0; + font: ${theme.font.H5Regular}; + color: ${theme.color.gray500}; + cursor: pointer; + + &:hover { + font: ${theme.font.H5Bold}; + color: ${theme.color.gray800}; + } +`; + +export const UserWrapper = styled.div` + display: flex; + justify-content: space-between; + align-items: center; +`; + +export const EditBtn = styled.div` + display: flex; + justify-content: center; + align-items: center; + gap: 4px; + + span { + padding: 0 20px; + font: ${theme.font.H5Bold}; + color: ${theme.color.gray500}; + cursor: pointer; + } + + button { + width: 106px; + height: 42px; + display: flex; + justify-content: center; + align-items: center; + background-color: ${theme.color.blue}; + border-radius: 8px; + font: ${theme.font.H5Bold}; + color: ${theme.color.gray100}; + cursor: pointer; + + &:hover { + background-color: ${theme.color.blueHover}; + } + } +`; diff --git a/src/components/Detail/Detail.jsx b/src/components/Detail/Detail.jsx new file mode 100644 index 00000000..35efb572 --- /dev/null +++ b/src/components/Detail/Detail.jsx @@ -0,0 +1,92 @@ +import * as S from "./Detail.styles"; +import { useState, useEffect } from "react"; +import { useParams } from "react-router-dom"; +import { getProductInfo } from "../../api/products"; +import Tag from "../Tag/Tag"; +import User from "../User/User"; +import emptyHeart from "../../assets/icons/emptyHeart.svg"; +import fullHeart from "../../assets/icons/fullHeart.svg"; +import dots from "../../assets/icons/dots.svg"; +import noneImg from "../../assets/icons/image.svg"; + +export default function Detail() { + const [product, setProduct] = useState({ + name: "", + description: "", + price: 0, + tags: [], + images: null, + favoriteCount: 0, + createdAt: "", + updatedAt: "", + ownerNickname: "", + }); + const [isFull, setIsFull] = useState(false); + const [isImgError, setIsImgError] = useState(false); + const { productId } = useParams(); + + useEffect(() => { + getProductInfo(productId) + .then((result) => setProduct(result)) + .catch((error) => console.error(error)); + }, []); + + const handleHeartChange = () => { + setIsFull((prev) => !prev); + }; + + return ( + + {product.images && !isImgError ? ( + setIsImgError(false)} + onError={() => setIsImgError(true)} + /> + ) : ( + + + + )} + + + + + + {product.name} + + + {product.price.toLocaleString()}원 + + + + 상품 소개 + {product.description} + + + 상품 태그 + + {product.tags.map((tag) => ( + + ))} + + + + + + + + + {product.favoriteCount} + + + + + ); +} diff --git a/src/components/Detail/Detail.styles.jsx b/src/components/Detail/Detail.styles.jsx new file mode 100644 index 00000000..6137f2e5 --- /dev/null +++ b/src/components/Detail/Detail.styles.jsx @@ -0,0 +1,186 @@ +import styled from "styled-components"; +import theme from "../../styles/theme"; + +export const DetailContainer = styled.div` + max-width: 1200px; + width: 100%; + display: flex; + justify-content: flex-start; + align-items: center; + gap: 24px; + padding-bottom: 40px; + border-bottom: 1px solid ${theme.color.gray200}; + + @media (max-width: 375px) { + flex-direction: column; + padding-bottom: 16px; + } + + @media (min-width: 376px) and (max-width: 768px) { + flex-direction: column; + padding-bottom: 32px; + } + + @media (min-width: 769px) and (max-width: 1280px) { + justify-content: center; + align-items: flex-start; + } +`; + +export const Image = styled.img` + max-width: 486px; + min-width: 343px; + width: 100%; + height: auto; + aspect-ratio: 1/1; + border-radius: 16px; + background-color: ${theme.color.gray200}; +`; + +export const NoneImageContainer = styled.div` + max-width: 486px; + min-width: 343px; + width: 100%; + flex-shrink: 0; + aspect-ratio: 1/1; + border-radius: 16px; + background-color: ${theme.color.gray200}; + display: flex; + justify-content: center; + align-items: center; + + /* @media (max-width: 767px) { + min-width: 343px; + } */ +`; + +export const NoneImage = styled.img` + width: 60px; + height: 60px; +`; + +export const Detail = styled.div` + width: 100%; + display: flex; + flex-direction: column; + gap: 62px; + + @media (max-width: 375px) { + gap: 24px; + } + + @media (min-width: 376px) and (max-width: 768px) { + gap: 40px; + } +`; + +export const Header = styled.div` + display: flex; + flex-direction: column; + gap: 16px; + padding-bottom: 16px; + margin-bottom: 24px; + border-bottom: 1px solid ${theme.color.gray200}; + + @media (max-width: 768px) { + gap: 8px; + padding-bottom: 8px; + } +`; + +export const TitleWrapper = styled.div` + display: flex; + justify-content: space-between; + align-items: center; +`; + +export const Title = styled.div` + font: ${theme.font.H2Regular}; + color: ${theme.color.gray800}; + + @media (max-width: 375px) { + font: ${theme.font.H5Regular}; + } + + @media (min-width: 376px) and (max-width: 768px) { + font: ${theme.font.H3Regular}; + } +`; + +export const Dots = styled.img` + width: 24px; + height: 24px; + cursor: pointer; +`; + +export const Price = styled.div` + font: ${theme.font.H0}; + color: ${theme.color.gray800}; + + @media (max-width: 375px) { + font: ${theme.font.H2Bold}; + } + + @media (min-width: 376px) and (max-width: 768px) { + font: ${theme.font.H1}; + font-size: 32px; + } +`; + +export const ProductInfo = styled.div` + display: flex; + flex-direction: column; + gap: 24px; + + @media (max-width: 768px) { + gap: 16px; + } +`; + +export const Label = styled.div` + font: ${theme.font.H5Bold}; + color: ${theme.color.gray600}; + padding-bottom: 16px; + + @media (max-width: 768px) { + padding-bottom: 8px; + font: ${theme.font.H7Bold}; + } +`; + +export const Content = styled.div` + font: ${theme.font.H5Regular}; + color: ${theme.color.gray600}; +`; + +export const TagWrapper = styled.div` + display: flex; + gap: 8px; + flex-wrap: wrap; +`; + +export const UserWrapper = styled.div` + display: flex; + justify-content: space-between; +`; + +export const HeartWrapper = styled.div` + display: flex; + justify-content: center; + align-items: center; + gap: 10px; + padding: 4px 12px; + border-radius: 35px; + border: 1px solid ${theme.color.gray200}; + cursor: pointer; +`; + +export const Heart = styled.img` + width: 32px; + height: 32px; +`; + +export const HeartCount = styled.div` + font: ${theme.font.H5Regular}; + color: ${theme.color.gray500}; +`; diff --git a/src/components/FileInput/FileInput.jsx b/src/components/FileInput/FileInput.jsx index 1d16453a..5d0f74ad 100644 --- a/src/components/FileInput/FileInput.jsx +++ b/src/components/FileInput/FileInput.jsx @@ -3,7 +3,7 @@ import x from "../../assets/icons/delete.svg"; import plus from "../../assets/icons/plus.svg"; import { useState, useRef } from "react"; -export default function FileInput({ lable, images, setValues }) { +export default function FileInput({ label, images, setValues }) { const [preview, setPreview] = useState(null); const inputRef = useRef(); @@ -34,7 +34,7 @@ export default function FileInput({ lable, images, setValues }) { return ( <> - {lable} + {label} diff --git a/src/components/FileInput/FileInput.styles.jsx b/src/components/FileInput/FileInput.styles.jsx index 938a0a11..5a3253f8 100644 --- a/src/components/FileInput/FileInput.styles.jsx +++ b/src/components/FileInput/FileInput.styles.jsx @@ -1,4 +1,5 @@ import { styled } from "styled-components"; +import theme from "../../styles/theme"; export const FileContainer = styled.div` display: flex; @@ -10,7 +11,7 @@ export const Label = styled.div` font-size: 18px; font-weight: 700; line-height: 26px; - color: var(--gray800); + color: ${theme.color.gray800}; `; export const File = styled.label` @@ -19,7 +20,7 @@ export const File = styled.label` display: flex; justify-content: center; align-items: center; - background-color: var(--gray100); + background-color: ${theme.color.gray100}; border-radius: 12px; cursor: pointer; @@ -44,10 +45,8 @@ export const PlusIcon = styled.img` `; export const AddImg = styled.div` - font-size: 16px; - font-weight: 400; - line-height: 26px; - color: var(--gray400); + font:${theme.font.H5Regular} + color: ${theme.color.gray400}; `; export const Preview = styled.div` diff --git a/src/components/Items/AllItems/AllItems.jsx b/src/components/Items/AllItems/AllItems.jsx index 32e3043a..8d684a21 100644 --- a/src/components/Items/AllItems/AllItems.jsx +++ b/src/components/Items/AllItems/AllItems.jsx @@ -7,33 +7,24 @@ import Search from "../../Search/Search"; import NoneItem from "../../NoneItem/NoneItem"; import { getProducts } from "../../../api/products"; import Paging from "../../Paging/Paging"; +import useResize from "../../../hooks/useResize"; const LIST = ["최신순", "좋아요순"]; export default function AllItems() { const [items, setItems] = useState([]); const [currentPage, setCurrentPage] = useState(1); - const [pageSize, setPageSize] = useState(10); const [sortOption, setSortOption] = useState("최신순"); const [keyword, setKeyword] = useState(""); const [totalItems, setTotalItems] = useState(0); + const { showItems } = useResize(4, 6, 10); const orderByValue = sortOption === "최신순" ? "recent" : "favorite"; - const updateItems = () => { - if (window.innerWidth <= 767) { - setPageSize(4); - } else if (window.innerWidth >= 768 && window.innerWidth <= 1199) { - setPageSize(6); - } else { - setPageSize(10); - } - }; - useEffect(() => { getProducts({ page: currentPage, - pageSize: pageSize, + pageSize: showItems, orderBy: orderByValue, keyword: keyword, }).then((result) => { @@ -41,19 +32,11 @@ export default function AllItems() { setItems(result.list); setTotalItems(result.totalCount); }); - }, [currentPage, pageSize, orderByValue, keyword]); - - useEffect(() => { - updateItems(); - window.addEventListener("resize", updateItems); - - return () => { - window.removeEventListener("resize", updateItems); - }; - }, []); + }, [currentPage, showItems, orderByValue, keyword]); const handleChangeClick = (sortOption) => { setSortOption(sortOption); + setCurrentPage(1); }; return ( @@ -71,13 +54,17 @@ export default function AllItems() { 상품 등록하기 - + {items.length !== 0 ? ( {items.map((items, idx) => ( - + ))} ) : ( @@ -85,7 +72,12 @@ export default function AllItems() { )} {items.length !== 0 && ( - + )} ); diff --git a/src/components/Items/AllItems/AllItems.styles.jsx b/src/components/Items/AllItems/AllItems.styles.jsx index 8ef5ff9d..dbbf137d 100644 --- a/src/components/Items/AllItems/AllItems.styles.jsx +++ b/src/components/Items/AllItems/AllItems.styles.jsx @@ -1,4 +1,5 @@ import styled from "styled-components"; +import theme from "../../../styles/theme"; export const AllItems = styled.div` display: flex; @@ -41,10 +42,8 @@ export const Div = styled.div` `; export const Title = styled.h2` - font-size: 20px; - font-weight: 700; - line-height: 32px; - color: var(--gray900); + font: ${theme.font.H3Bold}; + color: ${theme.color.gray900}; margin: 0; @media (max-width: 767px) { @@ -68,14 +67,12 @@ export const Filter = styled.div` export const AddBtn = styled.button` width: 133px; height: 42px; - background-color: var(--primary); - color: var(--gray100); + background-color: ${theme.color.blue}; + color: ${theme.color.gray100}; border-radius: 8px; border: none; text-align: center; - font-size: 16px; - font-weight: 600; - line-height: 26px; + font: ${theme.font.H5Bold}; cursor: pointer; @media (max-width: 767px) { @@ -96,11 +93,11 @@ export const ItemCardContainer = styled.div` grid-template-columns: repeat(5, 1fr); grid-template-rows: 1fr 1fr; gap: 24px; - margin: auto; @media (max-width: 767px) { grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; + gap: 8px; } @media (min-width: 768px) and (max-width: 1199px) { diff --git a/src/components/Items/BestItems/BestItems.jsx b/src/components/Items/BestItems/BestItems.jsx index fd179934..07977174 100644 --- a/src/components/Items/BestItems/BestItems.jsx +++ b/src/components/Items/BestItems/BestItems.jsx @@ -1,16 +1,17 @@ import * as S from "./BestItems.styles"; -import { useState, useEffect, useCallback } from "react"; +import { useState, useEffect } from "react"; import ItemCard from "../ItemCard/ItemCard"; import { getProducts } from "../../../api/products"; +import useResize from "../../../hooks/useResize"; export default function BestItems() { const [bestItems, setBestItems] = useState([]); - const [showItems, setShowItems] = useState(4); + const { showItems } = useResize(1, 2, 4); useEffect(() => { getProducts({ page: 1, - pageSize: showItems, + pageSize: 4, orderBy: "favorite", keyword: "", }).then((result) => { @@ -18,35 +19,16 @@ export default function BestItems() { const sortedBestItems = [...result.list].slice(0, 4); setBestItems(sortedBestItems); }); - }, [showItems]); - - const updateBestItems = useCallback(() => { - if (window.innerWidth <= 767) { - setShowItems(1); - } else if (window.innerWidth >= 768 && window.innerWidth <= 1199) { - setShowItems(2); - } else { - setShowItems(4); - } }, []); const responsiveItems = bestItems.slice(0, showItems); - useEffect(() => { - updateBestItems(); - window.addEventListener("resize", updateBestItems); - - return () => { - window.removeEventListener("resize", updateBestItems); - }; - }, [updateBestItems]); - return ( 베스트 상품 - {responsiveItems.map((items, idx) => ( - + {responsiveItems.map((items) => ( + ))} diff --git a/src/components/Items/BestItems/BestItems.styles.jsx b/src/components/Items/BestItems/BestItems.styles.jsx index dbd8b45f..8b812e29 100644 --- a/src/components/Items/BestItems/BestItems.styles.jsx +++ b/src/components/Items/BestItems/BestItems.styles.jsx @@ -1,4 +1,5 @@ import styled from "styled-components"; +import theme from "../../../styles/theme"; export const BestContainer = styled.div` display: flex; @@ -7,11 +8,9 @@ export const BestContainer = styled.div` `; export const Title = styled.h2` - font-size: 20px; - font-weight: 700; - line-height: 32px; + font: ${theme.font.H3Bold}; text-align: left; - color: var(--gray900); + color: ${theme.color.gray900}; margin: 0; margin-bottom: 16px; `; diff --git a/src/components/Items/ItemCard/ItemCard.jsx b/src/components/Items/ItemCard/ItemCard.jsx index eaab547a..33b01380 100644 --- a/src/components/Items/ItemCard/ItemCard.jsx +++ b/src/components/Items/ItemCard/ItemCard.jsx @@ -2,14 +2,29 @@ import * as S from "./ItemCard.styles"; import heart from "../../../assets/icons/heart.svg"; import NoneImage from "../../NoneImage/NoneImage"; import { useState } from "react"; +import { useNavigate } from "react-router-dom"; -export default function ItemCard({ list = "best", images, name, price, favoriteCount }) { +export default function ItemCard({ + list = "best", + id, + images, + name, + price, + favoriteCount, +}) { const [isImgError, setIsImgError] = useState(false); + const navigate = useNavigate(); return ( - + navigate(`/items/${id}`)}> {images[0] && !isImgError ? ( - setIsImgError(true)} /> + setIsImgError(false)} + onError={() => setIsImgError(true)} + /> ) : ( )} diff --git a/src/components/Items/ItemCard/ItemCard.styles.jsx b/src/components/Items/ItemCard/ItemCard.styles.jsx index 75f3e5f8..9727553c 100644 --- a/src/components/Items/ItemCard/ItemCard.styles.jsx +++ b/src/components/Items/ItemCard/ItemCard.styles.jsx @@ -1,4 +1,5 @@ import styled from "styled-components"; +import theme from "../../../styles/theme"; export const BEST_IMG = { PC: "282px", @@ -65,19 +66,13 @@ export const ContentContainer = styled.div` `; export const Title = styled.div` - font-size: 14px; - font-weight: 500; - line-height: 24px; - text-align: left; - color: var(--gray800); + font: ${theme.font.H7Regular}; + color: ${theme.color.gray800}; `; export const Price = styled.div` - font-size: 16px; - font-weight: 700; - line-height: 26px; - text-align: left; - color: var(--gray800); + font: ${theme.font.H5Bold}; + color: ${theme.color.gray800}; `; export const HeartContainer = styled.div` @@ -91,9 +86,6 @@ export const Heart = styled.img` `; export const HeartCount = styled.div` - font-size: 12px; - font-weight: 500; - line-height: 18px; - text-align: left; - color: var(--gray600); + font: ${theme.font.H8}; + color: ${theme.color.gray600}; `; diff --git a/src/components/NoneComment/NoneComment.jsx b/src/components/NoneComment/NoneComment.jsx new file mode 100644 index 00000000..64cbac6a --- /dev/null +++ b/src/components/NoneComment/NoneComment.jsx @@ -0,0 +1,11 @@ +import * as S from "./NoneComment.styles"; +import calling from "../../assets/icons/calling.svg"; + +export default function NoneComment() { + return ( + + + 아직 문의가 없어요 + + ); +} diff --git a/src/components/NoneComment/NoneComment.styles.jsx b/src/components/NoneComment/NoneComment.styles.jsx new file mode 100644 index 00000000..ecde14a7 --- /dev/null +++ b/src/components/NoneComment/NoneComment.styles.jsx @@ -0,0 +1,20 @@ +import styled from "styled-components"; +import theme from "../../styles/theme"; + +export const NoneCommentContainer = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 8px; + margin-bottom: 8px; + span { + font: ${theme.font.H5Regular}; + color: ${theme.color.gray400}; + } +`; + +export const None = styled.img` + width: 196px; + height: 196px; +`; diff --git a/src/components/NoneImage/NoneImage.jsx b/src/components/NoneImage/NoneImage.jsx index 1234238a..031e7130 100644 --- a/src/components/NoneImage/NoneImage.jsx +++ b/src/components/NoneImage/NoneImage.jsx @@ -1,9 +1,9 @@ import * as S from "./NoneImage.styles"; import noneImg from "../../assets/icons/image.svg"; -export default function NoneImage({ list }) { +export default function NoneImage({ list, detail }) { return ( - + ); diff --git a/src/components/NoneImage/NoneImage.styles.jsx b/src/components/NoneImage/NoneImage.styles.jsx index 6c4ee14b..1ade409d 100644 --- a/src/components/NoneImage/NoneImage.styles.jsx +++ b/src/components/NoneImage/NoneImage.styles.jsx @@ -1,14 +1,16 @@ import styled from "styled-components"; import { getImgSize } from "../Items/ItemCard/ItemCard.styles"; +import theme from "../../styles/theme"; export const NoneImgContainer = styled.div` width: ${({ list }) => getImgSize(list, "PC")}; height: ${({ list }) => getImgSize(list, "PC")}; border-radius: 16px; - background-color: var(--gray200); + background-color: ${theme.color.gray200}; display: flex; justify-content: center; align-items: center; + flex-shrink: 0; @media (max-width: 767px) { width: ${({ list }) => getImgSize(list, "Moblie")}; @@ -19,11 +21,6 @@ export const NoneImgContainer = styled.div` width: ${({ list }) => getImgSize(list, "Tablet")}; height: ${({ list }) => getImgSize(list, "Tablet")}; } - - @media (min-width: 1200px) { - width: ${({ list }) => getImgSize(list, "PC")}; - height: ${({ list }) => getImgSize(list, "PC")}; - } `; export const NoneImg = styled.img` diff --git a/src/components/NoneItem/NoneItem.styles.jsx b/src/components/NoneItem/NoneItem.styles.jsx index 720878a4..e81a8b69 100644 --- a/src/components/NoneItem/NoneItem.styles.jsx +++ b/src/components/NoneItem/NoneItem.styles.jsx @@ -1,4 +1,5 @@ import { styled } from "styled-components"; +import theme from "../../styles/theme"; export const NoneItemContainer = styled.div` width: 100%; @@ -13,5 +14,5 @@ export const Text = styled.div` font-size: 20px; font-weight: 700; line-height: 32px; - color: var(--gray900); + color: ${theme.color.gray900}; `; diff --git a/src/components/Paging/Paging.styles.jsx b/src/components/Paging/Paging.styles.jsx index 79b05ea8..a9c9e93d 100644 --- a/src/components/Paging/Paging.styles.jsx +++ b/src/components/Paging/Paging.styles.jsx @@ -1,4 +1,5 @@ import { styled } from "styled-components"; +import theme from "../../styles/theme"; export const PagingContainer = styled.div` width: 100%; @@ -15,11 +16,9 @@ export const PagingContainer = styled.div` li { width: 40px; height: 40px; - border: 1px solid var(--gray200); + border: 1px solid ${theme.color.gray200}; border-radius: 40px; - font-size: 16px; - font-weight: 600; - line-height: 26px; + font: ${theme.font.H5Regular}; display: flex; justify-content: center; align-items: center; @@ -36,14 +35,14 @@ export const PagingContainer = styled.div` } li.active a { - color: var(--gray50); + color: ${theme.color.gray50}; } a { display: flex; justify-content: center; align-items: center; - color: var(--gray500); + color: ${theme.color.gray500}; } } `; diff --git a/src/components/Router.jsx b/src/components/Router.jsx index e526ca45..aa3e9c25 100644 --- a/src/components/Router.jsx +++ b/src/components/Router.jsx @@ -3,6 +3,7 @@ import Layout from "../styles/Layout"; import ItemPage from "./pages/ItemPage/ItemPage"; import FreeBoardPage from "./pages/FreeBoardPage/FreeBoardPage"; import AddItemPage from "./pages/AddItemPage/AddItemPage"; +import ProductPage from "./pages/ProductPage/ProductPage"; export default function Router() { return ( @@ -10,6 +11,7 @@ export default function Router() { }> } /> + } /> } /> } /> diff --git a/src/components/Search/Search.styles.jsx b/src/components/Search/Search.styles.jsx index 90c44393..f109f374 100644 --- a/src/components/Search/Search.styles.jsx +++ b/src/components/Search/Search.styles.jsx @@ -1,10 +1,11 @@ import { styled } from "styled-components"; +import theme from "../../styles/theme"; export const SearchContainer = styled.div` width: 325px; height: 42px; padding: 9px 18px; - background-color: var(--gray100); + background-color: ${theme.color.gray100}; border-radius: 12px; display: flex; gap: 4px; @@ -36,6 +37,6 @@ export const Input = styled.input` } &::placeholder { - color: var(--gray400); + color: ${theme.color.gray400}; } `; diff --git a/src/components/Tag/Tag.jsx b/src/components/Tag/Tag.jsx index 322bc5fc..13902048 100644 --- a/src/components/Tag/Tag.jsx +++ b/src/components/Tag/Tag.jsx @@ -1,12 +1,12 @@ import * as S from "./Tag.styles"; import x from "../../assets/icons/delete.svg"; -export default function Tag({ tag, onClick }) { +export default function Tag({ tag, onClick, readOnly }) { return ( #{tag} - + ); diff --git a/src/components/Tag/Tag.styles.jsx b/src/components/Tag/Tag.styles.jsx index 2a227c66..4c3e15c4 100644 --- a/src/components/Tag/Tag.styles.jsx +++ b/src/components/Tag/Tag.styles.jsx @@ -1,8 +1,9 @@ import { styled } from "styled-components"; +import theme from "../../styles/theme"; export const TagContainer = styled.li` height: 36px; - background-color: var(--gray100); + background-color: ${theme.color.gray100}; border-radius: 26px; padding: 6px 12px; `; @@ -14,14 +15,14 @@ export const Tag = styled.div` `; export const TagName = styled.div` - font-size: 16px; - font-weight: 400; - line-height: 26px; - color: var(--gray800); + font: ${theme.font.H5Regular}; + color: ${theme.color.gray800}; `; export const DeleteTag = styled.img` width: 22px; height: 24px; cursor: pointer; + + display: ${({ readOnly }) => (readOnly ? "none" : "block")}; `; diff --git a/src/components/User/User.jsx b/src/components/User/User.jsx new file mode 100644 index 00000000..36822b3a --- /dev/null +++ b/src/components/User/User.jsx @@ -0,0 +1,19 @@ +import * as S from "./User.styles"; +import user from "../../assets/icons/user.svg"; + +export default function User({ owner, createdAt, detail }) { + const date = new Date(createdAt); + const formattedDate = `${date.getFullYear()}.${String( + date.getMonth() + 1 + ).padStart(2, "0")}.${String(date.getDate()).padStart(2, "0")}`; + + return ( + + + + {owner} + {formattedDate} + + + ); +} diff --git a/src/components/User/User.styles.jsx b/src/components/User/User.styles.jsx new file mode 100644 index 00000000..6c4b2edd --- /dev/null +++ b/src/components/User/User.styles.jsx @@ -0,0 +1,33 @@ +import styled from "styled-components"; +import theme from "../../styles/theme"; + +export const User = styled.div` + display: flex; + justify-content: flex-start; + align-items: center; + gap: ${({ $detail }) => ($detail ? "16px" : "8px")}; +`; + +export const Profile = styled.img` + width: ${({ $detail }) => ($detail ? "40px" : "32px")}; + height: ${({ $detail }) => ($detail ? "40px" : "32px")}; + border-radius: 100px; +`; + +export const UserInfo = styled.div` + display: flex; + flex-direction: column; + gap: 4px; +`; + +export const Name = styled.div` + font: ${({ $detail }) => + $detail ? `${theme.font.H7Regular}` : `${theme.font.H8}`}; + color: ${theme.color.gray600}; +`; + +export const CreatedAt = styled.div` + font: ${({ $detail }) => + $detail ? `${theme.font.H7Regular}` : `${theme.font.H8}`}; + color: ${theme.color.gray400}; +`; diff --git a/src/components/common/Dropdown/Dropdown.styles.jsx b/src/components/common/Dropdown/Dropdown.styles.jsx index b0821856..4b182382 100644 --- a/src/components/common/Dropdown/Dropdown.styles.jsx +++ b/src/components/common/Dropdown/Dropdown.styles.jsx @@ -2,6 +2,7 @@ import { styled } from "styled-components"; import down from "../../../assets/icons/arrowDown.svg"; import up from "../../../assets/icons/arrowUp.svg"; import dropdown from "../../../assets/icons/dropdown.svg"; +import theme from "../../../styles/theme"; export const DropdownContainer = styled.div` display: flex; @@ -25,8 +26,8 @@ export const Present = styled.div` height: 42px; padding: 12px 20px; border-radius: 12px; - border: 1px solid #e5e7eb; - background-color: white; + border: 1px solid ${theme.color.gray200}; + background-color: ${theme.color.white}; cursor: pointer; @media screen and (max-width: 767px) { @@ -36,10 +37,8 @@ export const Present = styled.div` `; export const PresentValue = styled.div` - font-size: 16px; - font-weight: 400; - line-height: 26px; - color: var(--gray800); + font: ${theme.font.H5Regular}; + color: ${theme.color.gray800}; @media screen and (max-width: 767px) { display: none; @@ -59,12 +58,12 @@ export const Arrow = styled.img` export const List = styled.div` width: 130px; - background-color: white; - border: 1px solid #e5e7eb; + background-color: ${theme.color.white}; + border: 1px solid ${theme.color.gray200}; border-radius: 12px; position: absolute; top: 55px; - color: var(--gray800); + color: ${theme.color.gray800}; `; export const ListItem = styled.div` @@ -72,14 +71,12 @@ export const ListItem = styled.div` display: flex; justify-content: center; align-items: center; - border-bottom: 1px solid #e5e7eb; - font-size: 16px; - font-weight: 400; - line-height: 26px; + border-bottom: 1px solid ${theme.color.gray200}; + font: ${theme.font.H5Regular}; cursor: pointer; &:hover { - color: var(--primary); + color: ${theme.color.blue}; } &:last-child { diff --git a/src/components/common/Header/Header.jsx b/src/components/common/Header/Header.jsx index aff14729..55e46589 100644 --- a/src/components/common/Header/Header.jsx +++ b/src/components/common/Header/Header.jsx @@ -2,6 +2,7 @@ import * as S from "./Header.styles"; import { NavLink, useLocation } from "react-router-dom"; import logo from "../../../assets/icons/panda.svg"; import user from "../../../assets/icons/user.svg"; +import theme from "../../../styles/theme"; export default function Header() { const location = useLocation().pathname; @@ -10,14 +11,18 @@ export default function Header() { const isItemsOrAddItem = isActive || location.startsWith("/addItem"); return { - color: isItemsOrAddItem ? "var(--primary)" : "var(--gray600)", + color: isItemsOrAddItem + ? `${theme.color.blue}` + : `${theme.color.gray600}`, }; }; const navLink = [ { to: "/freeBoard", - style: ({ isActive }) => ({ color: isActive ? "var(--primary)" : "var(--gray600)" }), + style: ({ isActive }) => ({ + color: isActive ? `${theme.color.blue}` : `${theme.color.gray600}`, + }), name: "자유게시판", }, { to: "/items", style: activeLink, name: "중고마켓" }, diff --git a/src/components/common/Header/Header.styles.jsx b/src/components/common/Header/Header.styles.jsx index 429ac353..8b32092b 100644 --- a/src/components/common/Header/Header.styles.jsx +++ b/src/components/common/Header/Header.styles.jsx @@ -1,23 +1,23 @@ import styled from "styled-components"; +import theme from "../../../styles/theme"; export const HeaderContainer = styled.div` - background: var(--white); + width: 100%; + height: 70px; display: flex; justify-content: space-between; align-items: center; + padding: 9px 200px; + background: ${theme.color.white}; + border-bottom: 1px solid #dfdfdf; position: sticky; top: 0; - border-bottom: 1px solid #dfdfdf; - width: 100%; - height: 70px; z-index: 1; - @media (min-width: 1200px) { - padding: 9px 200px; - } - @media screen and (min-width: 768px) and (max-width: 1199px) { + + @media (min-width: 769px) and (max-width: 1280px) { padding: 9px 24px; } - @media screen and (max-width: 767px) { + @media (max-width: 768px) { padding: 9px 16px; } `; @@ -40,10 +40,14 @@ export const Logo = styled.img` `; export const Title = styled.div` - font-family: ROKAF Sans; + font-family: "ROKAF Sans", sans-serif; font-size: 25px; font-weight: 700; - color: var(--primary); + color: ${theme.color.blue}; + + @media (max-width: 386px) { + display: none; + } `; export const NavList = styled.div` @@ -52,13 +56,17 @@ export const NavList = styled.div` `; export const NavItems = styled.div` + font: ${theme.font.H4Bold}; font-size: 18px; font-weight: 700; line-height: 26px; text-align: center; padding: 15px 21px; - color: var(--gray600); + color: ${theme.color.gray00}; cursor: pointer; + @media (max-width: 768px) { + padding: 15px 0; + } `; export const User = styled(Logo)``; diff --git a/src/components/common/Input/Input.jsx b/src/components/common/Input/Input.jsx index cd7d98f1..a519e71e 100644 --- a/src/components/common/Input/Input.jsx +++ b/src/components/common/Input/Input.jsx @@ -1,10 +1,25 @@ import * as S from "./Input.styles"; -export default function Input({ label, style, isTextarea, onChange, ...rest }) { +export default function Input({ + label, + style, + isTextarea, + onChange, + height, + largeHeight, + ...rest +}) { return ( {label} - + ); } diff --git a/src/components/common/Input/Input.styles.jsx b/src/components/common/Input/Input.styles.jsx index bafff8d2..e5de2cf1 100644 --- a/src/components/common/Input/Input.styles.jsx +++ b/src/components/common/Input/Input.styles.jsx @@ -1,4 +1,5 @@ -import { styled } from "styled-components"; +import styled from "styled-components"; +import theme from "../../../styles/theme"; export const InputContainer = styled.div` width: 100%; @@ -12,12 +13,13 @@ export const Label = styled.label` font-size: 18px; font-weight: 700; line-height: 26px; - color: var(--gray800); + color: ${theme.color.gray800}; `; export const StyledInput = styled.input` width: 100%; - background-color: var(--gray100); + height: ${({ height }) => height || "100%"}; + background-color: ${theme.color.gray100}; padding: 16px 24px; border-radius: 12px; resize: none; @@ -27,9 +29,17 @@ export const StyledInput = styled.input` } &::placeholder { - font-size: 16px; - font-weight: 400; - line-height: 26px; - color: var(--gray400); + font: ${theme.font.H5Regular}; + color: ${theme.color.gray400}; + } + @media (max-width: 375px) { + height: ${({ $largeHeight }) => $largeHeight}; + &::placeholder { + font: ${theme.font.H7Regular}; + } + } + + @media (min-width: 376px) and (max-width: 768px) { + height: ${({ $largeHeight }) => $largeHeight}; } `; diff --git a/src/components/pages/AddItemPage/AddItemPage.jsx b/src/components/pages/AddItemPage/AddItemPage.jsx index a1813c3a..f1ddc6a5 100644 --- a/src/components/pages/AddItemPage/AddItemPage.jsx +++ b/src/components/pages/AddItemPage/AddItemPage.jsx @@ -124,7 +124,11 @@ export default function AddItemPage() { - + {INPUT.map((i, idx) => ( - {values.tags.map((t, idx) => ( - handleTagDelete(t)} /> + {values.tags.map((t) => ( + handleTagDelete(t)} /> ))} diff --git a/src/components/pages/AddItemPage/AddItemPage.styles.jsx b/src/components/pages/AddItemPage/AddItemPage.styles.jsx index 7d372d8f..5d8e70b4 100644 --- a/src/components/pages/AddItemPage/AddItemPage.styles.jsx +++ b/src/components/pages/AddItemPage/AddItemPage.styles.jsx @@ -1,4 +1,5 @@ import { styled } from "styled-components"; +import theme from "../../../styles/theme"; export const AddItemContainer = styled.div` display: flex; @@ -36,7 +37,7 @@ export const Add = styled.h1` font-size: 20px; font-weight: 700; line-height: 32px; - color: var(--gray800); + color: ${theme.color.gray800}; `; export const AddBtn = styled.button` @@ -46,12 +47,12 @@ export const AddBtn = styled.button` font-size: 16px; font-weight: 600; line-height: 26px; - background-color: var(--primary); - color: var(--gray100); + background-color: ${theme.color.blue}; + color: ${theme.color.gray100}; cursor: pointer; &:disabled { - background-color: var(--gray400); + background-color: ${theme.color.gray400}; } `; diff --git a/src/components/pages/ProductPage/ProductPage.jsx b/src/components/pages/ProductPage/ProductPage.jsx new file mode 100644 index 00000000..e8330126 --- /dev/null +++ b/src/components/pages/ProductPage/ProductPage.jsx @@ -0,0 +1,22 @@ +import * as S from "./ProductPage.styles"; +import Detail from "../../Detail/Detail"; +import Comment from "../../Comment/Comment"; +import goBack from "../../../assets/icons/goBack.svg"; +import { useNavigate } from "react-router-dom"; + +export default function ProductPage() { + const navigate = useNavigate(); + + return ( + + + + + + navigate("/items")}> + 목록으로 돌아가기 + + + + ); +} diff --git a/src/components/pages/ProductPage/ProductPage.styles.jsx b/src/components/pages/ProductPage/ProductPage.styles.jsx new file mode 100644 index 00000000..16c12d32 --- /dev/null +++ b/src/components/pages/ProductPage/ProductPage.styles.jsx @@ -0,0 +1,55 @@ +import styled from "styled-components"; +import theme from "../../../styles/theme"; + +export const Product = styled.div` + width: 100%; + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: center; + padding: 0 24px; + margin: 24px 0 60px; + gap: 62px; + + @media (max-width: 375px) { + padding: 0 16px; + gap: 40px; + } + + @media (min-width: 376px) and (max-width: 768px) { + padding: 0 16px; + gap: 56px; + } +`; + +export const Container = styled.div` + width: 100%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 40px; +`; + +export const GoBackToList = styled.div` + width: 240px; + height: 48px; + display: flex; + justify-content: center; + align-items: center; + gap: 8px; + background-color: ${theme.color.blue}; + border-radius: 40px; + font: ${theme.font.H4Bold}; + color: ${theme.color.gray100}; + cursor: pointer; + + img { + width: 24px; + height: 24px; + } + + &:hover { + background-color: ${theme.color.blueHover}; + } +`; diff --git a/src/hooks/useResize.js b/src/hooks/useResize.js new file mode 100644 index 00000000..4bb0f51b --- /dev/null +++ b/src/hooks/useResize.js @@ -0,0 +1,32 @@ +import { useState, useEffect, useMemo } from "react"; +import throttle from "lodash.throttle"; + +export default function useResize(mobile, tablet, desktop) { + const [showItems, setShowItems] = useState(desktop); + + const updateItemsCount = () => { + if (window.innerWidth <= 767) { + setShowItems(mobile); + } else if (window.innerWidth >= 768 && window.innerWidth <= 1199) { + setShowItems(tablet); + } else { + setShowItems(desktop); + } + }; + + const handleThrottleUpdate = useMemo( + () => throttle(updateItemsCount, 500), + [] + ); + + useEffect(() => { + updateItemsCount(); + window.addEventListener("resize", handleThrottleUpdate); + return () => { + window.removeEventListener("resize", handleThrottleUpdate); + handleThrottleUpdate.cancel(); + }; + }, [handleThrottleUpdate, updateItemsCount]); + + return { showItems }; +} diff --git a/src/styles/GlobalStyle.jsx b/src/styles/GlobalStyle.jsx index de8f6a39..b44ad5c0 100644 --- a/src/styles/GlobalStyle.jsx +++ b/src/styles/GlobalStyle.jsx @@ -20,33 +20,32 @@ button, input, textarea { border:none; } -:root { - --primary: #3692ff; - --primary-hover: #1967d6; - --dark-text: #374151; - --bright-text: #f3f4f6; - --white: #ffffff; - --main-background: #fcfcfc; - --footer-background: #111827; - --landing-background: #cfe5ff; - --easy-login: #e6f2ff; - --gray50: #f9fafb; - --gray100: #f3f4f6; - --gray200: #e5e7eb; - --gray400: #9ca3af; - --gray500: #6b7280; - --gray600: #4b5563; - --gray700: #374151; - --gray800: #1f2937; - --gray900: #111827; - --error-red: #f74747; - --font: "Pretendard", sans-serif; +@font-face { + font-family: 'ROKAF Sans'; + src: url("/font/ROKAF.Bold.ttf"); + font-weight: 700; + font-style: normal; } + @font-face { - font-family: ROKAF Sans; - src: url("font/ROKAF\ Slab\ Serif\ Bold.ttf"); + font-family: 'Pretendard'; + src: url('https://fastly.jsdelivr.net/gh/Project-Noonnu/noonfonts_2107@1.1/Pretendard-Regular.woff2') format('woff2'); + src: url('https://fastly.jsdelivr.net/gh/Project-Noonnu/noonfonts_2107@1.1/Pretendard-Regular.woff') format('woff'); + font-display: swap; + font-weight: 400; + font-style: normal; + } + + @font-face { + font-family: "Pretendard"; + src: url('https://fastly.jsdelivr.net/gh/Project-Noonnu/noonfonts_2107@1.1/Pretendard-Bold.woff2') format('woff2'); + src: url('https://fastly.jsdelivr.net/gh/Project-Noonnu/noonfonts_2107@1.1/Pretendard-Bold.woff') format('woff'); + font-display: swap; + font-weight: 700; + font-style: normal; } + `; export default GlobalStyle; diff --git a/src/styles/theme.jsx b/src/styles/theme.jsx new file mode 100644 index 00000000..30d16c1b --- /dev/null +++ b/src/styles/theme.jsx @@ -0,0 +1,48 @@ +const theme = { + font: { + H0: "600 40px/47px 'Pretendard', sans-serif", + H1: "700 28px/42px 'Pretendard', sans-serif", + H2Bold: "700 24px/36px 'Pretendard', sans-serif", + H2Regular: "400 24px/36px 'Pretendard', sans-serif", + H3Bold: "700 20px/30px 'Pretendard', sans-serif", + H3Regular: "400 20px/30px 'Pretendard', sans-serif", + H4Bold: "700 18px/28px 'Pretendard', sans-serif", + H4Regular: "400 18px/28px 'Pretendard', sans-serif", + H5Bold: "700 16px/26px 'Pretendard', sans-serif", + H5Regular: "400 16px/26px 'Pretendard', sans-serif", + H6Bold: "700 15px/22px 'Pretendard', sans-serif", + H6Regular: "400 15px/22px 'Pretendard', sans-serif", + H7Bold: "700 14px/20px 'Pretendard', sans-serif", + H7Regular: "400 14px/20px 'Pretendard', sans-serif", + H8: "400 12px/20px 'Pretendard', sans-serif", + }, + color: { + textDark: "#374151", + textBright: "#f3f4f6", + + backgroundMain: "#fcfcfc", + backgroundFooter: "#111827", + backgroundLanding: "#cfe5ff", + + easyLogin: "#e6f2ff", + + white: "#ffffff", + blue: "#3692FF", + blueHover: "#1967d6", + + gray50: "#f9fafb", + gray100: "#f3f4f6", + gray200: "#e5e7eb", + gray300: "#D1D5DB", + gray400: "#9ca3af", + gray500: "#6b7280", + gray600: "#4b5563", + gray700: "#374151", + gray800: "#1f2937", + gray900: "#111827", + + error: "#f74747", + }, +}; + +export default theme;