diff --git a/app/layout.tsx b/app/layout.tsx index f03968c..0fe0362 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -25,6 +25,20 @@ export const metadata: Metadata = { description: "대학교 축제에서 운명의 인연을 만나보세요!", type: "website", locale: "ko_KR", + images: [ + { + url: "/og-image.png", + width: 1200, + height: 630, + alt: "코매칭 - 대학축제 커플매칭", + }, + ], + }, + twitter: { + card: "summary_large_image", + title: "코매칭 - 대학축제 커플매칭", + description: "대학교 축제에서 운명의 인연을 만나보세요!", + images: ["/og-image.png"], }, }; @@ -47,16 +61,16 @@ export default async function RootLayout({ - {/* */} - {/* */} -
- - {children} -
- {/*
*/} - {/*
*/} + + {/* */} +
+ + {children} +
+ {/*
*/} +
); diff --git a/app/register/_components/EmailStep.tsx b/app/register/_components/EmailStep.tsx new file mode 100644 index 0000000..6459eaf --- /dev/null +++ b/app/register/_components/EmailStep.tsx @@ -0,0 +1,85 @@ +import Button from "@/components/ui/Button"; +import FormInput from "@/components/ui/FormInput"; +import { validateEmail } from "@/lib/validators"; +import React, { useState } from "react"; + +type EmailStepProps = { + email: string; + onEmailChange: (email: string) => void; + onSubmit: (e: React.SyntheticEvent) => void; +}; + +export const EmailStep = ({ + email, + onEmailChange, + onSubmit, +}: EmailStepProps) => { + const [emailError, setEmailError] = useState(false); + + const handleEmailChange = (value: string) => { + onEmailChange(value); + if (emailError && value) { + setEmailError(!validateEmail(value)); + } + }; + + const handleSubmit = (e: React.SyntheticEvent) => { + e.preventDefault(); + + if (!validateEmail(email)) { + setEmailError(true); + return; + } + + setEmailError(false); + onSubmit(e); + }; + + return ( +
+
+ + STEP 1 / 2 + +

+ 이메일을 입력해주세요 +

+

+ 이메일은 로그인 시 아이디로 사용됩니다. +

+
+ +
+
+ + handleEmailChange(e.target.value)} + error={emailError} + /> + {emailError && ( + + *이메일 형식이 올바르지 않습니다. + + )} +
+ +
+ +
+
+
+ ); +}; diff --git a/app/register/_components/PasswordStep.tsx b/app/register/_components/PasswordStep.tsx new file mode 100644 index 0000000..e94545b --- /dev/null +++ b/app/register/_components/PasswordStep.tsx @@ -0,0 +1,105 @@ +import Button from "@/components/ui/Button"; +import FormInput from "@/components/ui/FormInput"; +import { + validatePasswordLength, + validatePasswordPattern, +} from "@/lib/validators"; +import { Check, Eye, EyeOff, X } from "lucide-react"; +import React, { useState } from "react"; + +type PasswordStepProps = { + password: string; + onPasswordChange: (password: string) => void; + onSubmit: (e: React.SyntheticEvent) => void; +}; + +export const PasswordStep = ({ + password, + onPasswordChange, + onSubmit, +}: PasswordStepProps) => { + const [showPassword, setShowPassword] = useState(false); + + const isLengthValid = validatePasswordLength(password); + const isPatternValid = validatePasswordPattern(password); + + return ( +
+
+ + STEP 2 / 2 + +

+ 비밀번호를 설정해주세요 +

+

+ 안전한 비밀번호로 계정을 보호하세요 +

+
+ +
+
+ + onPasswordChange(e.target.value)} + className={password ? "pr-20" : "pr-10"} + /> +
+ {password && ( + + )} + {password && ( + + )} +
+
+
+ + + 8~20자 이내 + + + + 영문 대소문자, 숫자 포함 + +
+ +
+ +
+
+
+ ); +}; diff --git a/app/register/_components/ScreenRegister.tsx b/app/register/_components/ScreenRegister.tsx new file mode 100644 index 0000000..8566598 --- /dev/null +++ b/app/register/_components/ScreenRegister.tsx @@ -0,0 +1,96 @@ +"use client"; +import { BackButton } from "@/components/ui/BackButton"; +import { useSendEmail } from "@/hooks/useSendEmail"; +import { useSignUp } from "@/hooks/useSignUp"; +import { useVerifyEmail } from "@/hooks/useVerifyEmail"; +import React, { useState } from "react"; +import { EmailStep } from "./EmailStep"; +import { VerificationStep } from "./VerificationStep"; +import { PasswordStep } from "./PasswordStep"; + +export const ScreenRegister = () => { + const [step, setStep] = useState<1 | 2 | 3>(1); + const [email, setEmail] = useState(""); + const [verificationCode, setVerificationCode] = useState(""); + const [password, setPassword] = useState(""); + + const { mutate: sendEmail, isPending: isSendingEmail } = useSendEmail(); + const { mutate: verifyEmail, isPending: isVerifyingEmail } = useVerifyEmail(); + const { mutate: signUp, isPending: isSigningUp } = useSignUp(); + + const handleEmailSubmit = (e: React.SyntheticEvent) => { + e.preventDefault(); + sendEmail(email, { + onSuccess: () => setStep(2), + }); + }; + + const handleVerify = (code: string, onError: () => void) => { + verifyEmail( + { email, code }, + { + onSuccess: (data) => { + if (data.status === 200) { + setStep(3); + } else { + onError(); + } + }, + onError, + }, + ); + }; + + const handlePasswordSubmit = (e: React.SyntheticEvent) => { + e.preventDefault(); + signUp( + { email, password }, + { + onSuccess: (data) => { + if (data.status === 200) { + // TODO: 회원가입 완료 후 이동 (예: router.push("/login")) + } else { + alert("회원가입에 실패했습니다. 다시 시도해주세요."); + } + }, + onError: () => alert("회원가입에 실패했습니다. 다시 시도해주세요."), + }, + ); + }; + + const handleResendCode = () => { + sendEmail(email, { + onError: () => alert("재전송에 실패했습니다. 다시 시도해주세요."), + }); + }; + + return ( +
+ + {step === 1 && ( + + )} + {step === 2 && ( + + )} + {step === 3 && ( + + )} +
+ ); +}; diff --git a/app/register/_components/VerificationStep.tsx b/app/register/_components/VerificationStep.tsx new file mode 100644 index 0000000..89cda7a --- /dev/null +++ b/app/register/_components/VerificationStep.tsx @@ -0,0 +1,138 @@ +import Button from "@/components/ui/Button"; +import FormInput from "@/components/ui/FormInput"; +import React, { useState, useEffect } from "react"; + +type VerificationStepProps = { + email: string; + verificationCode: string; + onVerificationCodeChange: (code: string) => void; + onVerify: (code: string, onError: () => void) => void; + onResend: () => void; + isVerifying?: boolean; +}; + +export const VerificationStep = ({ + email, + verificationCode, + onVerificationCodeChange, + onVerify, + onResend, + isVerifying = false, +}: VerificationStepProps) => { + const [timeLeft, setTimeLeft] = useState(300); // 5분 = 300초 + const [isWrong, setIsWrong] = useState(false); + + useEffect(() => { + if (timeLeft <= 0) return; + + const timer = setInterval(() => { + setTimeLeft((prev) => prev - 1); + }, 1000); + + return () => clearInterval(timer); + }, [timeLeft]); + + const formatTime = (seconds: number) => { + const mins = Math.floor(seconds / 60); + const secs = seconds % 60; + return `${mins.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`; + }; + + const handleVerificationCodeChange = (value: string) => { + onVerificationCodeChange(value); + if (isWrong && value) { + setIsWrong(false); + } + }; + + const handleSubmit = (e: React.SyntheticEvent) => { + e.preventDefault(); + onVerify(verificationCode, () => setIsWrong(true)); + }; + + const handleResend = () => { + setTimeLeft(300); // 타이머 리셋 + setIsWrong(false); // 에러 상태 초기화 + onResend(); + }; + + const maskEmail = (email: string) => { + const [local, domain] = email.split("@"); + if (!local || !domain) return email; + + const maskedLocal = local[0] + "*".repeat(local.length - 1); + + return `${maskedLocal}@${domain}`; + }; + + return ( +
+
+ + STEP 1 / 2 + +

+ 인증번호를 입력해주세요 +

+

+ {maskEmail(email)}로 인증번호를 보냈어요 +

+
+ +
+
+ +
+ handleVerificationCodeChange(e.target.value)} + error={isWrong} + maxLength={6} + inputMode="numeric" + className="flex-1" + /> + +
+ {isWrong && ( + + *인증번호를 다시 확인해 주세요 + + )} +
+
+
+ + 인증번호가 오지 않았나요? +
이전 단계에서 이메일을 재확인하거나, 스팸함을 확인해주세요. +
+
+ + +
+
+
+ ); +}; diff --git a/app/register/page.tsx b/app/register/page.tsx new file mode 100644 index 0000000..9f3067a --- /dev/null +++ b/app/register/page.tsx @@ -0,0 +1,5 @@ +import { ScreenRegister } from "./_components/ScreenRegister"; + +export default function RegisterPage() { + return ; +} diff --git a/app/tokens.css b/app/tokens.css index fa402ed..0766459 100644 --- a/app/tokens.css +++ b/app/tokens.css @@ -1,6 +1,6 @@ /* Auto-generated from token.json */ /* Run: pnpm run token to regenerate */ -/* Total unique tokens: 144 */ +/* Total unique tokens: 160 */ @layer base { :root { @@ -54,11 +54,14 @@ --color-flame-900: #ed1134; --color-gray-0-a30: #ffffff4d; --color-gray-0: #ffffff; + --color-gray-100-a30: #b3b3b34d; --color-gray-100: #e5e5e5; --color-gray-200: #cccccc; --color-gray-300-a40: #b3b3b366; --color-gray-300: #b3b3b3; --color-gray-400: #999999; + --color-gray-50-a10: #f5f5f51a; + --color-gray-50-a80: #f5f5f5cc; --color-gray-500: #808080; --color-gray-50: #f5f5f5; --color-gray-600: #666666; @@ -66,6 +69,8 @@ --color-gray-700: #4d4d4d; --color-gray-800: #333333; --color-gray-900: #1a1a1a; + --color-icon-background: var(--color-gray-100); + --color-icon-stroke-400: var(--color-gray-400); --color-orange-100: #ffc7b9; --color-orange-300: #ffa188; --color-orange-500: #ff8a6f; @@ -78,6 +83,8 @@ --color-pink-50: #fdeaf5; --color-pink-700: #f57db2; --color-pink-900: #e3468b; + --color-stroke-300: var(--color-gray-300); + --color-stroke-error: var(--color-flame-500); --color-surface-base: var(--color-gray-64); --color-text-black: var(--color-gray-900); --color-text-caption1: var(--color-gray-600); @@ -111,6 +118,15 @@ --headingregular: Regular; --high: 90%; --increased: 150%; + --inputfield-close-fill: var(--color-icon-background); + --inputfield-close-icon: var(--color-icon-stroke-400); + --inputfield-close-stroke: var(--color-gray-0-a30); + --inputfield-icon-stroke: var(--color-stroke-300); + --inputfield-stroke-default: var(--color-stroke-300); + --inputfield-stroke-error: var(--color-stroke-error); + --inputfield-text-hint: var(--color-text-caption3); + --inputfield-text-indicator: var(--color-brand-primary-flame); + --inputfield-text-primary: var(--color-text-black); --lg: var(--lg); --logo-text-black: var(--color-brand-black); --logo-text-pink: var(--color-brand-primary-flame); @@ -177,6 +193,30 @@ line-height: 1.5; } + .typo-11-400 { + font-size: 11px; + font-weight: 400; + line-height: 1.5; + } + + .typo-11-500 { + font-size: 11px; + font-weight: 500; + line-height: 1.5; + } + + .typo-11-600 { + font-size: 11px; + font-weight: 600; + line-height: 1.5; + } + + .typo-11-700 { + font-size: 11px; + font-weight: 700; + line-height: 1.5; + } + .typo-12-400 { font-size: 12px; font-weight: 400; @@ -201,6 +241,30 @@ line-height: 1.5; } + .typo-13-400 { + font-size: 13px; + font-weight: 400; + line-height: 1.5; + } + + .typo-13-500 { + font-size: 13px; + font-weight: 500; + line-height: 1.5; + } + + .typo-13-600 { + font-size: 13px; + font-weight: 600; + line-height: 1.5; + } + + .typo-13-700 { + font-size: 13px; + font-weight: 700; + line-height: 1.5; + } + .typo-14-400 { font-size: 14px; font-weight: 400; @@ -225,6 +289,30 @@ line-height: 1.5; } + .typo-15-400 { + font-size: 15px; + font-weight: 400; + line-height: 1.5; + } + + .typo-15-500 { + font-size: 15px; + font-weight: 500; + line-height: 1.5; + } + + .typo-15-600 { + font-size: 15px; + font-weight: 600; + line-height: 1.5; + } + + .typo-15-700 { + font-size: 15px; + font-weight: 700; + line-height: 1.5; + } + .typo-16-400 { font-size: 16px; font-weight: 400; @@ -249,6 +337,30 @@ line-height: 1.5; } + .typo-17-400 { + font-size: 17px; + font-weight: 400; + line-height: 1.5; + } + + .typo-17-500 { + font-size: 17px; + font-weight: 500; + line-height: 1.5; + } + + .typo-17-600 { + font-size: 17px; + font-weight: 600; + line-height: 1.5; + } + + .typo-17-700 { + font-size: 17px; + font-weight: 700; + line-height: 1.5; + } + .typo-18-400 { font-size: 18px; font-weight: 400; @@ -273,6 +385,30 @@ line-height: 1.5; } + .typo-19-400 { + font-size: 19px; + font-weight: 400; + line-height: 1.5; + } + + .typo-19-500 { + font-size: 19px; + font-weight: 500; + line-height: 1.5; + } + + .typo-19-600 { + font-size: 19px; + font-weight: 600; + line-height: 1.5; + } + + .typo-19-700 { + font-size: 19px; + font-weight: 700; + line-height: 1.5; + } + .typo-20-400 { font-size: 20px; font-weight: 400; @@ -297,6 +433,30 @@ line-height: 1.5; } + .typo-21-400 { + font-size: 21px; + font-weight: 400; + line-height: 1.5; + } + + .typo-21-500 { + font-size: 21px; + font-weight: 500; + line-height: 1.5; + } + + .typo-21-600 { + font-size: 21px; + font-weight: 600; + line-height: 1.5; + } + + .typo-21-700 { + font-size: 21px; + font-weight: 700; + line-height: 1.5; + } + .typo-22-400 { font-size: 22px; font-weight: 400; @@ -321,6 +481,30 @@ line-height: 1.5; } + .typo-23-400 { + font-size: 23px; + font-weight: 400; + line-height: 1.5; + } + + .typo-23-500 { + font-size: 23px; + font-weight: 500; + line-height: 1.5; + } + + .typo-23-600 { + font-size: 23px; + font-weight: 600; + line-height: 1.5; + } + + .typo-23-700 { + font-size: 23px; + font-weight: 700; + line-height: 1.5; + } + .typo-24-400 { font-size: 24px; font-weight: 400; @@ -345,6 +529,30 @@ line-height: 1.5; } + .typo-25-400 { + font-size: 25px; + font-weight: 400; + line-height: 1.5; + } + + .typo-25-500 { + font-size: 25px; + font-weight: 500; + line-height: 1.5; + } + + .typo-25-600 { + font-size: 25px; + font-weight: 600; + line-height: 1.5; + } + + .typo-25-700 { + font-size: 25px; + font-weight: 700; + line-height: 1.5; + } + .typo-26-400 { font-size: 26px; font-weight: 400; @@ -369,6 +577,30 @@ line-height: 1.5; } + .typo-27-400 { + font-size: 27px; + font-weight: 400; + line-height: 1.5; + } + + .typo-27-500 { + font-size: 27px; + font-weight: 500; + line-height: 1.5; + } + + .typo-27-600 { + font-size: 27px; + font-weight: 600; + line-height: 1.5; + } + + .typo-27-700 { + font-size: 27px; + font-weight: 700; + line-height: 1.5; + } + .typo-28-400 { font-size: 28px; font-weight: 400; @@ -393,6 +625,30 @@ line-height: 1.5; } + .typo-29-400 { + font-size: 29px; + font-weight: 400; + line-height: 1.5; + } + + .typo-29-500 { + font-size: 29px; + font-weight: 500; + line-height: 1.5; + } + + .typo-29-600 { + font-size: 29px; + font-weight: 600; + line-height: 1.5; + } + + .typo-29-700 { + font-size: 29px; + font-weight: 700; + line-height: 1.5; + } + .typo-30-400 { font-size: 30px; font-weight: 400; @@ -444,17 +700,22 @@ .bg-color-gray-0 { background-color: var(--color-gray-0); } .bg-color-gray-0-a30 { background-color: var(--color-gray-0-a30); } .bg-color-gray-100 { background-color: var(--color-gray-100); } + .bg-color-gray-100-a30 { background-color: var(--color-gray-100-a30); } .bg-color-gray-200 { background-color: var(--color-gray-200); } .bg-color-gray-300 { background-color: var(--color-gray-300); } .bg-color-gray-300-a40 { background-color: var(--color-gray-300-a40); } .bg-color-gray-400 { background-color: var(--color-gray-400); } .bg-color-gray-50 { background-color: var(--color-gray-50); } + .bg-color-gray-50-a10 { background-color: var(--color-gray-50-a10); } + .bg-color-gray-50-a80 { background-color: var(--color-gray-50-a80); } .bg-color-gray-500 { background-color: var(--color-gray-500); } .bg-color-gray-600 { background-color: var(--color-gray-600); } .bg-color-gray-64 { background-color: var(--color-gray-64); } .bg-color-gray-700 { background-color: var(--color-gray-700); } .bg-color-gray-800 { background-color: var(--color-gray-800); } .bg-color-gray-900 { background-color: var(--color-gray-900); } + .bg-color-icon-background { background-color: var(--color-icon-background); } + .bg-color-icon-stroke-400 { background-color: var(--color-icon-stroke-400); } .bg-color-orange-100 { background-color: var(--color-orange-100); } .bg-color-orange-300 { background-color: var(--color-orange-300); } .bg-color-orange-50 { background-color: var(--color-orange-50); } @@ -467,6 +728,8 @@ .bg-color-pink-500 { background-color: var(--color-pink-500); } .bg-color-pink-700 { background-color: var(--color-pink-700); } .bg-color-pink-900 { background-color: var(--color-pink-900); } + .bg-color-stroke-300 { background-color: var(--color-stroke-300); } + .bg-color-stroke-error { background-color: var(--color-stroke-error); } .bg-color-surface-base { background-color: var(--color-surface-base); } .bg-color-text-black { background-color: var(--color-text-black); } .bg-color-text-caption1 { background-color: var(--color-text-caption1); } @@ -501,17 +764,22 @@ .text-color-gray-0 { color: var(--color-gray-0); } .text-color-gray-0-a30 { color: var(--color-gray-0-a30); } .text-color-gray-100 { color: var(--color-gray-100); } + .text-color-gray-100-a30 { color: var(--color-gray-100-a30); } .text-color-gray-200 { color: var(--color-gray-200); } .text-color-gray-300 { color: var(--color-gray-300); } .text-color-gray-300-a40 { color: var(--color-gray-300-a40); } .text-color-gray-400 { color: var(--color-gray-400); } .text-color-gray-50 { color: var(--color-gray-50); } + .text-color-gray-50-a10 { color: var(--color-gray-50-a10); } + .text-color-gray-50-a80 { color: var(--color-gray-50-a80); } .text-color-gray-500 { color: var(--color-gray-500); } .text-color-gray-600 { color: var(--color-gray-600); } .text-color-gray-64 { color: var(--color-gray-64); } .text-color-gray-700 { color: var(--color-gray-700); } .text-color-gray-800 { color: var(--color-gray-800); } .text-color-gray-900 { color: var(--color-gray-900); } + .text-color-icon-background { color: var(--color-icon-background); } + .text-color-icon-stroke-400 { color: var(--color-icon-stroke-400); } .text-color-orange-100 { color: var(--color-orange-100); } .text-color-orange-300 { color: var(--color-orange-300); } .text-color-orange-50 { color: var(--color-orange-50); } @@ -524,6 +792,8 @@ .text-color-pink-500 { color: var(--color-pink-500); } .text-color-pink-700 { color: var(--color-pink-700); } .text-color-pink-900 { color: var(--color-pink-900); } + .text-color-stroke-300 { color: var(--color-stroke-300); } + .text-color-stroke-error { color: var(--color-stroke-error); } .text-color-surface-base { color: var(--color-surface-base); } .text-color-text-black { color: var(--color-text-black); } .text-color-text-caption1 { color: var(--color-text-caption1); } @@ -534,13 +804,71 @@ .text-color-text-white { color: var(--color-text-white); } .text-color-toggle-default { color: var(--color-toggle-default); } .text-footo-text-main { color: var(--footo-text-main); } + .text-inputfield-text-hint { color: var(--inputfield-text-hint); } + .text-inputfield-text-indicator { color: var(--inputfield-text-indicator); } + .text-inputfield-text-primary { color: var(--inputfield-text-primary); } .text-logo-text-black { color: var(--logo-text-black); } .text-logo-text-pink { color: var(--logo-text-pink); } .text-logo-text-white { color: var(--logo-text-white); } /* Border utilities */ .border-button-border-color { border-color: var(--button-border-color); } + .border-color-background-disabled { border-color: var(--color-background-disabled); } + .border-color-background-white { border-color: var(--color-background-white); } .border-color-border-light { border-color: var(--color-border-light); } + .border-color-brand-black { border-color: var(--color-brand-black); } + .border-color-brand-primary-flame { border-color: var(--color-brand-primary-flame); } + .border-color-brand-primary-orange { border-color: var(--color-brand-primary-orange); } + .border-color-brand-primary-pink { border-color: var(--color-brand-primary-pink); } + .border-color-brand-secondary-pink { border-color: var(--color-brand-secondary-pink); } + .border-color-flame-100 { border-color: var(--color-flame-100); } + .border-color-flame-300 { border-color: var(--color-flame-300); } + .border-color-flame-50 { border-color: var(--color-flame-50); } + .border-color-flame-500 { border-color: var(--color-flame-500); } + .border-color-flame-700 { border-color: var(--color-flame-700); } + .border-color-flame-900 { border-color: var(--color-flame-900); } + .border-color-gray-0 { border-color: var(--color-gray-0); } + .border-color-gray-0-a30 { border-color: var(--color-gray-0-a30); } + .border-color-gray-100 { border-color: var(--color-gray-100); } + .border-color-gray-100-a30 { border-color: var(--color-gray-100-a30); } + .border-color-gray-200 { border-color: var(--color-gray-200); } + .border-color-gray-300 { border-color: var(--color-gray-300); } + .border-color-gray-300-a40 { border-color: var(--color-gray-300-a40); } + .border-color-gray-400 { border-color: var(--color-gray-400); } + .border-color-gray-50 { border-color: var(--color-gray-50); } + .border-color-gray-50-a10 { border-color: var(--color-gray-50-a10); } + .border-color-gray-50-a80 { border-color: var(--color-gray-50-a80); } + .border-color-gray-500 { border-color: var(--color-gray-500); } + .border-color-gray-600 { border-color: var(--color-gray-600); } + .border-color-gray-64 { border-color: var(--color-gray-64); } + .border-color-gray-700 { border-color: var(--color-gray-700); } + .border-color-gray-800 { border-color: var(--color-gray-800); } + .border-color-gray-900 { border-color: var(--color-gray-900); } + .border-color-icon-background { border-color: var(--color-icon-background); } + .border-color-icon-stroke-400 { border-color: var(--color-icon-stroke-400); } + .border-color-orange-100 { border-color: var(--color-orange-100); } + .border-color-orange-300 { border-color: var(--color-orange-300); } + .border-color-orange-50 { border-color: var(--color-orange-50); } + .border-color-orange-500 { border-color: var(--color-orange-500); } + .border-color-orange-700 { border-color: var(--color-orange-700); } + .border-color-orange-900 { border-color: var(--color-orange-900); } + .border-color-pink-100 { border-color: var(--color-pink-100); } + .border-color-pink-300 { border-color: var(--color-pink-300); } + .border-color-pink-50 { border-color: var(--color-pink-50); } + .border-color-pink-500 { border-color: var(--color-pink-500); } + .border-color-pink-700 { border-color: var(--color-pink-700); } + .border-color-pink-900 { border-color: var(--color-pink-900); } + .border-color-stroke-300 { border-color: var(--color-stroke-300); } + .border-color-stroke-error { border-color: var(--color-stroke-error); } + .border-color-surface-base { border-color: var(--color-surface-base); } + .border-color-text-black { border-color: var(--color-text-black); } + .border-color-text-caption1 { border-color: var(--color-text-caption1); } + .border-color-text-caption2 { border-color: var(--color-text-caption2); } + .border-color-text-caption3 { border-color: var(--color-text-caption3); } + .border-color-text-disabled { border-color: var(--color-text-disabled); } + .border-color-text-highlight { border-color: var(--color-text-highlight); } + .border-color-text-white { border-color: var(--color-text-white); } + .border-color-toggle-default { border-color: var(--color-toggle-default); } /* Custom gradient buttons */ .bg-button-primary { diff --git a/components/common/Blur.tsx b/components/common/Blur.tsx index edee4b5..5299597 100644 --- a/components/common/Blur.tsx +++ b/components/common/Blur.tsx @@ -2,7 +2,7 @@ import React from "react"; const Blur = () => { return ( -
+
{/* orange blur - right */}
{/* pink blur - left */} diff --git a/components/ui/BackButton.tsx b/components/ui/BackButton.tsx index 6ab4b30..3ff5360 100644 --- a/components/ui/BackButton.tsx +++ b/components/ui/BackButton.tsx @@ -5,20 +5,40 @@ import { ChevronLeft } from "lucide-react"; import { cn } from "@/lib/utils"; type BackButtonProps = { - variant?: "absolute" | "static"; className?: string; onClick?: () => void; + text?: string; }; export const BackButton = ({ - variant = "static", className = "", onClick, + text, }: BackButtonProps) => { const router = useRouter(); - const positionClass = variant === "absolute" ? "absolute left-4 top-2" : ""; const handleClick = onClick ?? (() => router.back()); + // With text: centered layout with absolute positioned button + if (text) { + return ( +
+ + {text} +
+ ); + } + + // Without text: simple button (use className for absolute positioning if needed) return (