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
169 changes: 154 additions & 15 deletions .agents/skills/ui/timo-figma/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,171 @@
## 트리거

- "피그마에서 컴포넌트 뽑아줘"
- "피그마 링크 줄게, 이거 구현해줘"
- 피그마 링크 또는 노드 ID를 제공받은 경우

## 참조

- `docs/design/tokens.md` → 디자인 토큰
- `docs/design/figma.md` → 피그마 MCP 연동 방식
- `docs/conventions/code-style.md` → 컴포넌트 규칙
- `docs/design/tokens.md` → 색상·타이포 토큰 목록 및 클래스명
- `docs/design/figma.md` → MCP 설정 방법
- `docs/architecture/components.md` → 컴포넌트 계층
- `docs/architecture/structure.md` → 모노레포 구조
- `docs/conventions/code-style.md` → 컴포넌트 구조·네이밍 규칙

## 워크플로우
---

### Phase 1 — 피그마 노드 읽기
## Phase 1 — 피그마 노드 읽기

피그마 MCP를 통해 대상 프레임/컴포넌트 스펙을 읽는다.
아래 기준으로 MCP 도구를 선택한다:

- 크기, 패딩, 색상, 타이포그래피, 간격
| 목적 | 도구 |
| -------------------------------------- | ---------------------- |
| 컴포넌트 크기·색상·폰트·간격 전체 읽기 | `get_design_context` |
| 레이아웃을 시각적으로 확인 | `get_screenshot` |
| 피그마 변수(토큰) 목록 확인 | `get_variable_defs` |
| 컴포넌트명으로 검색 | `search_design_system` |
| SVG·이미지 에셋 추출 | `download_assets` |

### Phase 2 — 디자인 토큰 매핑
`get_design_context`로 아래 값을 수집한다:

읽어온 값을 `docs/design/tokens.md` 의 토큰에 매핑한다.
토큰에 없는 값이 있으면 사용자에게 토큰 추가 여부 확인한다.
- **크기**: width, height
- **색상**: fill hex, text hex
- **타이포**: font-size, font-weight
- **간격**: padding, gap
- **radius**: border-radius
- **variant 목록**: 피그마 component set의 variant 이름

### Phase 3 — 컴포넌트 생성
---

`timo-component` 워크플로우를 따라 컴포넌트 생성.
피그마 스펙을 Tailwind 클래스로 변환한다.
## Phase 2 — 디자인 토큰 매핑

### Phase 4 — Story 생성
`docs/design/tokens.md`를 읽어 수집한 값을 Tailwind 클래스로 변환한다.

`timo-storybook` 워크플로우를 따라 Story 파일을 함께 생성한다.
**색상**: Color 표에서 hex를 찾아 `bg-timo-{token}` / `text-timo-{token}` 형태로 변환한다.
표에 없는 hex가 나오면 → 사용자에게 "토큰에 없는 값입니다. 추가할까요?" 확인 후 진행.

**타이포**: Typography 표에서 font-size + font-weight 조합을 찾아 `typo-{token}` 클래스로 변환한다.

**크기·간격**: Tailwind 기본 단위 **1 = 4px**. `px ÷ 4`로 수치를 구한다.
4px 단위로 떨어지지 않으면 `h-[{n}px]`, `w-[{n}px]` arbitrary value를 사용한다.

**Border Radius**:

| 피그마 값 | 클래스 |
| ---------- | --------------- |
| 4px | `rounded-[4px]` |
| 8px | `rounded-lg` |
| 12px | `rounded-xl` |
| 50% / 원형 | `rounded-full` |

---

## Phase 3 — 컴포넌트 위치 결정

피그마 컴포넌트가 어느 계층에 속하는지 판단한다.

| 판단 기준 | 위치 |
| ----------------------------------- | ---------------------------------------------------- |
| 여러 앱·패키지에서 공유하는 순수 UI | `packages/timo-design-system/src/components/{name}/` |
| timo-web 전역에서 공유하는 순수 UI | `apps/timo-web/components/` |
| 특정 도메인에서만 쓰이는 순수 UI | `apps/timo-web/app/(domain)/_components/` |
| 특정 도메인 + 상태·쿼리 결합 | `apps/timo-web/app/(domain)/_containers/` |

