diff --git a/README.md b/README.md index 4efeb7a..217896a 100644 --- a/README.md +++ b/README.md @@ -1,44 +1,30 @@ -### 스프린트 미션 제출 방법 +# 🍀일상의 모든 물건을 거래할 수 있는 판다마켓 +### ✅ [배포 사이트 보러가기!](https://hy-pandamarket-1.netlify.app/) -- **codeit-bootcamp-backend / QA-sprint-mission** 에서 `fork` 합니다. + - - main branch only 체크를 **해제**합니다. + -- `fork` 해 온 repository에서 자신의 담당에 맞는 `백엔드/풀스택` 브랜치를 `clone` 해서 작업합니다. - ```bash - git clone -b 백엔드/풀스택 --single-branch {저장소 URL} - ``` +--- -- **클론한 폴더 열고, base 브랜치가 `백엔드/풀스택` 브랜치인지 확인합니다.** 만약 그렇지 않다면, 아래 명령어로 base 브랜치를 이동합니다. +
- ```bash - git checkout 백엔드/풀스택 - ``` +# 💖프로젝트 소개 -- `백엔드/풀스택` 브랜치를 기준으로 각 미션마다 `백엔드/풀스택-mission번호` 브랜치를 생성합니다. +
- ```bash - git checkout -b 백엔드/풀스택-mission3 - ``` +### ✨ 인기 상품을 확인해보세요! +- 지금 가장 HOT한 중고거래 물품을 판다 마켓에서 확인해 보세요! +### 🔍구매를 원하는 상품을 검색해보세요! +- 구매하고 싶은 물품은 검색해서 쉽게 찾아보세요! +### 🪄 판매를 원하는 상품을 등록해보세요! +- 어떤 물건이든 판매하고 싶은 상품을 쉽게 등록하세요! -- `백엔드/풀스택-mission3` 브랜치에서 해당 스프린트 미션을 진행 후 `push` 합니다. -- 위 과정을 통해 미션을 다 완료했다면, **PR**(Pull Request)을 올립니다. - - ex) `jiwoo-im/백엔드-mission3` → `codeit-bootcamp-backend/백엔드` +
+## 🧐기술 스택 - -### PR 템플릿 - -```markdown -### 주요 변경사항 -- -- - -### QA 의견 -- -- -``` - +## 💖주요 기능 diff --git a/index.html b/index.html new file mode 100644 index 0000000..e579472 --- /dev/null +++ b/index.html @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + 판다마켓 + + + + + + + + + +
+ +
+ +
+

일상의 모든 물건을
거래해보세요

+ 구경하러가기 +
+ + +
+ 헤더 이미지 +
+
+
+ +
+
+
+ link_save_img +
+
Hot item
+ + 인기 상품을 +
+ 확인해보세요 +
+

+ 가장 HOT한 중고거래 물품을 +
+ 판다 마켓에서 확인해 보세요 +

+
+
+
+ +
+
+
+
Search
+ + 구매를 원하는 +
+ 상품을 검색하세요 +
+

+ 구매하고 싶은 물품은 검색해서 +
+ 쉽게 찾아보세요 +

+
+ folder_name_modify_img +
+
+ +
+
+ social_media_share_img +
+
Register
+ + 판매를 원하는 +
+ 상품을 등록하세요 +
+

+ 어떤 물건이든 판매하고 싶은 상품을 +
+ 쉽게 등록하세요 +

+
+
+
+
+
+
+
+

+ 믿을 수 있는
+ 판다마켓 중고거래 +

