Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions src/components/DeviceFrame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@ type DeviceFrameProps = PropsWithChildren<{
className?: string;
}>;

export function DeviceFrame({ children, className = "" }: DeviceFrameProps) {
export function DeviceFrame({ children, className="" }: DeviceFrameProps) {
return (
<section className={`device-frame ${className}`}>
<section className={`flex flex-col w-[390px] min-h-[844px] overflow-hidden rounded-[44px] bg-[var(--app-bg)] [box-shadow:0_40px_100px_rgba(43,_143,_219,_0.25),_0_8px_32px_rgba(0,_0,_0,_0.08)] max-[820px]:max-w-[calc(100vw_-_28px)] ${className}`}>
<StatusBar />
<div className="device-content">{children}</div>
<div className="flex-1 overflow-hidden flex flex-col bg-[#f2f7ff]">{children}</div>
</section>
);
}

export function StatusBar() {
return (
<div className="status-bar" aria-hidden="true">
<div className="flex items-center justify-between min-h-[33px] py-2 px-6 bg-white text-[11px] font-bold min-h-8 p-[9px_20px] bg-[var(--surface)] text-[9px]" aria-hidden="true">
<strong>9:41</strong>
<span className="status-icons">
<span className="w-5 h-[3px] overflow-hidden rounded-full bg-[rgba(8,_37,_95,_0.4)] text-transparent text-[var(--muted-2)] inline-flex items-center gap-[7px]">
<Signal size={14} strokeWidth={2.6} />
<Wifi size={14} strokeWidth={2.6} />
<BatteryMedium size={15} strokeWidth={2.6} />
Expand All @@ -36,12 +36,13 @@ type SubHeaderProps = {

export function SubHeader({ title, action, backIcon, onBack }: SubHeaderProps) {
return (
<header className="sub-header">
<button className="icon-circle" onClick={onBack} type="button" aria-label="뒤로 가기">
{backIcon ? <img src={backIcon} alt="" /> : <ChevronLeft size={18} strokeWidth={2.8} />}
<header className="flex items-center gap-3 h-[57px] py-3 px-4 [border-bottom:1px_solid_var(--sky-100)] bg-white">
<button className="grid place-items-center w-8 h-8 rounded-full bg-[var(--sky-100)] text-[var(--primary-soft)] text-[22px] leading-none" onClick={onBack} type="button" aria-label="뒤로 가기">
{backIcon ? <img className="w-[17px] h-[17px]" src={backIcon} alt="" /> : <ChevronLeft size={18} strokeWidth={2.8} />}
</button>
<h1>{title}</h1>
{action ? <div className="sub-header-action">{action}</div> : null}
<h1 className="flex-1 m-0 text-sm font-black">{title}</h1>
{action ? <div className="flex items-center">{action}</div> : null}
</header>
);
}

36 changes: 22 additions & 14 deletions src/components/UI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,27 @@ type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
icon?: ReactNode;
};

const buttonVariantClasses = {
primary: "bg-[var(--primary)] text-white",
secondary: "border-[1px_solid_#dbe8f8] bg-[#f5faff] text-[var(--primary)]",
disabled: "bg-[#d2d3d4] text-white cursor-default",
ghost: "",
} as const;

export function Button({
children,
className = "",
className="",
icon,
variant = "primary",
...props
}: ButtonProps) {
return (
<button
className={`button button-${variant} ${className}`}
className={`inline-flex items-center justify-center w-full min-h-[34px] rounded-[10px] p-[0_14px] text-[13px] font-black whitespace-nowrap w-[auto] min-h-8 rounded-[14px] text-xs gap-2 min-h-13 rounded-2xl text-sm ${buttonVariantClasses[variant]} ${className}`}
type="button"
{...props}
>
{icon ? <span className="button-icon">{icon}</span> : null}
{icon ? <span className="inline-flex items-center justify-center">{icon}</span> : null}
<span>{children}</span>
</button>
);
Expand All @@ -36,18 +43,18 @@ type TextFieldProps = InputHTMLAttributes<HTMLInputElement> & {

export function TextField({ label, ...props }: TextFieldProps) {
return (
<label className="field">
<label className="grid gap-[3px] w-full text-[var(--muted)] text-[8px] font-bold gap-[7px] text-[13px]">
<span>{label}</span>
<input {...props} />
<input className="w-full min-h-8 border-[1px_solid_#dbe8f8] rounded-[10px] p-[0_13px] bg-[#f6faff] text-[var(--navy)] outline-none placeholder:text-[#c6d1e0] min-h-12 rounded-2xl text-sm" {...props} />
</label>
);
}

export function Panel({
children,
className = "",
className="",
}: PropsWithChildren<{ className?: string }>) {
return <section className={`panel ${className}`}>{children}</section>;
return <section className={`w-full border-[1px_solid_var(--sky-100)] rounded-2xl p-4 bg-white [box-shadow:0_1px_3px_rgba(0,_0,_0,_0.1)] ${className}`}>{children}</section>;
}

export function WireImage({
Expand All @@ -58,28 +65,29 @@ export function WireImage({
tall?: boolean;
}) {
return (
<div className={`wire-image ${tall ? "wire-image-tall" : ""}`}>
<Landmark className="wire-image-icon" size={20} strokeWidth={2.4} />
<span>{label}</span>
<i />
<div className={`relative h-16 overflow-hidden rounded-[10px] bg-[#c5cedc] after:[content:''] after:absolute after:[inset:auto_0_0] after:h-[40%] after:bg-[#b8d8b8] h-[82px] rounded-2xl grid place-items-center text-[rgba(8,_37,_95,_0.5)] ${tall ? "" : ""}`}>
<Landmark className="relative z-10" size={20} strokeWidth={2.4} />
<span className="absolute z-10 top-[8px] left-[8px] text-[rgba(8,_37,_95,_0.6)] text-[8px] font-bold top-[auto] bottom-[8px] text-[rgba(8,_37,_95,_0.68)]">{label}</span>
<i className="absolute left-[10px] right-[10px] top-[50%] h-[5px] rounded-full bg-[rgba(255,_255,_255,_0.45)]" />
</div>
);
}

export function SearchInput({ placeholder }: { placeholder: string }) {
return (
<label className="search-box">
<label className="flex items-center gap-2 h-8 p-[0_12px] border-[1px_solid_#dbe8f8] rounded-2xl bg-[var(--sky-50)] h-[46px] rounded-[18px] p-[0_14px] bg-white text-[var(--muted-2)] [box-shadow:0_1px_5px_rgba(8,_37,_95,_0.08)] min-h-11 mt-[-4px] border-[1px_solid_#dce8f5] rounded-[17px] p-[0_18px] text-[#9aa8bb] [box-shadow:0_3px_9px_rgba(8,_37,_95,_0.08)]">
<Search size={16} strokeWidth={2.4} />
<input placeholder={placeholder} />
<input className="w-full border-0 bg-transparent text-[var(--navy)] text-[9px] outline-none text-[13px] text-[var(--ink)]" placeholder={placeholder} />
</label>
);
}

export function LocationBadge({ children }: PropsWithChildren) {
return (
<span className="location-badge">
<span className="inline-flex items-center gap-1 w-fit rounded-full p-[5px_9px] bg-[rgba(255,_255,_255,_0.82)] text-[var(--primary)] text-[11px] font-black p-[3px_7px] text-[10px]">
<MapPin size={13} strokeWidth={2.4} />
{children}
</span>
);
}

23 changes: 12 additions & 11 deletions src/pages/Auth/LoginPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,21 @@ export default function LoginPage() {
const navigate = useNavigate();
return (
<>
<section className="login-screen">
<header className="login-brand">
<span>
<section className="flex flex-col py-6 px-5 items-center text-center min-h-[100%] p-[24px_22px_30px]">
<header className="flex items-center gap-2.5 text-[var(--primary)]">
<span className="grid w-10 h-10 place-items-center rounded-2xl bg-[var(--sky-100)]">
<MapPinned size={22} strokeWidth={2.6} />
</span>
<strong>Quespot</strong>
<strong className="text-[22px] font-black">Quespot</strong>
</header>

<div className="login-hero-card">
<img src={figmaAssets.mascot} alt="Quespot 캐릭터" />
<h1>미션으로 떠나는 여행</h1>
<p>숨은 장소를 발견하고, 인증 미션을 완료하며 보상을 모아보세요.</p>
<div className="flex flex-col items-center mt-[42px] rounded-[28px] p-[26px_22px_28px] bg-[linear-gradient(169deg,_#eaf5ff_0%,_#c8e8ff_100%)] text-center">
<img className="w-24 h-24 object-contain w-[148px] h-[148px] object-contain" src={figmaAssets.mascot} alt="Quespot 캐릭터" />
<h1 className="m-[28px_0_0] text-[var(--primary)] text-[22px] leading-[33px] m-[18px_0_10px] text-[var(--navy)] text-[25px] leading-[1.24]">미션으로 떠나는 여행</h1>
<p className="m-[0_0_18px] text-[var(--muted)] text-[9px] font-bold leading-normal max-w-[260px] m-0 text-[#5a6b84] text-sm leading-relaxed">숨은 장소를 발견하고, 인증 미션을 완료하며 보상을 모아보세요.</p>
</div>

<div className="auth-actions">
<div className="grid gap-2 w-full gap-2.5 mt-[auto]">
<Button
icon={<Chrome size={18} strokeWidth={2.4} />}
onClick={() => navigate(PATH.HOME)}
Expand All @@ -39,13 +39,14 @@ export default function LoginPage() {
</div>

<button
className="auth-text-button"
className="mt-[18px] bg-transparent text-[var(--muted)] text-[13px]"
onClick={() => navigate(PATH.SIGNUP)}
type="button"
>
계정이 없으신가요? <b>회원가입</b>
계정이 없으신가요? <b className="text-[var(--primary)] text-[var(--primary)]">회원가입</b>
</button>
</section>
</>
);
}

11 changes: 6 additions & 5 deletions src/pages/Auth/SignupCheckPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ export default function SignupCheckPage() {
const navigate = useNavigate();
return (
<>
<section className="terms-screen">
<h1>약관동의</h1>
<div className="terms-list">
<section className="flex flex-col py-6 px-5 gap-6 min-h-[100%] p-[36px_24px_30px] gap-7">
<h1 className="m-[16px_0_12px] text-[var(--navy)] text-sm leading-[21px] text-center m-[8px_0_18px] text-2xl leading-tight">약관동의</h1>
<div className="overflow-hidden border-[1px_solid_#dbe8f8] rounded-[14px] bg-white rounded-[20px]">
{rows.map((row, index) => (
<label className="check-row" key={row}>
<span className={index === 0 ? "checked" : ""}>
<label className="flex items-center gap-2 min-h-[42px] p-[10px_0] [border-bottom:1px_solid_#dbe8f8] text-[10px] text-[var(--navy)] last:[border-bottom:0] min-h-[58px] py-3 px-1 text-sm" key={row}>
<span className={index === 0 ? "border-[var(--primary)] bg-[var(--primary)] after:[content:''] after:w-[5px] after:h-[5px] after:rounded-full after:bg-white text-white after:hidden" : ""}>
{index === 0 ? (
<ShieldCheck size={10} strokeWidth={2.8} />
) : null}
Expand All @@ -37,3 +37,4 @@ export default function SignupCheckPage() {
</>
);
}

Loading
Loading