**순수 UI 판단**: `'use client'` 없이도 동작 가능하면 `_components`. useQuery·zustand·useState가 필요하면 `_containers`.

---

## Phase 4 — 컴포넌트 구현

### 공통 규칙 (`docs/conventions/code-style.md`)

- Arrow function + Named export: `export const {Name} = (props: {Name}Props) => {}`
- Props 타입은 `interface {Name}Props`로 정의, `any` 금지
- Tailwind 클래스만 사용, 인라인 style 금지
- 조건부 클래스는 `cn()` 유틸 사용

### variant가 있는 경우 — Record 패턴

```tsx
import { cn } from "../../lib";

export type Priority = "매우중요" | "중요" | "보통" | "낮음" | "Disable";

const PRIORITY_COLOR: Record<Priority, string> = {
매우중요: "bg-timo-red",
중요: "bg-timo-orange",
보통: "bg-timo-gray-600",
낮음: "bg-timo-black",
Disable: "bg-timo-gray-500",
};

export interface PriorityIconProps {
priority: Priority;
}

export const PriorityIcon = ({ priority = "매우중요" }: PriorityIconProps) => {
return (
<div
className={cn("size-4.5 shrink-0 rounded-full", PRIORITY_COLOR[priority])}
/>
);
};
```

### variant가 없는 경우 — 인라인 클래스

```tsx
export interface TagProps {
text: string;
}

export const Tag = ({ text = "과제" }: TagProps) => {
return (
<div className="bg-timo-gray-300 flex h-4 w-7.5 items-center justify-center rounded-[4px]">
<span className="typo-caption-r-10 text-timo-gray-800 whitespace-nowrap">
{text}
</span>
</div>
);
};
```

### design-system 컴포넌트 추가 작업

`packages/timo-design-system`에 추가하는 경우에만 아래를 추가로 수행한다:

**파일 구조**: 컴포넌트별 폴더로 분리

```
packages/timo-design-system/src/components/
{name}/
{Name}.tsx
{Name}.stories.tsx
```

**index.ts re-export 등록**:

```ts
// packages/timo-design-system/src/components/index.ts
export { {Name} } from "./{name}/{Name}";
```
Comment on lines +143 to +155

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

코드 블록 언어를 지정해 주세요.

이 fenced block은 언어가 없어서 markdownlint의 MD040 경고에 걸립니다. texttsx를 붙이면 린트와 가독성을 같이 잡을 수 있습니다.

🔧 제안 수정안
-```
+```text
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```
packages/timo-design-system/src/components/
{name}/
{Name}.tsx
{Name}.stories.tsx
```
**index.ts re-export 등록**:
```ts
// packages/timo-design-system/src/components/index.ts
export { {Name} } from "./{name}/{Name}";
```
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 143-143: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.agents/skills/ui/timo-figma/SKILL.md around lines 143 - 155, The fenced
code block in the SKILL.md section is missing a language tag and triggers
markdownlint MD040. Update the affected markdown fence to include an explicit
language such as text or tsx, and keep the same content structure around the
component path and the index.ts re-export example so the block is valid and
readable.

Source: Linters/SAST tools


---

## Phase 5 — Story 작성 (design-system 한정)

`timo-storybook` 워크플로우를 따른다. design-system 컴포넌트가 아니면 생략한다.

- variant가 있으면 각 variant마다 Story export 추가
- `argTypes`에 props Control 연동
- 위치: `{Name}.stories.tsx` (컴포넌트와 같은 폴더)

---

## 자가 검토

- [ ] 피그마 hex가 토큰 클래스로 변환되고 하드코딩되지 않았는가
- [ ] variant별 클래스가 Record로 분리되어 있는가
- [ ] `_components` vs `_containers` 구분이 올바른가
- [ ] design-system이면 `index.ts` re-export가 추가되었는가
- [ ] `any` 미사용, ESLint 오류 없음
164 changes: 148 additions & 16 deletions .agents/skills/ui/timo-storybook/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,161 @@