+
+
+ 바텀헤더 이미지 +
+
+
+ + + diff --git a/pages/faq.html b/pages/faq.html new file mode 100644 index 0000000..c5b3e00 --- /dev/null +++ b/pages/faq.html @@ -0,0 +1,12 @@ + + + + + + FQA + + + + FQA 페이지에용 + + diff --git a/pages/items.html b/pages/items.html new file mode 100644 index 0000000..f22ffd0 --- /dev/null +++ b/pages/items.html @@ -0,0 +1,12 @@ + + + + + + items + + + + 아이템 페이지에용 + + diff --git a/pages/privacy.html b/pages/privacy.html new file mode 100644 index 0000000..f97d90f --- /dev/null +++ b/pages/privacy.html @@ -0,0 +1,12 @@ + + + + + + Privacy + + + + 프라이버시 페이지에용 + + diff --git a/pages/signin.html b/pages/signin.html new file mode 100644 index 0000000..16900cf --- /dev/null +++ b/pages/signin.html @@ -0,0 +1,81 @@ + + + + + + + + + + Login + + +
+
+ +
+ + +
+ +
+
+ +
+
+ 판다마켓이 처음이신가요? + 회원가입 +
+
+ + + diff --git a/pages/signup.html b/pages/signup.html new file mode 100644 index 0000000..713fa20 --- /dev/null +++ b/pages/signup.html @@ -0,0 +1,110 @@ + + + + + + + + + + Signup + + +
+
+ +
+ + + + +
+ +
+
+ +
+
+ 이미 회원이신가요? + 로그인 +
+
+ + + diff --git a/scripts/auth/validConfirm.js b/scripts/auth/validConfirm.js new file mode 100644 index 0000000..f82b325 --- /dev/null +++ b/scripts/auth/validConfirm.js @@ -0,0 +1,30 @@ +import { addErrorStyle } from "../errors/errors.js"; +import { + USER_DATA, + inputEmail, + inputPassword, + emailErrorMessage, + passwordErrorMessage, +} from "../constants.js"; + +// <아이디&비밀번호 올바르게 입력했을 경우 /items로 이동하고 아닌 경우 확인메시지 출력> +export function validAccount(email, password) { + const accountMatch = USER_DATA.find((account) => account.email === email); + + if (accountMatch && accountMatch.password === password) { + window.location.href = "./items.html"; + return true; + } + + if (!accountMatch) { + addErrorStyle(inputEmail, emailErrorMessage, "이메일을 확인해주세요."); + } else if (accountMatch.password !== password) { + addErrorStyle( + inputPassword, + passwordErrorMessage, + "비밀번호를 확인해주세요." + ); + } + + return false; +} diff --git a/scripts/components/modal.js b/scripts/components/modal.js new file mode 100644 index 0000000..649cf83 --- /dev/null +++ b/scripts/components/modal.js @@ -0,0 +1,34 @@ +export function showModal(message) { + const modal = document.getElementById("error-modal"); + const modalMessage = document.getElementById("modal-message"); + if (modal && modalMessage) { + modalMessage.textContent = message; + modal.style.display = "block"; + } + + document.body.classList.add("modal-open"); +} + +export function hideModal() { + const modal = document.getElementById("error-modal"); + if (modal) { + modal.style.display = "none"; + document.body.classList.remove("modal-open"); + } +} + +document.addEventListener("DOMContentLoaded", () => { + hideModal(); + const confirmButton = document.getElementById("confirm-button"); + + if (confirmButton) { + confirmButton.addEventListener("click", hideModal); + } + + window.addEventListener("click", (event) => { + const modal = document.getElementById("error-modal"); + if (event.target === modal) { + hideModal(); + } + }); +}); diff --git a/scripts/constants.js b/scripts/constants.js new file mode 100644 index 0000000..56f744c --- /dev/null +++ b/scripts/constants.js @@ -0,0 +1,21 @@ +const USER_DATA = [ + { email: "codeit1@codeit.com", password: "codeit101!" }, + { email: "codeit2@codeit.com", password: "codeit202!" }, + { email: "codeit3@codeit.com", password: "codeit303!" }, + { email: "codeit4@codeit.com", password: "codeit404!" }, + { email: "codeit5@codeit.com", password: "codeit505!" }, + { email: "codeit6@codeit.com", password: "codeit606!" }, +]; + +const inputEmail = document.querySelector("#sign-up"); +const inputPassword = document.querySelector("#password"); +const emailErrorMessage = document.querySelector(".email-error-message"); +const passwordErrorMessage = document.querySelector(".password-error-message"); + +export { + USER_DATA, + inputEmail, + inputPassword, + emailErrorMessage, + passwordErrorMessage, +}; diff --git a/scripts/errors/errors.js b/scripts/errors/errors.js new file mode 100644 index 0000000..931424a --- /dev/null +++ b/scripts/errors/errors.js @@ -0,0 +1,19 @@ +//<입력하는 동안에는 에러메시지 안 보이게 하기 > +import { emailErrorMessage } from "../constants.js"; + +function errorMessageStop() { + emailErrorMessage.style.display = "none"; +} +//<에러가 발생한 경우 CSS속성 정리>> +function addErrorStyle(inputElement, errorMessageElement, errorMessage) { + inputElement.style.border = "1px solid red"; + errorMessageElement.style.display = "block"; + errorMessageElement.textContent = errorMessage; +} +function removeErrorStyle(inputElement, errorMessageElement, errorMessage) { + inputElement.style.border = "1px solid #ccd5e3"; + errorMessageElement.style.display = "none"; + errorMessageElement.textContent = errorMessage; +} + +export { errorMessageStop, addErrorStyle, removeErrorStyle }; diff --git a/scripts/input/checkEmailFormat.js b/scripts/input/checkEmailFormat.js new file mode 100644 index 0000000..f982673 --- /dev/null +++ b/scripts/input/checkEmailFormat.js @@ -0,0 +1,23 @@ +// <이메일 형식검증, 오류메시지 출력 > +import { addErrorStyle, removeErrorStyle } from "../errors/errors.js"; +import { inputEmail, emailErrorMessage } from "../constants.js"; +import { emailRegex } from "./emailRegex.js"; + +function checkEmailFormat() { + const email = inputEmail.value.trim(); + + if (email === "") { + addErrorStyle(inputEmail, emailErrorMessage, "이메일을 입력해주세요."); + } else if (!emailRegex(email)) { + addErrorStyle( + inputEmail, + emailErrorMessage, + "올바른 이메일 주소가 아닙니다." + ); + } else { + removeErrorStyle(inputEmail, emailErrorMessage); + return true; + } +} + +export { checkEmailFormat }; diff --git a/scripts/input/checkPassword.js b/scripts/input/checkPassword.js new file mode 100644 index 0000000..d91798c --- /dev/null +++ b/scripts/input/checkPassword.js @@ -0,0 +1,29 @@ +//<비밀번호 빈 값일때 오류메시지 출력> +import { addErrorStyle, removeErrorStyle } from "../errors/errors.js"; +import { inputPassword, passwordErrorMessage } from "../constants.js"; + +function checkPassword() { + const password = inputPassword.value; + + if (password === "") { + addErrorStyle( + inputPassword, + passwordErrorMessage, + "비밀번호를 입력해주세요." + ); + } else if ( + password.length < 8 || + !/^(?=.*[a-zA-Z])(?=.*[0-9])/.test(password) + ) { + addErrorStyle( + inputPassword, + passwordErrorMessage, + "비밀번호는 8자 이상 입력해주세요." + ); + } else { + removeErrorStyle(inputPassword, passwordErrorMessage); + return true; + } +} + +export { checkPassword }; diff --git a/scripts/input/emailDuplicationCheck.js b/scripts/input/emailDuplicationCheck.js new file mode 100644 index 0000000..1ffd675 --- /dev/null +++ b/scripts/input/emailDuplicationCheck.js @@ -0,0 +1,38 @@ +// import { inputEmail, emailErrorMessage } from "../constants.js"; +// import { addErrorStyle } from "../errors/errors.js"; + +// //이메일 중복을 확인하는 함수 +// async function emailDuplicationCheck(emailStr) { +// const checkEmail = { +// email: emailStr, +// }; +// try { +// const response = await fetch( +// "https://bootcamp-api.codeit.kr/api/check-email", +// { +// method: "POST", +// headers: { +// "Content-Type": "application/json", +// }, +// body: JSON.stringify(checkEmail), +// } +// ); + +// if (response.status === 200) { +// //중복이 아님 +// return true; +// } +// if (response.status === 409) { +// addErrorStyle( +// inputEmail, +// emailErrorMessage, +// "이미 사용중인 이메일입니다." +// ); +// return false; +// } +// } catch (error) { +// console.log(error); +// } +// } + +// export { emailDuplicationCheck }; diff --git a/scripts/input/emailRegex.js b/scripts/input/emailRegex.js new file mode 100644 index 0000000..a5a3fa8 --- /dev/null +++ b/scripts/input/emailRegex.js @@ -0,0 +1,6 @@ +function emailRegex(email) { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return emailRegex.test(email); +} + +export { emailRegex }; diff --git a/scripts/signin.js b/scripts/signin.js new file mode 100644 index 0000000..6820753 --- /dev/null +++ b/scripts/signin.js @@ -0,0 +1,96 @@ +import { errorMessageStop, addErrorStyle } from "./errors/errors.js"; +import { checkPassword } from "./input/checkPassword.js"; +import { toggleImage } from "./toggleImage.js"; +import { + inputEmail, + inputPassword, + emailErrorMessage, + passwordErrorMessage, + USER_DATA, +} from "./constants.js"; +import { checkEmailFormat } from "./input/checkEmailFormat.js"; +import { validAccount } from "./auth/validConfirm.js"; +import { hideModal, showModal } from "./components/modal.js"; + +const eyeImagePasswordEl = document.querySelector("#eyeImage-password"); +const eyeImagePassword = eyeImagePasswordEl.children[0]; +const submitButton = document.querySelector('button[type="submit"]'); +const loginForm = document.querySelector("form"); + +document.addEventListener("DOMContentLoaded", () => { + const modal = document.createElement("div"); + modal.id = "error-modal"; + modal.className = "modal"; + modal.innerHTML = ` + + `; + + // 모달을 body에 추가하고 최초에 숨김처리 + document.body.appendChild(modal); + hideModal(); + + // confirmButton 클릭 시 모달 숨기기 + const confirmButton = document.getElementById("confirm-button"); + confirmButton.addEventListener("click", hideModal); +}); + +function validateForm() { + const emailValid = + inputEmail.value.trim() !== "" && emailErrorMessage.textContent === ""; + const passwordValid = + inputPassword.value.trim() !== "" && + passwordErrorMessage.textContent === ""; + submitButton.disabled = !(emailValid && passwordValid); +} + +// 눈모양 아이콘 클릭 시 비밀번호 입력타입 변경 및 폼 유효성 검사 +eyeImagePassword.addEventListener("click", () => { + toggleImage(eyeImagePassword, inputPassword); + validateForm(); +}); + +// 입력 중에는 에러 메시지 숨기기 +inputEmail.addEventListener("input", () => { + errorMessageStop(); + validateForm(); +}); +inputPassword.addEventListener("input", () => { + errorMessageStop(); + validateForm(); +}); + +// 이메일 형식 검증 및 에러 메시지 출력 +inputEmail.addEventListener("focusout", () => { + checkEmailFormat(); + validateForm(); +}); + +// 비밀번호 빈 값일 때 에러 메시지 출력 +inputPassword.addEventListener("focusout", () => { + checkPassword(); + validateForm(); +}); + +// 모달 초기 숨김 상태 및 유효성 검사 호출 +validateForm(); + +// 로그인 폼 제출 시 계정 유효성 검사 및 모달 표시 +loginForm.addEventListener("submit", function (event) { + event.preventDefault(); + const email = inputEmail.value.trim(); + const password = inputPassword.value; + const isValid = validAccount(email, password); + + if (!isValid) { + showModal("존재하지 않는 이메일, 혹은 비밀번호입니다"); + } +}); + +// 페이지 로드 시 초기 유효성 검사 호출 +document.addEventListener("DOMContentLoaded", () => { + hideModal(); + validateForm(); +}); diff --git a/scripts/signup.js b/scripts/signup.js new file mode 100644 index 0000000..6146050 --- /dev/null +++ b/scripts/signup.js @@ -0,0 +1,164 @@ +import { + errorMessageStop, + addErrorStyle, + removeErrorStyle, +} from "./errors/errors.js"; +import { toggleImage } from "./toggleImage.js"; +import { checkPassword } from "./input/checkPassword.js"; +import { + USER_DATA, + inputEmail, + inputPassword, + emailErrorMessage, + passwordErrorMessage, +} from "./constants.js"; +import { checkEmailFormat } from "./input/checkEmailFormat.js"; +import { hideModal, showModal } from "./components/modal.js"; + +const eyeImagePasswordEl = document.querySelector("#eyeImage-password"); +const eyeImagePasswordReEl = document.querySelector("#eyeImage-password-re"); +const eyeImagePassword = eyeImagePasswordEl.children[0]; +const eyeImagePasswordRe = eyeImagePasswordReEl.children[0]; +const submitButton = document.querySelector('button[type="submit"]'); +const signupForm = document.querySelector("form"); +const inputPasswordRe = document.querySelector("#password-re"); +const passwordReErrorMessage = document.querySelector( + ".password-re-error-message" +); + +document.addEventListener("DOMContentLoaded", () => { + // 모달을 생성합니다. + const modal = document.createElement("div"); + modal.id = "error-modal"; + modal.className = "modal"; + modal.innerHTML = ` + + `; + + // 모달을 body에 추가합니다. + document.body.appendChild(modal); + + // 페이지가 로드될 때 모달을 숨깁니다. + hideModal(); + + // confirmButton 클릭 시 모달 숨기기 + const confirmButton = document.getElementById("confirm-button"); + confirmButton.addEventListener("click", hideModal); +}); + +// <눈모양 아이콘 적용, 비밀번호 입력타입 변경> +eyeImagePassword.addEventListener("click", () => { + toggleImage(eyeImagePassword, inputPassword); +}); +eyeImagePasswordRe.addEventListener("click", () => { + toggleImage(eyeImagePasswordRe, inputPasswordRe); +}); + +//<입력하는 동안에는 에러메시지 안 보이게 하기> +inputEmail.addEventListener("input", () => { + errorMessageStop(); + validateForm(); +}); +inputPassword.addEventListener("input", () => { + errorMessageStop(); + validateForm(); +}); +inputPasswordRe.addEventListener("input", () => { + errorMessageStop(); + validateForm(); +}); + +//<이메일 형식 검증> +inputEmail.addEventListener("focusout", () => { + checkEmailFormat(); + validateForm(); +}); + +//<비밀번호 형식 검증> +inputPassword.addEventListener("focusout", () => { + checkPassword(); + validateForm(); +}); + +// 비밀번호-비밀번호 확인 값 일치 확인 +function checkPasswordRe() { + const password = inputPassword.value; + const passwordRe = inputPasswordRe.value; + if (password !== passwordRe) { + addErrorStyle( + inputPasswordRe, + passwordReErrorMessage, + "비밀번호가 일치하지 않아요." + ); + return false; + } else { + removeErrorStyle(inputPasswordRe, passwordReErrorMessage); + return true; + } +} +inputPasswordRe.addEventListener("focusout", checkPasswordRe); + +// 폼 유효성 검사 및 버튼 상태 업데이트 +function validateForm() { + const isEmailValid = + inputEmail.value.trim() !== "" && emailErrorMessage.textContent === ""; + const isPasswordValid = + inputPassword.value.trim() !== "" && + passwordErrorMessage.textContent === ""; + const isPasswordReValid = + inputPasswordRe.value.trim() !== "" && + passwordReErrorMessage.textContent === ""; + + submitButton.disabled = !( + isEmailValid && + isPasswordValid && + isPasswordReValid + ); +} + +console.log("1번"); +// 이메일과 비밀번호 모두 유효한 값이라면 버튼 동작하도록 함 + +function submitAccount() { + const isEmailValid = checkEmailFormat(); + const isPasswordValid = checkPassword(); + const isPasswordReValid = checkPasswordRe(); + + if (isEmailValid && isPasswordValid && isPasswordReValid) { + return true; + } + return false; +} + +validateForm(); +console.log("2번"); + +signupForm.addEventListener("submit", function (event) { + event.preventDefault(); + + // 이메일과 비밀번호 입력값 가져오기 + const email = inputEmail.value.trim(); + const password = inputPassword.value.trim(); + + // 이미 있는 이메일인지 확인 + const isExistingEmail = USER_DATA.some((user) => user.email === email); + console.log("4번"); + // 이미 있는 이메일인 경우 모달 표시 + if (isExistingEmail) { + console.log("이메일 중복"); + showModal("중복된 이메일입니다."); + return; // 함수 종료 + } + console.log("5번"); + + // 유효성 검사 통과 후 회원가입 진행 + if (submitAccount()) { + window.location.href = "./items.html"; + } +}); + +hideModal(); +validateForm(); diff --git a/scripts/toggleImage.js b/scripts/toggleImage.js new file mode 100644 index 0000000..3763449 --- /dev/null +++ b/scripts/toggleImage.js @@ -0,0 +1,12 @@ +// <눈모양 아이콘 적용, 비밀번호 입력타입 변경> +function toggleImage(image, inputPassword) { + if (image.src.includes("eye-close")) { + image.setAttribute("src", "/src/assets/svg/eye-open-icon.svg"); + inputPassword.setAttribute("type", "text"); + } else { + image.setAttribute("src", "/src/assets/svg/eye-close-icon.svg"); + inputPassword.setAttribute("type", "password"); + } +} + +export { toggleImage }; diff --git a/src/assets/image/Header-Content-Image.png b/src/assets/image/Header-Content-Image.png new file mode 100644 index 0000000..85a793c Binary files /dev/null and b/src/assets/image/Header-Content-Image.png differ diff --git a/src/assets/image/home_01-image.png b/src/assets/image/home_01-image.png new file mode 100644 index 0000000..e4e9c3c Binary files /dev/null and b/src/assets/image/home_01-image.png differ diff --git a/src/assets/image/home_02-image.png b/src/assets/image/home_02-image.png new file mode 100644 index 0000000..1bdd747 Binary files /dev/null and b/src/assets/image/home_02-image.png differ diff --git a/src/assets/image/home_03-image.png b/src/assets/image/home_03-image.png new file mode 100644 index 0000000..0994c0e Binary files /dev/null and b/src/assets/image/home_03-image.png differ diff --git a/src/assets/image/home_bottom-image.png b/src/assets/image/home_bottom-image.png new file mode 100644 index 0000000..e563f39 Binary files /dev/null and b/src/assets/image/home_bottom-image.png differ diff --git a/src/assets/svg/Logo-Large.svg b/src/assets/svg/Logo-Large.svg new file mode 100644 index 0000000..7c2d736 --- /dev/null +++ b/src/assets/svg/Logo-Large.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/assets/svg/Logo-Small.svg b/src/assets/svg/Logo-Small.svg new file mode 100644 index 0000000..bdb3c4d --- /dev/null +++ b/src/assets/svg/Logo-Small.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svg/X-icon.svg b/src/assets/svg/X-icon.svg new file mode 100644 index 0000000..ada9a50 --- /dev/null +++ b/src/assets/svg/X-icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/svg/default-profile-icon.svg b/src/assets/svg/default-profile-icon.svg new file mode 100644 index 0000000..acc9826 --- /dev/null +++ b/src/assets/svg/default-profile-icon.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/svg/eye-close-icon.svg b/src/assets/svg/eye-close-icon.svg new file mode 100644 index 0000000..a24694f --- /dev/null +++ b/src/assets/svg/eye-close-icon.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/assets/svg/eye-open-icon.svg b/src/assets/svg/eye-open-icon.svg new file mode 100644 index 0000000..43a5af1 --- /dev/null +++ b/src/assets/svg/eye-open-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svg/facebook-icon.svg b/src/assets/svg/facebook-icon.svg new file mode 100644 index 0000000..9261570 --- /dev/null +++ b/src/assets/svg/facebook-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svg/google-login-icon.svg b/src/assets/svg/google-login-icon.svg new file mode 100644 index 0000000..8a096b0 --- /dev/null +++ b/src/assets/svg/google-login-icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/svg/inquiry_empty-icon.svg b/src/assets/svg/inquiry_empty-icon.svg new file mode 100644 index 0000000..b087d0d --- /dev/null +++ b/src/assets/svg/inquiry_empty-icon.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/src/assets/svg/instagram-icon.svg b/src/assets/svg/instagram-icon.svg new file mode 100644 index 0000000..bb6820d --- /dev/null +++ b/src/assets/svg/instagram-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svg/kakao-login-icon.svg b/src/assets/svg/kakao-login-icon.svg new file mode 100644 index 0000000..57908a2 --- /dev/null +++ b/src/assets/svg/kakao-login-icon.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/assets/svg/plus-icon.svg b/src/assets/svg/plus-icon.svg new file mode 100644 index 0000000..5bb9abf --- /dev/null +++ b/src/assets/svg/plus-icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/svg/reply_empty-icon.svg b/src/assets/svg/reply_empty-icon.svg new file mode 100644 index 0000000..dbac501 --- /dev/null +++ b/src/assets/svg/reply_empty-icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/svg/twitter-icon.svg b/src/assets/svg/twitter-icon.svg new file mode 100644 index 0000000..3d1ede5 --- /dev/null +++ b/src/assets/svg/twitter-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svg/youtube-icon.svg b/src/assets/svg/youtube-icon.svg new file mode 100644 index 0000000..1d332e7 --- /dev/null +++ b/src/assets/svg/youtube-icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/style/index.css b/src/style/index.css new file mode 100644 index 0000000..199509f --- /dev/null +++ b/src/style/index.css @@ -0,0 +1,666 @@ +@import "./reset.css"; + +/* header */ +nav { + width: 100%; + background-color: var(--White); + position: sticky; + top: 0; + z-index: 999; +} + +.nav_logo { + width: 15.3rem; + height: 5.1rem; + background-size: contain; + background-repeat: no-repeat; + background-position: center; + background-image: url("/src/assets/svg/Logo-Large.svg"); + + @media (min-width: 375px) and (max-width: 767px) { + width: 8.1rem; + height: 2.7rem; + background-image: url("/src/assets/svg/Logo-Small.svg"); + } +} + +.header_nav { + top: 0; + position: sticky; + max-width: 192rem; + margin: auto; + padding-left: 20rem; + padding-right: 20rem; + display: flex; + align-items: center; + background-color: var(--White); + + @media (min-width: 768px) and (max-width: 1199px) { + padding-left: 2.4rem; + padding-right: 2.4rem; + } + + @media (min-width: 375px) and (max-width: 767px) { + padding-left: 1.6rem; + padding-right: 1.6rem; + } +} + +.header_category { + width: 100%; + margin-left: 3.2rem; + + display: flex; + flex-direction: row; + justify-content: flex-start; + + @media (min-width: 768px) and (max-width: 1199px) { + margin-left: 2rem; + } + + @media (min-width: 375px) and (max-width: 767px) { + margin-left: 1.6rem; + } +} + +.category_btn { + padding: 2.4rem 1.6rem; + + font-size: 1.8rem; + font-style: normal; + font-weight: 700; + line-height: normal; + + color: var(--Cool-Gray600); + + @media (min-width: 375px) and (max-width: 767px) { + font-size: 1.6rem; + } +} + +.login_btn { + height: 4.2rem; + padding: 1.2rem 2.3rem; + border-radius: 0.8rem; + + display: inline-flex; + flex-direction: row; + justify-content: center; + align-items: center; + flex-shrink: 0; + + background: var(--Brand-Blue); + color: var(--White); + + font-size: 1.6rem; + font-weight: 600; +} + +.header_section { + width: 100%; + height: 54rem; + position: relative; + display: flex; + flex-direction: row; + justify-content: center; + + background-color: #cfe5ff; + + @media (min-width: 768px) and (max-width: 1199px) { + height: 77.1rem; + flex-direction: column; + } + @media (max-width: 767px) { + height: 54rem; + /* display: flex; */ + flex-direction: column; + } +} + +.header_container { + width: 100%; + height: 100%; + + display: flex; + + flex-direction: row; + justify-content: center; + align-items: center; + /* 추가 */ + overflow: hidden; + + @media (min-width: 768px) and (max-width: 1199px) { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + } + @media (max-width: 767px) { + flex-direction: column; + } +} + +.bottom_header_section .header_container { + @media (min-width: 768px) and (max-width: 1199px) { + display: flex; + /* margin-top: 9.4rem; */ + flex-direction: column; + justify-content: normal; + align-items: normal; + } +} + +.header_title { + padding-right: 6.2rem; + + color: var(--Cool-Gray700); + font-size: 4rem; + font-weight: 700; + line-height: 140%; + + @media (min-width: 768px) and (max-width: 1199px) { + padding-right: 0; + } + + @media (max-width: 767px) { + font-size: 3.2rem; + padding-right: 0; + } +} + +.header_title br { + display: inline; + + @media (min-width: 768px) and (max-width: 1199px) { + display: none; + } + @media (max-width: 767px) { + display: inline; + } +} + +.bottom_header_section .header_title br { + display: inline; + + @media (max-width: 1199px) { + display: inline; + } +} + +.header_btn { + margin-top: 3.2rem; + padding: 1.6rem 12.4rem; + border-radius: 4rem; + + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + + background: var(--Brand-Blue); + color: var(--White); + + font-size: 2rem; + font-weight: 600; + line-height: 24px; + + @media (min-width: 768px) and (max-width: 1199px) { + margin-top: 2.4rem; + } + + @media (max-width: 767px) { + margin-top: 1.6rem; + } +} + +.header_wrapper { + width: 35.7rem; + + display: flex; + flex-direction: column; + align-items: flex-start; + position: absolute; + left: 36rem; + + z-index: 1; + + @media (min-width: 768px) and (max-width: 1199px) { + width: 100%; + padding: 9.4rem 0; + position: relative; + left: 0; + align-items: center; + } + + @media (max-width: 767px) { + width: 100%; + padding: 4.85rem 0; + left: 50%; + top: 0; + transform: translateX(-50%); + align-items: center; + } +} + +.bottom_header_wrapper { + width: 35.7rem; + /* height: 20rem; */ + + display: flex; + flex-direction: column; + align-items: flex-start; + position: absolute; + left: 36rem; + + z-index: 1; + + @media (min-width: 768px) and (max-width: 1199px) { + /* display: flex; */ + width: 100%; + padding: 9.4rem 0; + position: relative; + left: 0; + align-items: center; + } + + @media (max-width: 767px) { + width: 100%; + padding-top: 9rem; + padding-bottom: 9rem; + left: 50%; + top: 0; + transform: translateX(-50%); + align-items: center; + } +} + +.bottom .header_title { + @media (min-width: 768px) and (max-width: 1199px) { + display: flex; + width: 100%; + padding: 6rem 0; + position: relative; + left: 0; + align-items: center; + } +} + +.header_img_container { + padding-top: 3rem; + margin-bottom: -9.1rem; + display: flex; + position: absolute; + flex-direction: row; + align-items: flex-end; + right: 22.3rem; + + @media (min-width: 768px) and (max-width: 1199px) { + width: 100%; + display: flex; + padding-top: 3rem; + margin: 0 auto; + position: relative; + justify-content: center; + align-items: flex-end; + right: 0; + } + + @media (max-width: 767px) { + width: 100%; + display: flex; + margin: 0 auto; + padding: 0; + position: absolute; + left: 50%; + transform: translateX(-50%); + bottom: 0; + + justify-content: center; + align-items: flex-end; + right: 0; + height: 262px; + overflow: hidden; + } +} + +.header_img_container img { + width: 996px; + height: 417px; + overflow: hidden; + object-fit: cover; + + @media (max-width: 767px) { + min-width: 626px; + margin-top: 1.88rem; + width: 626px; + height: 262px; + object-fit: cover; + height: auto; + } +} + +/* section */ +main { + max-width: 120rem; + margin: 0 auto; + flex-shrink: 0; +} + +section { + display: flex; + justify-content: center; + align-items: center; +} + +.section_container_left { + width: 100%; + padding: 13.8rem 0; + + display: flex; + flex-direction: row; + justify-content: flex-start; + align-items: center; + + @media (min-width: 768px) and (max-width: 1199px) { + padding: 4rem 2.4rem; + justify-content: center; + } + + @media (max-width: 767px) { + padding: 2.4rem 1.6rem; + } +} + +.section_container_right { + width: 100%; + padding: 13.8rem 0; + + display: flex; + flex-direction: row; + justify-content: flex-end; + align-items: center; + + @media (min-width: 768px) and (max-width: 1199px) { + padding: 4rem 2.4rem; + justify-content: center; + } + + @media (max-width: 767px) { + padding: 2.4rem 1.6rem; + } +} + +.section_wrapper { + display: flex; + flex-direction: row; + + @media (max-width: 1199px) { + flex-direction: column; + } +} + +.section_wrapper.right { + @media (max-width: 1199px) { + flex-direction: column-reverse; + } +} + +.text_content_left { + margin-right: 6.4em; + + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-start; + + @media (min-width: 768px) and (max-width: 1199px) { + text-align: right; + margin-right: 0; + align-items: flex-end; + } + + @media (max-width: 767px) { + margin-top: 0.8rem; + margin-right: 0; + text-align: right; + align-items: flex-end; + } +} + +.text_content_right { + margin-left: 6.4rem; + + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-start; + + @media (min-width: 768px) and (max-width: 1199px) { + margin-left: 0; + } + + @media (max-width: 767px) { + margin-top: 0.8rem; + margin-left: 0; + } +} + +.section_badge { + color: var(--brand-blue, #3692ff); + font-family: Pretendard; + font-size: 1.8rem; + font-style: normal; + font-weight: 700; + line-height: 140%; + + @media (min-width: 768px) and (max-width: 1199px) { + margin-top: 1.6rem; + } + + @media (max-width: 767px) { + font-size: 1.6rem; + } +} + +.section_title { + margin-top: 1.2rem; + margin-bottom: 2.4rem; + + font-size: 4rem; + font-style: normal; + font-weight: 700; + line-height: 140%; + letter-spacing: 0.08rem; + color: var(--Cool-Gray700); + + @media (min-width: 768px) and (max-width: 1199px) { + margin-top: 0.8rem; + margin-bottom: 1.6rem; + + font-size: 3.2rem; + } + + @media (max-width: 767px) { + font-size: 2.4rem; + margin: 0; + } +} + +.section_title br { + display: inline; + + @media (max-width: 1199px) { + display: none; + } +} + +.section_summary { + font-size: 2.4rem; + font-style: normal; + font-weight: 500; + line-height: 120%; + letter-spacing: 0.192rem; + color: var(--Cool-Gray700); + + @media (min-width: 768px) and (max-width: 1199px) { + font-size: 1.8rem; + } + + @media (max-width: 767px) { + font-size: 1.6rem; + } +} + +.section_img { + width: 58.8rem; + height: 44.4rem; + + display: flex; + flex-direction: row; + justify-content: flex-start; + + @media (min-width: 768px) and (max-width: 1199px) { + width: 69.6rem; + height: auto; + } + + @media (max-width: 767px) { + width: 100%; + height: auto; + } +} + +/* bottom_header */ +.bottom_header_img_container { + display: flex; + flex-direction: row; + margin-bottom: -6.8rem; + display: flex; + position: absolute; + flex-direction: row; + align-items: flex-end; + + @media (min-width: 768px) and (max-width: 1199px) { + width: 100%; + display: flex; + margin: 0 auto; + position: relative; + justify-content: center; + align-items: flex-end; + right: 0; + } + + @media (max-width: 767px) { + width: 100%; + display: flex; + margin: 0 auto; + padding: 0; + position: absolute; + left: 50%; + transform: translateX(-50%); + bottom: 0; + + justify-content: center; + align-items: flex-end; + right: 0; + height: 262px; + overflow: hidden; + } +} + +.bottom_header_img_container img { + width: 996px; + height: 472px; + overflow: hidden; + object-fit: cover; + + @media (max-width: 767px) { + display: flex; + width: 498px; + height: 270px; + padding-top: 34.042px; + justify-content: center; + align-items: center; + flex-shrink: 0; + } +} + +.bottom_header_section { + width: 100%; + height: 54rem; + margin-top: 13.8rem; + + position: relative; + + background-color: #cfe5ff; + + @media (min-width: 768px) and (max-width: 1199px) { + height: 77.1rem; + margin-top: 4rem; + } +} + +/* footer */ +footer { + width: 100%; + height: 16rem; + + padding-top: 3.2rem; + + display: flex; + justify-content: center; + + background-color: var(--Cool-Gray900); + + @media (max-width: 767px) { + padding-top: 0; + height: 16rem; + } +} + +.footer_wrapper { + width: 100%; + max-width: 192rem; + height: fit-content; + padding: 0 10.4rem; + + display: grid; + grid-template: "copyright explain sns_icon"; + justify-content: space-between; + + @media (max-width: 767px) { + padding: 3.2rem; + display: grid; + grid-template-areas: + "explain sns_icon" + "copyright ."; + row-gap: 6rem; + justify-content: space-around; + } +} + +.copyright { + grid-area: copyright; + + font-size: 1.6rem; + color: var(--Cool-Gray400); +} + +.explain { + grid-area: explain; + padding-right: 1.8rem; + display: flex; + column-gap: 3rem; +} + +.footer_link { + font-size: 1.6rem; + color: var(--Cool-Gray400); +} + +.sns_icon { + grid-area: sns_icon; + height: 2rem; + display: flex; + column-gap: 1.2rem; +} diff --git a/src/style/modal.css b/src/style/modal.css new file mode 100644 index 0000000..c2e6153 --- /dev/null +++ b/src/style/modal.css @@ -0,0 +1,91 @@ +@import "./reset.css"; + +body.modal-open { + overflow: hidden; +} + +.modal { + display: none; + position: fixed; + z-index: 1; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: auto; + background-color: rgba(0, 0, 0, 0.7); + display: flex; + align-items: center; + justify-content: center; +} + +.modal-content { + width: 54rem; + height: 25rem; + position: absolute; + transform: translate(-50%, -50%); + top: 50%; + left: 50%; + + display: flex; + flex-direction: column; + align-items: center; + + background-color: var(--White); + border: 1px solid #888; + + text-align: center; + box-shadow: 0px 4px 16px rgba(108, 57, 57, 0.2); + border-radius: 8px; + + @media (max-width: 767px) { + width: 32.7rem; + height: 22rem; + } +} + +p { + margin-top: 10.8rem; + + color: var(--Cool-Gray-800); + text-align: center; + + font-size: 1.6rem; + font-style: normal; + font-weight: 500; + line-height: normal; + + @media (max-width: 767px) { + margin-top: 8.1rem; + } +} + +.confirm-button { + width: 12rem; + height: 4.8rem; + padding: 1.2rem 2.3rem; + border: none; + border-radius: 5px; + + position: absolute; + right: 2.8rem; + bottom: 2.8rem; + + background-color: var(--brand-blue); + color: white; + + cursor: pointer; + font-size: 1.6rem; + + @media (max-width: 767px) { + position: absolute; + left: 50%; + bottom: 2.3rem; + transform: translateX(-50%); + } +} + +.confirm-button:hover, +.confirm-button:focus { + background-color: var(--brand-blue); +} diff --git a/src/style/reset.css b/src/style/reset.css new file mode 100644 index 0000000..5837548 --- /dev/null +++ b/src/style/reset.css @@ -0,0 +1,43 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; + vertical-align: top; + font-family: "Pretendard"; +} + +:root { + --White: #ffffff; + --Cool-Gray50: #f9fafb; + --Cool-Gray100: #f3f4f6; + --Cool-Gray200: #e5e7eb; + --Cool-Gray300: #d1d5db; + --Cool-Gray400: #9ca3af; + --Cool-Gray500: #6b7280; + --Cool-Gray600: #4b5563; + --Cool-Gray700: #374151; + --Cool-Gray800: #1f2937; + --Cool-Gray900: #111827; + --gray-gray500: #737373; + --error-red: #f74747; + --Brand-Blue: #3182f6; + --brand-blue: #3692ff; +} + +html, +body { + position: relative; + background-color: --var(--White); + font-size: 62.5%; +} + +button { + outline: none; + cursor: pointer; +} + +a { + text-decoration: none; + cursor: pointer; + color: inherit; +} diff --git a/src/style/sign.css b/src/style/sign.css new file mode 100644 index 0000000..b93664a --- /dev/null +++ b/src/style/sign.css @@ -0,0 +1,237 @@ +@import "./reset.css"; + +body { + display: flex; + flex-direction: column; + align-items: center; + font-family: "Pretendard"; +} + +form { + width: 64rem; + margin-top: 6rem; + margin-bottom: 41.8rem; + + display: flex; + flex-direction: column; + align-items: center; + + @media (max-width: 767px) { + width: 100%; + max-width: 40rem; + margin-top: 4.8rem; + margin-bottom: 38rem; + } +} + +header { + width: 100%; + margin-bottom: 4rem; + + display: flex; + flex-direction: column; + align-items: center; + + @media (max-width: 767px) { + margin-bottom: 2.4rem; + } +} + +.title-logo { + width: 39.6rem; + height: 13.2rem; + + @media (max-width: 767px) { + width: 19.8rem; + height: 6.6rem; + } +} + +.title-logo img { + width: 100%; +} + +.title-subtext { + margin-top: 1.2rem; + + text-align: center; + font-size: 1.5rem; + font-weight: 500; + color: var(--Cool-Gray800); +} + +label { + width: 100%; + max-width: 64rem; + margin: 1.2rem 0; + border-radius: 0.8rem; + + display: flex; + flex-direction: column; + + font-size: 1.8rem; + font-weight: 700; + line-height: 21.48px; + + @media (max-width: 767px) { + max-width: 40rem; + padding: 0 1.6rem; + } +} + +button:disabled { + width: 100%; + padding: 1.6rem 2rem; + margin: 1.2rem auto; + + display: flex; + justify-content: center; + align-items: center; + + color: var(--White); + border-radius: 4rem; + border: none; + background: var(--Cool-Gray400); + font-size: 1.8rem; + font-weight: 600; + + cursor: not-allowed; +} + +button { + width: 100%; + padding: 1.6rem 2rem; + margin: 1.2rem auto; + + display: flex; + justify-content: center; + align-items: center; + + color: var(--White); + border-radius: 4rem; + border: none; + background-color: var(--Brand-Blue); + + font-size: 1.8rem; + font-weight: 600; + + cursor: pointer; +} + +.padding_helper { + width: 100%; + + @media (max-width: 767px) { + padding: 0 1.6rem; + } +} + +.input-container { + width: 100%; + margin-top: 1.6rem; +} + +input { + width: 100%; + padding: 1.6rem 2.4rem; + + outline: none; + display: flex; + justify-content: center; + align-items: center; + border-radius: 1.2rem; + border: 0.2rem solid var(--Cool-Gray100); + background: var(--Cool-Gray100); +} + +input:focus { + border-radius: 1.2rem; + border: 0.2rem solid #3692ff; +} + +.helper { + width: 100%; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + position: relative; +} + +.input-eye-off { + right: 1.5rem; + position: absolute; + display: flex; + flex-direction: row; + justify-content: center; +} + +.another-signup { + margin: 1.2rem 0; + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + width: 100%; + padding: 1.2rem 2.4rem; + border-radius: 0.8rem; + background-color: #e6f2ff; +} + +.another-signup-logo { + display: flex; + flex-direction: row; + justify-content: flex-end; + background-color: #e6f2ff; + gap: 1.6rem; +} + +.another-signup-text { + text-decoration: solid; + background-color: #e7effb; + font-size: 1.4rem; +} + +.deco_text { + text-decoration-line: underline; + color: var(--Brand-Blue); +} + +#eyeImage-password, +#eyeImage-password-re { + right: 1.5rem; + display: flex; + flex-direction: row; + justify-content: center; + cursor: pointer; +} + +.email-error-message { + padding-top: 0.8rem; + padding-left: 1.6rem; + + font-size: 1.5rem; + color: var(--error-red); + display: none; +} + +.password-error-message { + padding-top: 0.8rem; + padding-left: 1.6rem; + + font-size: 1.5rem; + color: var(--error-red); + display: none; +} + +.password-re-error-message { + padding-top: 0.8rem; + padding-left: 1.6rem; + + font-size: 1.5rem; + color: var(--error-red); +} + +.error-border { + border: 0.12rem var(--error-red); +}