Skip to content
Open
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
42 changes: 42 additions & 0 deletions frontend/docs/features/components/Button.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Button 컴포넌트

공용 버튼 컴포넌트. `React.ButtonHTMLAttributes<HTMLButtonElement>`를 extend하여 HTML 버튼의 모든 속성을 지원한다.

## Props

| Prop | Type | Default | 설명 |
| ---------- | ---------------------------- | -------- | ------------------------------------------------------------------------ |
| `width` | `string` | `'auto'` | 버튼 너비 (예: `'100%'`, `'150px'`) |
| `animated` | `boolean` | `false` | hover 시 pulse 애니메이션, active 시 scale 축소 |
| 그 외 | `React.ButtonHTMLAttributes` | — | `type`, `disabled`, `onClick`, `aria-*`, `data-*` 등 모든 HTML 버튼 속성 |

## 사용 예시

```tsx
// 기본
<Button onClick={handleClick}>저장</Button>

// 너비 지정 + submit
<Button width="100%" type="submit">로그인</Button>

// 애니메이션 + 비활성화
<Button animated disabled={isLoading} onClick={handleSave}>
저장하기
</Button>
```

## 스타일

테마 시스템(`theme.colors`, `theme.typography`)을 참조한다.

- 배경: `gray[900]` (#3A3A3A)
- 텍스트: `base.white`
- 높이: 42px, border-radius: 10px
- 폰트: `typography.paragraph.p2` (16px, weight 600)
- disabled: `gray[500]` 배경, `gray[600]` 텍스트

## 관련 코드

- `src/components/common/Button/Button.tsx` — 컴포넌트 구현
- `src/styles/theme/colors.ts` — 색상 토큰
- `src/styles/theme/typography.ts` — 타이포그래피 토큰
67 changes: 29 additions & 38 deletions frontend/src/components/common/Button/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,71 +1,62 @@
import type { ButtonHTMLAttributes } from 'react';
import styled, { css, keyframes } from 'styled-components';

export interface ButtonProps {
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
width?: string;
children: React.ReactNode;
type?: string;
onClick?: () => void;
animated?: boolean;
disabled?: boolean;
className?: string;
}

const pulse = keyframes`
0% { transform: scale(1); background-color: #3a3a3a; }
50% { transform: scale(1.05); background-color: #505050; }
100% { transform: scale(1); background-color: #3a3a3a; }
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
`;

const StyledButton = styled.button<ButtonProps>`
background-color: #3a3a3a;
color: #ffffff;
const StyledButton = styled.button<{ $animated: boolean; $width?: string }>`
display: inline-flex;
align-items: center;
justify-content: center;
background-color: ${({ theme }) => theme.colors.gray[900]};
color: ${({ theme }) => theme.colors.base.white};
height: 42px;
border-radius: 10px;
padding: 0 16px;
border: none;
font-weight: 600;
font-size: 16px;
border-radius: 10px;
font-size: ${({ theme }) => theme.typography.paragraph.p2.size};
font-weight: ${({ theme }) => theme.typography.paragraph.p2.weight};
cursor: pointer;
transition: background-color 0.2s;
width: ${({ width }) => width || 'auto'};
width: ${({ $width }) => $width ?? 'auto'};

&:hover {
background-color: #333333;
${({ animated }) =>
animated &&
&:hover:not(:disabled) {
background-color: ${({ theme }) => theme.colors.gray[800]};
${({ $animated }) =>
$animated &&
css`
animation: ${pulse} 0.4s ease-in-out;
`}
}

&:active {
transform: ${({ animated }) => (animated ? 'scale(0.95)' : 'none')};
&:active:not(:disabled) {
transform: ${({ $animated }) => ($animated ? 'scale(0.95)' : 'none')};
}

&:disabled {
background-color: #cccccc; /* 비활성화된 느낌의 회색 */
color: #666666;
cursor: not-allowed; /* 클릭할 수 없음을 나타내는 커서 */
background-color: ${({ theme }) => theme.colors.gray[500]};
color: ${({ theme }) => theme.colors.gray[600]};
cursor: not-allowed;
opacity: 0.7;
}
`;

const Button = ({
width,
children,
onClick,
type,
animated = false,
disabled = false,
className,
type = 'button',
children,
...rest
}: ButtonProps) => (
<StyledButton
width={width}
onClick={onClick}
animated={animated}
type={type}
disabled={disabled}
className={className}
>
<StyledButton $animated={animated} $width={width} type={type} {...rest}>
{children}
</StyledButton>
);
Expand Down
Loading