## 참조

- 대상 컴포넌트의 `XxxProps` 인터페이스
- 대상 컴포넌트의 Props 인터페이스
- `docs/design/storybook.md` → Story 작성 컨벤션 (패턴 A/B/C, argTypes, autodocs, play 함수)
- `docs/conventions/naming.md` → 파일 네이밍

## 워크플로우
---

### Phase 1 — 컴포넌트 분석
## Phase 1 — 컴포넌트 분석

대상 컴포넌트를 읽어 props 인터페이스와 사용 패턴 파악한다.
대상 컴포넌트를 읽어 아래를 파악한다:

### Phase 2 — Story 구성 계획
- props 인터페이스와 각 prop의 타입
- variant/열거형 prop이 있는지 (유니언 타입, Record 패턴)
- 단순 string/boolean prop인지
- 여러 항목을 목록으로 전시하는 컴포넌트인지 (Color, Typography 등 토큰 전시용)

- `Default`: 기본 상태
- 주요 props 변형마다 Story 추가 (`Primary`, `Disabled`, `WithIcon` 등)
- 인터랙티브 요소가 있으면 `play` 함수로 동작 시나리오 추가
---

### Phase 3Story 파일 생성
## Phase 2패턴 선택

- 위치: 컴포넌트와 동일 폴더 (`Xxx.stories.tsx`)
- `argTypes` 로 Controls 패널 연동
- a11y addon 설정 포함
컴포넌트 성격에 따라 아래 세 패턴 중 하나를 선택한다.

### Phase 4자가 검토
### 패턴 Avariant별 개별 Story (PriorityIcon 참조)

- [ ] 필수 props가 Controls에 노출됨
- [ ] 빈 상태·에러 상태 등 엣지케이스 Story 포함
- [ ] 컴포넌트 description 작성
variant(유니언 타입) prop이 있는 컴포넌트. variant마다 Story를 따로 export한다.

```tsx
import { PriorityIcon } from "./PriorityIcon";
import type { Meta, StoryObj } from "@storybook/react";

const meta = {
title: "Components/PriorityIcon",
component: PriorityIcon,
parameters: {
layout: "centered",
},
argTypes: {
priority: {
control: "select",
options: ["매우중요", "중요", "보통", "낮음", "Disable"],
},
},
} satisfies Meta<typeof PriorityIcon>;

export default meta;
type Story = StoryObj<typeof meta>;

export const 매우중요: Story = { args: { priority: "매우중요" } };
export const 중요: Story = { args: { priority: "중요" } };
export const 보통: Story = { args: { priority: "보통" } };
export const 낮음: Story = { args: { priority: "낮음" } };
export const Disable: Story = { args: { priority: "Disable" } };
```

### 패턴 B — Default 단일 Story (Tag 참조)

string/boolean 등 자유 입력 prop을 받는 컴포넌트. Controls에서 직접 수정한다.

```tsx
import { Tag } from "./Tag";
import type { Meta, StoryObj } from "@storybook/react";

const meta = {
title: "Components/Tag",
component: Tag,
parameters: {
layout: "centered",
},
argTypes: {
text: {
control: "text",
description: "태그에 표시될 텍스트",
},
},
} satisfies Meta<typeof Tag>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Default: Story = {
args: { text: "과제" },
};
```

### 패턴 C — render() 전시 Story (Color, Typography 참조)

토큰·목록을 전체 나열하는 전시용 컴포넌트. `component`를 meta에서 생략하고 `render()`로 직접 렌더링한다.

```tsx
import { Color } from "./Color";
import { COLOR_TOKENS } from "../../tokens/color-token";
import type { Meta, StoryObj } from "@storybook/react";

const meta = {
title: "Tokens/Color",
parameters: {
layout: "padded",
},
} satisfies Meta;

export default meta;
type Story = StoryObj<typeof meta>;

export const All: Story = {
name: "All Colors",
render: () => (
<div
style={{ fontFamily: "var(--font-family-pretendard)", maxWidth: "600px" }}
>
{COLOR_TOKENS.map((token) => (
<Color key={token.name} {...token} />
))}
</div>
),
};
```

