feat(skill): absorb external design skills, restore interview gate, add review route
- Restore the step-0 interview as a mechanical gate the skill explicitly depends on; add harness.md (per-harness question tools, limits, fallbacks) and brief-interview.md (slots, question cards, rounds). - Add 10 reference docs absorbed from external design skills (accessibility, interaction-feel, elevation, color, icons, product-copy, component-systems, critique, change-review, print-email) and extend existing references. - Add a review-only route and two hard-gate clauses (truncated content reachability, three-flashes limit). - design-gate: split tap targets into WCAG 2.5.8 and 44px contract layers, run axe-core when available, and fix false positives found on a real site (decorative alt="", stacked wordmark line count, url-only pages). - lint-skill: fail if the interview gate section or its links disappear. - Ship agents/openai.yaml and THIRD_PARTY_NOTICES.md.
This commit is contained in:
parent
79e79c120b
commit
6805fb2be7
37 changed files with 5688 additions and 128 deletions
282
packages/skill/references/component-systems.md
Normal file
282
packages/skill/references/component-systems.md
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
# component-systems — 기존 컴포넌트 라이브러리 위에서 일하기
|
||||
|
||||
기존 컴포넌트 라이브러리 위에서 일할 때 4단계 전에 읽는다. `components.json`, `@radix-ui/*`, Base UI 계열 패키지, `cva`·`tailwind-merge` 같은 컴포넌트 기반이 감지되면 여는 조건부 문서다. 감지되지 않으면 이 문서는 읽지 않는다 — designpaca의 기본 파이프라인(SVG 필터·three.js·순수 CSS)은 이 문서 없이도 완결된다.
|
||||
|
||||
## 이 문서를 읽는 법
|
||||
|
||||
| 상황 | 읽을 곳 |
|
||||
|---|---|
|
||||
| 지금 컴포넌트 기반이 있는지부터 확인해야 한다 | §0 |
|
||||
| 화면을 만들기 전에 뭘 먼저 검색해야 하는지 | §1 |
|
||||
| "이건 브랜드 디자인 시스템 작업인가?"가 헷갈린다 | §2 |
|
||||
| 토큰 이름을 라이브러리 변수로 옮겨야 한다 | §3 |
|
||||
| Dialog·Avatar·Group 같은 합성 실수를 피해야 한다 | §4 |
|
||||
| Base UI와 Radix 중 뭘 쓰는 프로젝트인지 API가 다르다 | §5 |
|
||||
| CLI로 컴포넌트를 추가·갱신해야 한다 | §6 |
|
||||
| Tailwind 프로젝트다 | §7(조건부) |
|
||||
| 설정·대시보드 같은 화면 유형의 조합을 참고하고 싶다 | §8(관찰 후보) |
|
||||
| 채팅·스트리밍 UI를 만든다 | §9(조건부) |
|
||||
| 작업 직전 마지막 확인 | §10 |
|
||||
|
||||
---
|
||||
|
||||
## 0. 언제·감지 방법
|
||||
|
||||
**하드 게이트는 없다.** 이 절은 "이 문서를 열어야 하는가"만 판정한다.
|
||||
|
||||
다음 중 하나라도 있으면 컴포넌트 기반이 있는 프로젝트다.
|
||||
|
||||
- 저장소 루트 또는 앱 루트에 `components.json`이 있다(shadcn/ui 계열의 표준 설정 파일)
|
||||
- `package.json` 의존성에 `@radix-ui/*`, Base UI 계열 패키지(`@base-ui/react`, 구 `@base-ui-components/react` 등), `cva`(class-variance-authority), `tailwind-merge`/`clsx` 중 하나 이상이 있다
|
||||
- `src/components/ui/`(또는 동등한 디렉터리)에 `button.tsx`, `dialog.tsx` 같은 생성된 프리미티브 파일이 이미 있다
|
||||
|
||||
`components.json`이 있으면 그 안의 `aliases`, `style`, `iconLibrary`, `tailwind` 필드를 먼저 읽는다. CLI가 있으면(`npx shadcn@latest info` 등) 실행해 프로젝트가 실제로 어떤 `base`(`radix` 또는 `base`)·경로 별칭·아이콘 라이브러리를 쓰는지 확정하고, 이후 모든 결정에서 그 값을 그대로 따른다. 설정을 읽지 못했는데 라이브러리 종류·경로를 추측해서 설치·수정을 진행하지 않는다 — 불확실하면 사용자에게 묻는다([brief-interview.md](brief-interview.md)) [SKILL-SHADCN].
|
||||
|
||||
---
|
||||
|
||||
## 1. 기존 컴포넌트·반복 패턴 우선
|
||||
|
||||
**프로젝트 계약.** 커스텀 마크업(스타일링한 `div`)을 쓰기 전에 같은 역할의 컴포넌트가 이미 있는지 먼저 검색한다. 콜아웃·빈 상태·로딩 placeholder·상태 배지처럼 화면마다 반복되는 UI는 손으로 다시 짜지 않고 기존 컴포넌트를 확장하거나 그대로 쓴다.
|
||||
|
||||
| 대신 손으로 짜지 않는다 | 이미 있는지 검색한다 |
|
||||
|---|---|
|
||||
| 색칠한 `div` + 아이콘 + 텍스트 | Alert/Callout류 컴포넌트 |
|
||||
| `animate-pulse` 커스텀 div | Skeleton류 컴포넌트 |
|
||||
| `rounded-full bg-*` span | Badge류 컴포넌트 |
|
||||
| `<hr>` 또는 `border-t` div | Separator류 컴포넌트 |
|
||||
| 빈 목록을 위한 임의 레이아웃 | Empty state류 컴포넌트 |
|
||||
|
||||
검색 순서는 (1) 프로젝트에 이미 설치된 컴포넌트 디렉터리, (2) 그 라이브러리의 공식 카탈로그, (3) 그래도 없을 때만 새로 합성이다. 새 컴포넌트를 만들기로 했다면 §2의 역할 분담을 먼저 확인한다 [SKILL-SHADCN].
|
||||
|
||||
---
|
||||
|
||||
## 2. 역할 분담 — 컴포넌트 기반은 브랜드 디자인 시스템이 아니다
|
||||
|
||||
**프로젝트 계약.** shadcn/ui, Base UI, Radix 같은 컴포넌트 기반은 "이 프로젝트가 어떻게 보여야 하는가"에 답하지 않는다. 이들은 조립 규칙(합성·상태·접근성 배선)을 제공할 뿐, 브랜드·타입 위계·간격 리듬·모션 언어는 여전히 designpaca가 3~4단계에서 정한 값이다.
|
||||
|
||||
역할을 나누면 다음과 같다.
|
||||
|
||||
- **designpaca가 맡는 것**: 방향 결정(1~2단계), 토큰 값(3단계: 타입 스케일·색·간격·radius·모션), 브랜드 표면(히어로·섹션 구성·카피 목소리), 검증(5단계: 접근성·성능·시각 감사)
|
||||
- **컴포넌트 라이브러리가 맡는 것**: 인터랙션 프리미티브의 조립(Dialog의 포커스 트랩, Select의 키보드 탐색, Tabs의 ARIA 배선), 상태 관리, 합성 규칙
|
||||
|
||||
두 역할이 섞이면 실패한다. 라이브러리의 기본 variant·기본 그림자·기본 radius를 그대로 두고 "이게 이 프로젝트의 디자인"이라고 부르면, `tokens.md`가 정의한 역할 토큰이 무의미해진다. 역으로 라이브러리가 이미 처리하는 포커스 트랩·키보드 탐색을 손으로 재구현하면 §4의 합성 규칙을 깨고 접근성 배선이 두 벌로 충돌한다.
|
||||
|
||||
**컴포넌트 라이브러리를 새로 설치할지 여부는 사용자 결정이다.** 브리프에 없는데 컴포넌트 기반을 새로 도입하지 않는다. 이미 있는 프로젝트에서는 그 기반을 존중하고, 그 위에 designpaca의 토큰·브랜드 표면·검증을 얹는다 [SKILL-SHADCN].
|
||||
|
||||
---
|
||||
|
||||
## 3. 토큰 매핑표 — designpaca 역할 토큰 → 라이브러리 CSS 변수
|
||||
|
||||
**프로젝트 계약.** 라이브러리가 이미 CSS 변수 기반 테마 시스템을 갖추고 있으면(shadcn/ui 계열이 대표적이다) `tokens.md` §2에서 정한 역할 토큰 값을 새 변수 체계로 중복 정의하지 말고, 라이브러리가 읽는 변수에 그대로 대입한다. 변수 이름은 라이브러리마다 다를 수 있으므로 실제 설치된 테마 파일(보통 `globals.css`)에서 확인하고, 아래는 shadcn/ui 계열에서 관찰되는 이름의 매핑 시작값이다.
|
||||
|
||||
| designpaca 역할 토큰(`tokens.md` §2) | 라이브러리 CSS 변수(관찰값) | 비고 |
|
||||
|---|---|---|
|
||||
| `--surface` | `--background` | 페이지 바탕 |
|
||||
| `--surface-raised` | `--card` (또는 `--popover`) | 카드·패널·오버레이 표면 |
|
||||
| `--ink` | `--foreground` | 본문 텍스트 |
|
||||
| `--ink-muted` | `--muted-foreground` | 보조 텍스트. 대비 4.5:1은 여전히 검증한다 |
|
||||
| `--line` | `--border` | 경계선. 폼 입력 테두리는 `--input`으로 분리되어 있을 수 있다 |
|
||||
| `--accent` | `--primary` | 강조 역할의 기본 토큰 |
|
||||
| `--accent-ink` | `--primary-foreground` | 강조 위 글자색 |
|
||||
| (역할 확장) | `--destructive` / `--destructive-foreground` | 위험·삭제 상태. designpaca 쪽 상태색 확장 자리(`tokens.md` §2)에 대응 |
|
||||
| (역할 확장) | `--ring` | 포커스 링. `accessibility.md`의 포커스 하드 게이트와 연결 |
|
||||
| `--radius-*` | `--radius` | 라이브러리는 보통 단일 `--radius`에서 `calc()`로 나머지 반경을 파생시킨다. designpaca가 3종 미만으로 제한한 radius 어휘와 정신이 같다(`tokens.md` §3-b) |
|
||||
|
||||
이 표는 라이브러리 변수명을 designpaca가 강제하는 것이 아니라, 이미 있는 값을 designpaca 쪽 역할 토큰과 대응시켜 이중 정의를 막는 목적이다. 색상 값 자체(라이트/다크 쌍)를 어떻게 정하는지는 `tokens.md` §2와 [color.md](color.md)를 따른다. `--dur-*`·`--ease-*` 모션 토큰은 라이브러리가 별도로 정의하지 않는 한 `tokens.md` §4와 [motion.md](motion.md)의 값을 그대로 쓴다 — 라이브러리 컴포넌트의 트랜지션 duration이 프로젝트 모션 토큰과 다르면 그 불일치를 기록하고 맞출지 결정한다 [SKILL-SHADCN].
|
||||
|
||||
---
|
||||
|
||||
## 4. 합성 규칙
|
||||
|
||||
**하드 게이트 하나, 나머지는 프로젝트 계약.** 컴포넌트 기반을 쓰기로 한 이상 아래는 그 라이브러리의 계약이며, 어기면 스타일이 아니라 동작이 깨진다.
|
||||
|
||||
### 4-1. Dialog·Sheet·Drawer는 접근 가능한 제목이 필수다 — 하드 게이트
|
||||
|
||||
시각적으로 숨기더라도 `sr-only` 텍스트로는 존재해야 한다. 완전 생략은 WCAG 4.1.2(Name, Role, Value) 위반이다 [WCAG-NRV, SKILL-SHADCN]. 상세 판정 기준과 검사 방법은 [accessibility.md](accessibility.md)를 따른다.
|
||||
|
||||
```tsx
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>프로필 수정</DialogTitle>
|
||||
<DialogDescription>정보를 업데이트합니다.</DialogDescription>
|
||||
</DialogHeader>
|
||||
...
|
||||
</DialogContent>
|
||||
```
|
||||
|
||||
시각적으로 제목을 숨겨야 한다면:
|
||||
|
||||
```tsx
|
||||
<DialogTitle className="sr-only">프로필 수정</DialogTitle>
|
||||
```
|
||||
|
||||
### 4-2. Avatar는 Fallback이 필수다 — 프로젝트 계약
|
||||
|
||||
이미지 로드가 실패했을 때의 대체 표시가 없으면 빈 원이 남는다.
|
||||
|
||||
```tsx
|
||||
<Avatar>
|
||||
<AvatarImage src="/avatar.png" alt="사용자" />
|
||||
<AvatarFallback>홍</AvatarFallback>
|
||||
</Avatar>
|
||||
```
|
||||
|
||||
### 4-3. Group 안에 Item — 프로젝트 계약
|
||||
|
||||
리스트형 컴포넌트는 콘텐츠 컨테이너에 Item을 직접 렌더링하지 않고 Group으로 감싼다. 감싸지 않으면 스크린리더가 그룹 경계를 읽지 못하거나 레이아웃이 깨진다.
|
||||
|
||||
| Item | Group |
|
||||
|---|---|
|
||||
| `SelectItem`, `SelectLabel` | `SelectGroup` |
|
||||
| `DropdownMenuItem`, `DropdownMenuLabel` | `DropdownMenuGroup` |
|
||||
| `CommandItem` | `CommandGroup` |
|
||||
| `MenubarItem` | `MenubarGroup` |
|
||||
|
||||
```tsx
|
||||
// 틀림 — Item이 콘텐츠 컨테이너에 직접
|
||||
<SelectContent>
|
||||
<SelectItem value="apple">사과</SelectItem>
|
||||
</SelectContent>
|
||||
|
||||
// 맞음
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem value="apple">사과</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
```
|
||||
|
||||
[SKILL-SHADCN]
|
||||
|
||||
### 4-4. 폼 필드 묶음 — 프로젝트 계약
|
||||
|
||||
라이브러리가 필드 묶음 컴포넌트(FieldGroup/Field류)를 제공하면 raw `div` + `space-y-*`/`grid gap-*`로 폼 레이아웃을 다시 짜지 않는다. 검증 상태는 컨테이너와 컨트롤에 이중으로 표시한다 — 컨테이너에는 `data-invalid`(또는 동등 속성), 실제 입력 요소에는 `aria-invalid`. 비활성 상태도 같은 패턴(`data-disabled`는 컨테이너, `disabled`는 컨트롤)이다. 스타일링과 스크린리더 판정이 같은 진실을 가리키게 하는 목적이며, 한쪽만 표시하면 시각과 보조기술 판정이 어긋난다 [SKILL-SHADCN].
|
||||
|
||||
### 4-5. 오버레이의 z-index를 손으로 조정하지 않는다 — 프로젝트 계약
|
||||
|
||||
Dialog, Sheet, Popover, DropdownMenu, Tooltip, HoverCard 같은 오버레이 컴포넌트는 자체 스태킹 컨텍스트를 관리한다. `z-50`이나 `z-[999]` 같은 임의값을 얹으면 라이브러리가 관리하는 레이어 순서와 충돌해 다른 오버레이 뒤에 가려지거나 앞으로 튀어나온다. 스태킹 순서를 바꿔야 할 실제 필요가 있으면 라이브러리가 제공하는 portal·z-index prop을 먼저 찾고, 없으면 그 사실을 design.md에 남긴다 [SKILL-SHADCN].
|
||||
|
||||
---
|
||||
|
||||
## 5. Base UI vs Radix — API 차이
|
||||
|
||||
**프로젝트 계약.** 같은 컴포넌트 이름이라도 밑바탕 프리미티브가 Base UI인지 Radix인지에 따라 prop 시그니처가 다르다. `components.json`의 `base` 필드나 `info` 출력에서 어느 쪽인지 먼저 확인하고, 프로젝트가 쓰는 쪽의 문법만 쓴다. 섞어 쓰면 조용히 동작하지 않는 코드가 나온다 [SKILL-SHADCN].
|
||||
|
||||
| 상황 | Radix | Base UI |
|
||||
|---|---|---|
|
||||
| 커스텀 트리거 엘리먼트 | `asChild` | `render` |
|
||||
| non-button 엘리먼트로 트리거를 렌더링할 때 | 해당 없음 | `nativeButton={false}` 추가 필요 |
|
||||
| Select 아이템 정의 | JSX로 인라인 | root에 `items` prop 필요 |
|
||||
| Select placeholder | `<SelectValue placeholder="...">` | `{ value: null }` 아이템으로 표현 |
|
||||
| ToggleGroup 단일 선택 | `type="single"`, `defaultValue`는 문자열 | prop 불필요(기본 단일), `defaultValue`는 항상 배열 |
|
||||
| ToggleGroup 다중 선택 | `type="multiple"` | `multiple` boolean prop |
|
||||
| Slider 단일 thumb | 항상 배열 | 단일 숫자 허용(range는 둘 다 배열) |
|
||||
| Accordion | `type="single"\|"multiple"` 필요, `collapsible` 지원, `defaultValue`는 문자열 | `type` prop 없음, `multiple` boolean, `defaultValue`는 항상 배열 |
|
||||
|
||||
트리거를 감싸는 예시:
|
||||
|
||||
```tsx
|
||||
// 틀림 — 트리거를 추가 엘리먼트로 감쌈
|
||||
<DialogTrigger>
|
||||
<div><Button>열기</Button></div>
|
||||
</DialogTrigger>
|
||||
|
||||
// 맞음 (Radix)
|
||||
<DialogTrigger asChild>
|
||||
<Button>열기</Button>
|
||||
</DialogTrigger>
|
||||
|
||||
// 맞음 (Base UI)
|
||||
<DialogTrigger render={<Button />}>열기</DialogTrigger>
|
||||
```
|
||||
|
||||
non-button 렌더 대상 예시(Base UI):
|
||||
|
||||
```tsx
|
||||
// 틀림 — nativeButton={false} 누락
|
||||
<Button render={<a href="/docs" />}>문서 보기</Button>
|
||||
|
||||
// 맞음
|
||||
<Button render={<a href="/docs" />} nativeButton={false}>
|
||||
문서 보기
|
||||
</Button>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. CLI 안전
|
||||
|
||||
**프로젝트 계약.** 컴포넌트 추가·갱신에 CLI(예: `shadcn` CLI)를 쓸 수 있으면 다음 순서를 지킨다.
|
||||
|
||||
1. **먼저 `info`로 프로젝트 설정을 확인한다.** 별칭 경로(`aliases`), Tailwind 버전, `base`(radix/base), `iconLibrary`를 읽고 그 값을 그대로 따른다. 값을 추측하거나 프로젝트 대신 기본값을 고르지 않는다.
|
||||
2. **`--dry-run`·`--diff`로 먼저 미리 본다.** 실제 적용 전에 영향받는 파일과 upstream 대비 diff를 확인한다. 로컬 변경이 없는 파일은 덮어써도 안전하지만, 로컬 변경이 있는 파일은 diff를 읽고 upstream 갱신을 로컬 수정과 함께 병합한다.
|
||||
3. **`--overwrite`는 사용자의 명시적 승인 없이 쓰지 않는다.** "그냥 다 업데이트해" 같은 포괄 지시가 와도 실행 전에 한 번 더 확인한다.
|
||||
4. **레지스트리를 추측하지 말고 묻는다.** 사용자가 "로그인 블록 추가해줘"처럼 레지스트리를 명시하지 않고 요청하면, 기본 레지스트리를 임의로 고르지 않고 어느 레지스트리를 쓸지 먼저 묻는다.
|
||||
5. **원격 파일을 손으로 fetch하지 않는다.** GitHub raw 등에서 파일을 직접 받아오지 않고 CLI의 레지스트리 해석·경로 처리·CSS diff 기능을 쓴다.
|
||||
6. **추가한 컴포넌트는 반드시 읽고 검증한다.** 빠진 하위 컴포넌트(예: `SelectGroup` 없는 `SelectItem`), 빠진 import, §4 합성 규칙 위반을 확인한 뒤 다음 단계로 넘어간다. 아이콘 import는 프로젝트의 `iconLibrary`로 맞춘다.
|
||||
|
||||
[SKILL-SHADCN]
|
||||
|
||||
---
|
||||
|
||||
## 7. Tailwind 프로젝트 요약
|
||||
|
||||
> 언제: 프로젝트가 Tailwind CSS를 쓸 때만. designpaca는 특정 CSS 프레임워크를 전제하지 않으므로, Tailwind가 아닌 프로젝트에는 이 절을 적용하지 않는다.
|
||||
|
||||
**프로젝트 계약.** 컴포넌트 라이브러리가 Tailwind 위에서 동작하도록 설계돼 있으면, 아래는 그 라이브러리와 충돌하지 않기 위한 클래스 위생 요약이다. 취향 규범이 아니라 라이브러리가 이미 semantic 토큰·유틸리티로 값을 관리하고 있어서 수동으로 덮어쓰면 어긋나는 항목들이다.
|
||||
|
||||
- 컴포넌트 색상·타이포그래피를 `className`으로 직접 오버라이드하지 않는다. 레이아웃(간격·정렬·폭)에만 쓴다
|
||||
- 수직 스택은 `space-y-*` 대신 `flex flex-col gap-*`(또는 `grid gap-*`)를 쓴다
|
||||
- 너비와 높이가 같으면 `w-10 h-10` 대신 `size-10`을 쓴다
|
||||
- 잘린 텍스트는 `overflow-hidden text-ellipsis whitespace-nowrap` 대신 `truncate` 축약을 쓴다
|
||||
- 수동 `dark:` 색상 오버라이드 대신 라이브러리의 semantic 토큰(`bg-background`, `text-muted-foreground` 등)을 쓴다 — §3 매핑표 참고
|
||||
- 조건부 클래스는 수동 템플릿 리터럴 삼항 대신 `cn()`류 유틸리티를 쓴다
|
||||
- 오버레이 컴포넌트에는 §4-5의 이유로 수동 `z-index`를 얹지 않는다
|
||||
- built-in variant(`variant="outline"` 등)를 먼저 시도하고, 그걸로 안 될 때만 `className`으로 보강한다
|
||||
|
||||
레지스트리·CLI 플래그·`registry.json` 스키마 자체는 designpaca 범위 밖이므로 다루지 않는다 [SKILL-SHADCN].
|
||||
|
||||
---
|
||||
|
||||
## 8. 화면 유형별 조합 참고
|
||||
|
||||
> 언제: 컴포넌트 기반 위에서 익숙한 화면 유형(설정, 대시보드 등)을 조립할 때. 아래는 **관찰 후보**이며 정답이 아니다.
|
||||
|
||||
**관찰 후보.** 특정 화면 유형에서 자주 관찰되는 컴포넌트 조합 예시다. 그대로 베끼면 템플릿화된 결과가 나온다는 경고와 함께 둔다.
|
||||
|
||||
| 화면 유형 | 자주 보이는 조합(예시일 뿐) |
|
||||
|---|---|
|
||||
| 설정 페이지 | Tabs 또는 사이드 내비 + Card + Form(FieldGroup/Field) |
|
||||
| 대시보드 | 고정 사이드바 + Card + 차트 컴포넌트 + 데이터 테이블 |
|
||||
| 목록 + 상세 | 목록 패널 + 인스펙터 패널(넓은 화면에서만, `layout.md` §4-f(데스크톱 어포던스) 참고) |
|
||||
|
||||
**주의**:
|
||||
|
||||
1. 이 표를 정답으로 채택하지 않는다. 브리프·업종·목표 행동에 따라 완전히 다른 조합이 맞을 수 있다
|
||||
2. `reference-method.md`의 레퍼런스 우선 원칙이 이 표보다 앞선다 — 실제 레퍼런스 조사 없이 이 표만으로 화면을 확정하지 않는다
|
||||
3. 같은 조합을 모든 설정·대시보드 화면에 기계적으로 반복하면 그 자체가 제네릭 신호다(`antipatterns.md` 참고)
|
||||
|
||||
[SKILL-SHADCN]
|
||||
|
||||
---
|
||||
|
||||
## 9. 스트리밍·채팅 UI는 라이브러리 기본 동작을 재발명하지 않는다
|
||||
|
||||
> 언제: 프로젝트의 컴포넌트 라이브러리가 채팅·스트리밍 UI 프리미티브를 이미 제공할 때.
|
||||
|
||||
**프로젝트 계약.** 스트리밍 채팅 화면에서 자동 스크롤 추적·앵커링·"최신으로 이동" 로직은 흔히 재발명되는 영역이다. 라이브러리가 이 동작을 소유하는 컴포넌트(메시지 스크롤 컨테이너류)를 이미 제공하면, `useStickToBottom`이나 수동 `ResizeObserver` 훅으로 직접 재구현하지 않는다. 합성으로 표현할 수 없는 동작만 라이브러리가 제공하는 훅으로 보강하고, 그것도 없을 때만 직접 구현한다. 메시지 행·표면(bubble)·첨부·시스템 구분선도 마찬가지로 라이브러리 프리미티브가 있으면 그것을 합성하고, flex `div`로 직접 재구성하지 않는다 [SKILL-SHADCN].
|
||||
|
||||
---
|
||||
|
||||
## 10. 체크리스트
|
||||
|
||||
- [ ] `components.json`(또는 동등 설정)과 `info` 명령 출력을 프로젝트에서 실제로 확인했다. 값을 추측하지 않았다
|
||||
- [ ] 새 UI를 손으로 짜기 전에 기존 컴포넌트·반복 패턴을 먼저 검색했다(§1)
|
||||
- [ ] 이 작업이 "라이브러리 조립"과 "designpaca 토큰·브랜드 표면·검증" 중 어디에 속하는지 구분했다(§2)
|
||||
- [ ] 새 CSS 변수를 중복 정의하지 않고 §3 매핑표로 기존 라이브러리 변수에 연결했다
|
||||
- [ ] Dialog·Sheet·Drawer에 접근 가능한 제목이 있다(§4-1, 하드 게이트)
|
||||
- [ ] Avatar·Group/Item·폼 필드 묶음·오버레이 z-index를 §4의 합성 규칙대로 처리했다
|
||||
- [ ] 프로젝트가 Base UI인지 Radix인지 확인하고 그 문법만 썼다(§5)
|
||||
- [ ] CLI로 추가·갱신했다면 `info` → `--dry-run`/`--diff` → 검증 순서를 지켰고, `--overwrite`는 승인 없이 쓰지 않았다(§6)
|
||||
- [ ] Tailwind 프로젝트라면 §7 클래스 위생을 지켰다. 아니라면 이 절은 적용하지 않았다
|
||||
- [ ] §8의 화면 유형 조합을 정답이 아니라 참고로만 썼고, 실제 레퍼런스 조사로 확인했다
|
||||
Loading…
Add table
Add a link
Reference in a new issue