feat: 로그인 화면

This commit is contained in:
2026-02-26 12:29:45 +09:00
parent 53f160384d
commit dabb442048
14 changed files with 402 additions and 102 deletions

20
src/shared/lib/colors.ts Normal file
View File

@@ -0,0 +1,20 @@
/**
* VibeRoom 공통 색상 팔레트 (JS/TS 환경용)
*
* Tailwind CSS(globals.css)에 정의된 색상과 동일한 값을 가집니다.
* 이 파일은 Tailwind 클래스를 사용할 수 없는 곳(Canvas, Three.js 3D 객체, 차트 라이브러리 등)이나
* JS 로직 내에서 색상 코드가 직접 필요할 때 사용합니다.
*/
export const colors = {
brand: {
primary: '#63adf2', // 소프트 코발트 (핵심 액션, 포인트)
dark: '#304d6d', // 딥 네이비 (메인 텍스트, 무게감 있는 배경)
soft: '#a7cced', // 파스텔 스카이 (은은한 배경, 호버/체크마크)
},
base: {
background: '#f8fafc', // slate-50 (서비스 기본 배경)
white: '#ffffff',
}
} as const;
export type BrandColor = keyof typeof colors.brand;

65
src/shared/ui/Button.tsx Normal file
View File

@@ -0,0 +1,65 @@
import React, { ButtonHTMLAttributes } from 'react';
import Link, { LinkProps } from 'next/link';
// 1. 버튼 Variant(스타일) 및 Size 타입 정의
export type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'ghost';
export type ButtonSize = 'sm' | 'md' | 'lg' | 'full';
// 2. 공통 Props 인터페이스 정의 (HTML 버튼 속성 + Link 속성 혼합)
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: ButtonVariant;
size?: ButtonSize;
href?: string; // href가 주어지면 Next.js Link 컴포넌트로 렌더링
className?: string;
children: React.ReactNode;
}
/**
* VibeRoom 공통 버튼 컴포넌트
* (3-Color System 엄격 적용: #304d6d, #63adf2, #a7cced)
*/
export const Button = React.forwardRef<HTMLButtonElement | HTMLAnchorElement, ButtonProps>(
(
{ variant = 'primary', size = 'md', href, className = '', children, ...props },
ref
) => {
// 공통 베이스 스타일
const baseStyle = "inline-flex items-center justify-center font-bold rounded-xl transition-all duration-200";
// Variant별 테마 (VibeRoom 3-Color System)
const variants: Record<ButtonVariant, string> = {
primary: "bg-brand-primary text-white hover:bg-brand-primary/90 shadow-sm",
secondary: "bg-slate-50 text-brand-dark hover:bg-slate-100",
outline: "bg-white/50 text-brand-dark border border-brand-dark/20 hover:bg-white shadow-sm",
ghost: "bg-transparent text-brand-dark/80 hover:text-brand-primary",
};
// 크기별 테마
const sizes: Record<ButtonSize, string> = {
sm: "px-4 py-2 text-sm",
md: "px-5 py-2.5 text-base",
lg: "px-8 py-4 text-lg",
full: "w-full py-3 px-4 text-base",
};
const combinedClassName = `${baseStyle} ${variants[variant]} ${sizes[size]} ${className}`;
// href 속성이 있으면 Next.js Link로 렌더링
if (href) {
return (
<Link href={href} className={combinedClassName} ref={ref as React.Ref<HTMLAnchorElement>}>
{children}
</Link>
);
}
// 기본은 HTML Button
return (
<button className={combinedClassName} ref={ref as React.Ref<HTMLButtonElement>} {...props}>
{children}
</button>
);
}
);
Button.displayName = 'Button';