@x-plat/design-system 0.5.46 → 0.5.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/guidelines/AGENT_PROMPT.md +167 -0
- package/package.json +1 -1
|
@@ -300,6 +300,91 @@ DS 컴포넌트는 자체 외부 margin 없음. 부모가 gap 안 주면 모두
|
|
|
300
300
|
</div>
|
|
301
301
|
```
|
|
302
302
|
|
|
303
|
+
### 폼 요소 구조 — 중첩 금지
|
|
304
|
+
|
|
305
|
+
**Input / TextArea / Select 안에 다른 컴포넌트를 넣지 않는다.** 레이아웃이 깨진다. 폼 요소는 항상 **형제(sibling)** 로 배치한다.
|
|
306
|
+
|
|
307
|
+
#### ❌ 금지 — 폼 요소 중첩
|
|
308
|
+
|
|
309
|
+
```tsx
|
|
310
|
+
// Input 안에 Button → 잘못
|
|
311
|
+
<Input>
|
|
312
|
+
<Button>검색</Button>
|
|
313
|
+
</Input>
|
|
314
|
+
|
|
315
|
+
// TextArea 안에 Button → 잘못
|
|
316
|
+
<TextArea>
|
|
317
|
+
<Button>전송</Button>
|
|
318
|
+
</TextArea>
|
|
319
|
+
|
|
320
|
+
// Select 안에 Input → 잘못
|
|
321
|
+
<Select>
|
|
322
|
+
<Input placeholder="검색" />
|
|
323
|
+
</Select>
|
|
324
|
+
|
|
325
|
+
// form 안에 form → 절대 금지
|
|
326
|
+
<form>
|
|
327
|
+
<form>...</form>
|
|
328
|
+
</form>
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
#### ✅ 올바른 패턴
|
|
332
|
+
|
|
333
|
+
**Pattern 1: "입력 + 전송 버튼" 합성** → `ChatInput` 컴포넌트 사용
|
|
334
|
+
```tsx
|
|
335
|
+
import { ChatInput } from "@x-plat/design-system/components";
|
|
336
|
+
|
|
337
|
+
<ChatInput
|
|
338
|
+
placeholder="메시지 입력"
|
|
339
|
+
onSubmit={(value) => handleSend(value)}
|
|
340
|
+
/>
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
**Pattern 2: Input 우측 아이콘/소형 액션** → Input 의 `suffix` 슬롯 사용
|
|
344
|
+
```tsx
|
|
345
|
+
<Input
|
|
346
|
+
placeholder="검색"
|
|
347
|
+
suffix={<Icon name="search" />}
|
|
348
|
+
/>
|
|
349
|
+
```
|
|
350
|
+
> `suffix` 는 작은 아이콘/버튼 1개용. 일반 `<Button>` 컴포넌트 넣지 말 것 (사이즈 충돌).
|
|
351
|
+
|
|
352
|
+
**Pattern 3: 폼 요소 + 액션 버튼** → 형제 배치, wrapper 에서 gap
|
|
353
|
+
```tsx
|
|
354
|
+
<div style={{ display: "flex", gap: "var(--spacing-space-2)" }}>
|
|
355
|
+
<Input placeholder="이메일" />
|
|
356
|
+
<Button type="primary">확인</Button>
|
|
357
|
+
</div>
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
**Pattern 4: 폼 요소들 묶음** → 모두 형제, 부모에서 gap
|
|
361
|
+
```tsx
|
|
362
|
+
<form style={{
|
|
363
|
+
display: "flex",
|
|
364
|
+
flexDirection: "column",
|
|
365
|
+
gap: "var(--spacing-space-4)",
|
|
366
|
+
}}>
|
|
367
|
+
<Input label="이름" />
|
|
368
|
+
<TextArea label="자기소개" />
|
|
369
|
+
<Select label="국가" options={...} />
|
|
370
|
+
<Button type="primary">제출</Button>
|
|
371
|
+
</form>
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
#### 폼 요소별 children/슬롯 정책
|
|
375
|
+
|
|
376
|
+
| 컴포넌트 | children 받음? | 슬롯 |
|
|
377
|
+
|---|---|---|
|
|
378
|
+
| `Input` | ❌ | `suffix` (아이콘 1개) |
|
|
379
|
+
| `TextArea` | ❌ | 없음 |
|
|
380
|
+
| `Select` | ❌ | `options` prop 사용 (children 아님) |
|
|
381
|
+
| `CheckBox` / `Radio` / `Switch` | ❌ | `label` prop |
|
|
382
|
+
| `DatePicker` / `Calendar` | ❌ | controlled value/onChange |
|
|
383
|
+
| `Button` | ✅ | 텍스트/아이콘만 |
|
|
384
|
+
| `ChatInput` | ❌ | 입력+버튼 합성 컴포넌트 |
|
|
385
|
+
|
|
386
|
+
> "입력 안에 무언가를 넣고 싶다" 는 욕구가 들면 → **다른 형태의 합성 컴포넌트가 있는지 먼저 확인**. 없으면 **wrapper + 형제** 패턴 사용.
|
|
387
|
+
|
|
303
388
|
### 케이스별 권장 spacing 값
|
|
304
389
|
|
|
305
390
|
| 케이스 | 권장 값 |
|
|
@@ -338,6 +423,88 @@ DS 컴포넌트는 자체 외부 margin 없음. 부모가 gap 안 주면 모두
|
|
|
338
423
|
|
|
339
424
|
---
|
|
340
425
|
|
|
426
|
+
## 안전 가이드 — 자주 누락되는 5가지
|
|
427
|
+
|
|
428
|
+
UI 디자인 결정(레이아웃 폭, 색상 조합, 위계 등)은 **사용자 의도를 존중** 한다. 다만 아래 5가지는 빠지면 **레이아웃이 깨지거나 DS 가 오용되는 영역**이라 반드시 적용.
|
|
429
|
+
|
|
430
|
+
### 1. 반응형 GridItem 누락 방지
|
|
431
|
+
|
|
432
|
+
`GridItem` 의 `column` 은 default(laptop) 만 주면 **모바일에서 깨진다**. tablet(≤8) / mobile(≤4) 한계를 반드시 확인.
|
|
433
|
+
|
|
434
|
+
```tsx
|
|
435
|
+
// ❌ default 만 → mobile 에서 6/4 = 1.5 칸이 되어 잘림
|
|
436
|
+
<GridItem column={{ default: 6 }}>...</GridItem>
|
|
437
|
+
|
|
438
|
+
// ✅ 브레이크별 명시
|
|
439
|
+
<GridItem column={{ default: 6, tablet: 4, mobile: 4 }}>...</GridItem>
|
|
440
|
+
|
|
441
|
+
// ✅ 전체 폭이면 명시적으로 12/8/4
|
|
442
|
+
<GridItem column={{ default: 12, tablet: 8, mobile: 4 }}>...</GridItem>
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
### 2. 컴포넌트 오용 방지 — 상황별 매칭
|
|
446
|
+
|
|
447
|
+
비슷해 보이는 컴포넌트가 여러 개일 때 잘못 고르면 UX 가 망가진다.
|
|
448
|
+
|
|
449
|
+
| 상황 | 사용할 컴포넌트 | 쓰지 말 것 |
|
|
450
|
+
|---|---|---|
|
|
451
|
+
| 짧은 알림(3-5초 후 사라짐) | `Toast` | Modal, Alert |
|
|
452
|
+
| 인라인 경고/상태 메시지 | `Alert` | Modal |
|
|
453
|
+
| 추가 설명 1-2줄 (hover) | `Tooltip` | PopOver |
|
|
454
|
+
| 풍부한 부가 정보 + 액션 | `PopOver` | Modal, Tooltip |
|
|
455
|
+
| 전용 작업 흐름 (작성/확인) | `Modal` | Drawer (전화면 시) |
|
|
456
|
+
| 사이드 패널 (긴 흐름) | `Drawer` | Modal |
|
|
457
|
+
| 단일 선택 (2-7개) | `Radio` | Select |
|
|
458
|
+
| 단일 선택 (8개 이상) | `Select` | Radio |
|
|
459
|
+
| 다중 선택 | `CheckBox` 리스트 또는 multi-`Select` | Radio |
|
|
460
|
+
| boolean on/off | `Switch` | CheckBox |
|
|
461
|
+
|
|
462
|
+
### 3. 상태 처리 3종 — Loading / Empty / Error
|
|
463
|
+
|
|
464
|
+
데이터를 가져오는 곳(API/비동기)에는 반드시 3개 상태를 모두 처리.
|
|
465
|
+
|
|
466
|
+
```tsx
|
|
467
|
+
// ✅ 3개 상태 모두 처리
|
|
468
|
+
if (loading) return <Skeleton />; // 또는 <Spinner />
|
|
469
|
+
if (error) return <Alert type="error">오류: {error.message}</Alert>;
|
|
470
|
+
if (data.length === 0) return <EmptyState title="데이터가 없습니다" />;
|
|
471
|
+
|
|
472
|
+
return <Table data={data} />;
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
데이터 무관 정적 화면이면 이 패턴 생략 가능.
|
|
476
|
+
|
|
477
|
+
### 4. 접근성 기본 4개
|
|
478
|
+
|
|
479
|
+
빠지면 시각/키보드 사용자가 못 씀.
|
|
480
|
+
|
|
481
|
+
| 요소 | 필수 처리 |
|
|
482
|
+
|---|---|
|
|
483
|
+
| 이미지 (`<img>`) | `alt` 속성 (장식이면 `alt=""`) |
|
|
484
|
+
| 아이콘 only 버튼 | `aria-label` (예: `<Button aria-label="삭제"><TrashIcon /></Button>`) |
|
|
485
|
+
| Input / TextArea / Select 등 폼 | `label` prop |
|
|
486
|
+
| Modal / Drawer 등 오버레이 | DS 가 처리하지만 닫기 버튼이 키보드로 접근 가능한지 확인 |
|
|
487
|
+
|
|
488
|
+
### 5. 콘텐츠 안전
|
|
489
|
+
|
|
490
|
+
```tsx
|
|
491
|
+
// ❌ 자리 채우기 lorem ipsum
|
|
492
|
+
<p>Lorem ipsum dolor sit amet...</p>
|
|
493
|
+
|
|
494
|
+
// ❌ 한국어 컨텍스트에 영어 placeholder
|
|
495
|
+
<Input placeholder="Enter your name" />
|
|
496
|
+
|
|
497
|
+
// ✅ 의미 있는 / 한국어 placeholder
|
|
498
|
+
<Input placeholder="이름을 입력하세요" />
|
|
499
|
+
|
|
500
|
+
// ✅ 데이터가 없을 때 명시적 처리
|
|
501
|
+
<EmptyState title="등록된 게시글이 없습니다" />
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
언어 일관성: 사용자가 한국어로 요청했으면 모든 UI 텍스트(label / placeholder / button / message)도 한국어.
|
|
505
|
+
|
|
506
|
+
---
|
|
507
|
+
|
|
341
508
|
## 컴포넌트 사용 패턴
|
|
342
509
|
|
|
343
510
|
### ✅ 올바른 사용
|