---

## Phase 3 — 세부 규칙

### title 네이밍

| 컴포넌트 유형 | title |
| -------------------------- | --------------------- |
| 일반 UI 컴포넌트 | `"Components/{Name}"` |
| 토큰 전시 (색상·타이포 등) | `"Tokens/{Name}"` |

### layout 선택

| 상황 | layout |
| ------------------ | ------------ |
| 작은 단일 컴포넌트 | `"centered"` |
| 목록·넓은 컨텐츠 | `"padded"` |

### argTypes control 선택

| prop 타입 | control |
| --------------------- | ----------------------------- |
| 유니언 타입 (variant) | `"select"` + `options: [...]` |
| string | `"text"` |
| boolean | `"boolean"` |
| number | `"number"` |

### Story export 이름

- 패턴 A: variant 값 그대로 export (`export const 매우중요`, `export const Disable`)
- 패턴 B: `Default`
- 패턴 C: 목적을 나타내는 이름 (`All`, `Scale` 등)

---

## Phase 4 — 자가 검토

- [ ] `satisfies Meta<typeof {Component}>` 형식 사용 (패턴 C는 `satisfies Meta`)
- [ ] `type Story = StoryObj<typeof meta>` 선언
- [ ] 파일 위치: 컴포넌트와 동일 폴더 (`{Name}.stories.tsx`)
- [ ] variant 있는 컴포넌트는 모든 variant에 Story가 있는가
- [ ] Controls 패널에서 props가 조작 가능한가
2 changes: 1 addition & 1 deletion .coderabbit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ reviews:
- 컴포넌트는 반드시 Arrow function + Named export: `export const Component = () => {}`
- `default export`는 `page.tsx`, `layout.tsx` 등 Next.js 필수 파일에만 허용
- 자식 없으면 self-closing: `<Component />`
- 최상단 래퍼는 Fragment: `<>…</>`
- 불필요한 div 래핑 금지 — 컴포넌트 자체가 시각적 컨테이너(배경색·크기·border-radius 등 스타일을 정의)인 경우는 div 허용, 그 외 단순 그룹핑 목적이면 Fragment `<>…</>` 사용

### Import
- 절대 경로 import 사용 (상대 경로 `../../` 지양)
Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Claude Code / Codex 공통 진입점입니다.

- Product: Timo 클라이언트
- 도구: Claude Code, Codex
- 스택: Next.js 16 (App Router) · TypeScript · Tailwind CSS · Zustand · TanStack React Query · pnpm · Turborepo
- 스택: Next.js 16 (App Router) · TypeScript · Tailwind CSS · Zustand · TanStack React Query · pnpm · Turborepo · Storybook

---

Expand Down Expand Up @@ -67,4 +67,4 @@ Claude Code / Codex 공통 진입점입니다.
| ------------------------------------------ | -------------------- |
| 커밋·브랜치·이슈·코드 스타일·네이밍 컨벤션 | `docs/conventions/` |
| 기술 스택·컴포넌트 계층·상태 전략·스캐폴딩 | `docs/architecture/` |
| 디자인 토큰·피그마 MCP 연동 | `docs/design/` |
| 디자인 토큰·피그마 MCP 연동·스토리북 | `docs/design/` |
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ Timo is optimized for desktop use — designed for students and job seekers who
| ----------------------------------------------------- | ------------------------------------------ |
| Conventions (commit · branch · code style · naming) | [docs/conventions/](./docs/conventions/) |
| Architecture (stack · structure · components · state) | [docs/architecture/](./docs/architecture/) |
| Design (tokens · Figma) | [docs/design/](./docs/design/) |
| Design (tokens · Figma · Storybook) | [docs/design/](./docs/design/) |

## AI Collaboration

Expand Down
Loading
Loading