diff --git a/.vscode/settings.json b/.vscode/settings.json index 6b665aaa..97b4e259 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,9 @@ { - "liveServer.settings.port": 5501 + "liveServer.settings.port": 5501, + "github.copilot.enable": { + "*": false, + "plaintext": false, + "markdown": false, + "scminput": false + } } diff --git a/src/css/auth.css b/src/css/auth.css index 391ebd82..d6789151 100644 --- a/src/css/auth.css +++ b/src/css/auth.css @@ -51,21 +51,36 @@ body { font-style: normal; font-weight: 400; line-height: 26px; + width: 100%; +} + +.auth__input.error { + border: 1px solid red; +} + +.auth__error-message { + font-size: 14px; + color: red; +} + +.auth__input.success { + border: 1px solid var(--blue); } -.auth__input:focus { +.hidden { + display: none; +} + +/* .auth__input:focus { border: 2px solid #3692ff; outline: none; -} +} */ .auth__visible { - width: 24px; - height: 24px; position: absolute; - right: 24px; - top: 71%; - transform: translateY(-50%); + right: 12px; cursor: pointer; + top: 58px; } .auth__button { @@ -88,6 +103,10 @@ body { cursor: pointer; } +.auth__button:disabled { + background: var(--gray-400); +} + .auth__social { margin-top: 24px; width: 100%; @@ -155,4 +174,8 @@ body { padding: 0 16px; max-width: 400px; } + + .auth__label { + font-size: 14px; + } } diff --git a/src/css/index.css b/src/css/index.css index 35748061..38e67c9a 100644 --- a/src/css/index.css +++ b/src/css/index.css @@ -317,6 +317,11 @@ .banner__container--bottom { gap: 0; } + + .highlight:nth-child(3) .highlight__container { + flex-direction: column; + align-items: flex-end; + } } /* 태블릿 */ @@ -337,9 +342,12 @@ padding: 24px; } - .highlight:nth-child(3) .highlight__container { - flex-direction: column; - align-items: flex-end; + .highlight__title { + font-size: 32px; + } + + .highlight__subtitle { + font-size: 18px; } .feature-content { @@ -370,9 +378,12 @@ padding: 52px 15px; } - .highlight:nth-child(3) .highlight__container { - flex-direction: column; - align-items: flex-end; + .highlight__title { + font-size: 24px; + } + + .highlight__subtitle { + font-size: 18px; } .br-highlight { @@ -380,7 +391,7 @@ } .footer { - padding: 32px 100px; + padding: 32px; } .footer__section { diff --git a/src/html/items.html b/src/html/items.html index e69de29b..1ea20598 100644 --- a/src/html/items.html +++ b/src/html/items.html @@ -0,0 +1,11 @@ + + + + + + items + + + items + + diff --git a/src/html/login.html b/src/html/login.html index 5c267bb4..38a7ae0d 100644 --- a/src/html/login.html +++ b/src/html/login.html @@ -7,6 +7,8 @@ + +
@@ -30,6 +32,7 @@ placeholder="이메일을 입력해주세요" required /> +
@@ -39,7 +42,9 @@ id="password" class="auth__input" placeholder="비밀번호를 입력해주세요" + required /> +
- +
diff --git a/src/html/signup.html b/src/html/signup.html index 6b7920e8..a4f85c01 100644 --- a/src/html/signup.html +++ b/src/html/signup.html @@ -7,6 +7,8 @@ + +
@@ -30,6 +32,7 @@ placeholder="이메일을 입력해주세요" required /> +
@@ -42,6 +45,7 @@ placeholder="닉네임을 입력해주세요" required /> +
@@ -54,6 +58,7 @@ placeholder="비밀번호를 입력해주세요" required /> +
- +
diff --git a/src/js/formvalidation.js b/src/js/formvalidation.js new file mode 100644 index 00000000..abfd587e --- /dev/null +++ b/src/js/formvalidation.js @@ -0,0 +1,67 @@ +export const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + +/* 에러 메시지 */ +export function showError(input, error, message) { + input.classList.remove('success'); + input.classList.add('error'); + error.textContent = message; + error.classList.remove('hidden'); +} + +/* 성공 메시지 */ +export function showSuccess(input, error) { + input.classList.add('success'); + input.classList.remove('error'); + error.classList.add('hidden'); +} + +/* 이메일 유효성 검사 */ +export function validationEmail(input, error) { + const value = input.value.trim(); + + if (value === '') { + showError(input, error, '이메일을 입력해주세요'); + } else if (!emailRegex.test(value)) { + showError(input, error, '잘못된 이메일 형식입니다'); + } else { + showSuccess(input, error); + } +} + +/* 비밀번호 유효성 검사 */ +export function validationPassword(input, error) { + const value = input.value.trim(); + + if (value === '') { + showError(input, error, '비밀번호를 입력해주세요'); + } else if (value.length < 8) { + showError(input, error, '비밀번호를 8자 이상 입력해주세요'); + } else { + showSuccess(input, error); + } +} + +/* 모든 입력값이 유효한지 확인 */ +export function inputsValid(input) { + for (let i = 0; i < input.length; i++) { + if (!input[i].classList.contains('success')) { + return false; + } + } + return true; +} + +/* 버튼 활성화 */ +export function updateButtonState(input, button) { + button.disabled = !inputsValid(input); +} + +/* 버튼 누르면 페이지 이동 */ +export function goToPage(input, button, url) { + button.addEventListener('click', (e) => { + e.preventDefault(); + if (inputsValid(input)) { + window.location.href = url; + } + }); +} diff --git a/src/js/login.js b/src/js/login.js new file mode 100644 index 00000000..1acec93f --- /dev/null +++ b/src/js/login.js @@ -0,0 +1,33 @@ +import { + validationEmail, + validationPassword, + updateButtonState, + goToPage, +} from './formvalidation.js'; +const emailInput = document.getElementById('email'); +const pwInput = document.getElementById('password'); +const emailError = emailInput.nextElementSibling; +const pwError = pwInput.nextElementSibling; +const loginButton = document.querySelector('.auth__button'); + +const inputs = [emailInput, pwInput]; + +/* 이메일 검증 */ +function validateEmail() { + validationEmail(emailInput, emailError); + updateButtonState(inputs, loginButton); +} + +/* 비밀번호 검증 */ +function validatePassword() { + validationPassword(pwInput, pwError); + updateButtonState(inputs, loginButton); +} + +/* 페이지 이동 */ +goToPage(inputs, loginButton, '../html/items.html'); + +emailInput.addEventListener('blur', validateEmail); +emailInput.addEventListener('input', validateEmail); +pwInput.addEventListener('blur', validatePassword); +pwInput.addEventListener('input', validatePassword); diff --git a/src/js/signup.js b/src/js/signup.js new file mode 100644 index 00000000..f27c9a64 --- /dev/null +++ b/src/js/signup.js @@ -0,0 +1,72 @@ +import { + validationEmail, + validationPassword, + updateButtonState, + goToPage, + showError, + showSuccess, +} from './formvalidation.js'; +const emailInput = document.getElementById('email'); +const nicknameInput = document.getElementById('nickname'); +const pwInput = document.getElementById('password'); +const pwcheckInput = document.getElementById('passwordcheck'); + +const emailError = emailInput.nextElementSibling; +const nicknameError = nicknameInput.nextElementSibling; +const pwError = pwInput.nextElementSibling; +const pwcheckError = pwcheckInput.nextElementSibling; + +const signupButton = document.querySelector('.auth__button'); + +const inputs = [emailInput, nicknameInput, pwInput, pwcheckInput]; + +/* 이메일 검증 */ +function validateEmail() { + validationEmail(emailInput, emailError); + updateButtonState(inputs, signupButton); +} + +/* 비밀번호 검증 */ +function validatePassword() { + validationPassword(pwInput, pwError); + updateButtonState(inputs, signupButton); +} + +/* 닉네임 유효성 검사 */ +function validateNickname() { + const value = nicknameInput.value.trim(); + + if (value === '') { + showError(nicknameInput, nicknameError, '닉네임을 입력해주세요'); + } else { + showSuccess(nicknameInput, nicknameError); + } + updateButtonState(inputs, signupButton); +} + +/* 비밀번호 확인 유효성 검사 */ +function validatePasswordcheck() { + const pwValue = pwInput.value.trim(); + const pwcheckValue = pwcheckInput.value.trim(); + + if (pwcheckValue === '') { + showError(pwcheckInput, pwcheckError, '비밀번호를 다시 입력해주세요'); + } else if (!(pwValue === pwcheckValue)) { + showError(pwcheckInput, pwcheckError, '비밀번호가 일치하지 않습니다.'); + } else { + showSuccess(pwcheckInput, pwcheckError); + } + updateButtonState(inputs, signupButton); +} + +/* 페이지 이동 */ +goToPage(inputs, signupButton, '../html/login.html'); + +emailInput.addEventListener('blur', validateEmail); +emailInput.addEventListener('input', validateEmail); +nicknameInput.addEventListener('blur', validateNickname); +nicknameInput.addEventListener('input', validateNickname); +pwInput.addEventListener('blur', validatePassword); +pwInput.addEventListener('input', validatePassword); +pwcheckInput.addEventListener('blur', validatePasswordcheck); +pwcheckInput.addEventListener('input', validatePasswordcheck); diff --git a/src/js/toggle.js b/src/js/toggle.js new file mode 100644 index 00000000..63e5eb53 --- /dev/null +++ b/src/js/toggle.js @@ -0,0 +1,19 @@ +const toggleButtons = document.querySelectorAll('.auth__visible'); + +for (let i = 0; i < toggleButtons.length; i++) { + const toggleBtn = toggleButtons[i]; + const wrapper = toggleBtn.parentElement; + const input = wrapper.querySelector('input'); + const icon = toggleBtn.querySelector('img'); + let isVisible = false; + + toggleBtn.addEventListener('click', () => { + isVisible = !isVisible; + + input.type = isVisible ? 'text' : 'password'; + icon.src = isVisible + ? '../assets/images/visibility_on.svg' + : '../assets/images/visibility_off.svg'; + icon.alt = isVisible ? '비밀번호 보기' : '비밀번호 숨김'; + }); +}