@t3q-ui/design-system 1.57.0 → 1.59.0
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/CHANGELOG.md +16 -0
- package/dist/{chunk-LEHEPAQO.js → chunk-GQFI5K5T.js} +6 -5
- package/dist/chunk-GQFI5K5T.js.map +1 -0
- package/dist/{chunk-Z2SHCDPP.cjs → chunk-KT6LEYWL.cjs} +6 -5
- package/dist/chunk-KT6LEYWL.cjs.map +1 -0
- package/dist/{index-ZIfOVixA.d.cts → index-CHnIxzU2.d.cts} +3 -2
- package/dist/{index-ZIfOVixA.d.ts → index-CHnIxzU2.d.ts} +3 -2
- package/dist/index.cjs +470 -458
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +15 -3
- package/dist/index.js.map +1 -1
- package/dist/patterns/index.cjs +69 -69
- package/dist/patterns/index.cjs.map +1 -1
- package/dist/patterns/index.d.cts +2 -2
- package/dist/patterns/index.d.ts +2 -2
- package/dist/patterns/index.js +1 -1
- package/dist/patterns/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/tds/SKILL.md +9 -7
- package/skills/tds/examples/detail-page.tsx +13 -13
- package/skills/tds/examples/list-page.tsx +3 -2
- package/skills/tds/reference/components.md +7 -6
- package/dist/chunk-LEHEPAQO.js.map +0 -1
- package/dist/chunk-Z2SHCDPP.cjs.map +0 -1
package/skills/tds/SKILL.md
CHANGED
|
@@ -44,14 +44,14 @@ license: Proprietary. See LICENSE.md in the @t3q-ui/design-system package
|
|
|
44
44
|
- Pattern 본문은 이미 `<section>` landmark 다. `<main>` 으로 또 감싸지 않는다. 앱의 `<main>` 에는 `p-8`·`max-w-*`·배경을 주지 않는다 — Pattern 이 헤더 띠(화면 끝까지)와 본문 여백(24px)을 가지므로, 감싸면 헤더가 가장자리에서 32px 떠 보인다(2026-10-18 한 줄 판 run). **사이드바·상단바 같은 앱 셸을 새로 그리지 않는다** — 과제는 페이지 하나이고 Pattern 이 헤더(제목·행동)를 그린다. 셸이 꼭 필요하면 `DashboardLayout` 을 쓰고, `fixed` 사이드바로 Pattern 을 덮지 않는다(2026-10-20 17차: Codex 가 그린 사이드바가 ListPage 제목을 덮어 27점).
|
|
45
45
|
- `ListPage` 의 `onResetFilters` 는 **항상** 넘기고, 필터가 없을 때는 `filtersActive={false}` 로 보이지 않게 한다(자리는 유지). 조건부로 넘기면 빈 결과 화면에서 버튼이 튀어나와 배치가 바뀐다. 버튼은 패턴이 빠른 필터 줄 오른쪽에 그린다 — 검색창 옆에 직접 만들지 않는다.
|
|
46
46
|
- `ListPage` 에 `tableTitle`("실행환경 목록" 같은 표 제목)을 넘기지 않는다 — 헤더 제목과 중복이고 빠른 필터 옆에서 빽빽해 보인다(2026-10-18 10차). 빠른 필터 줄에는 칩과 "필터 초기화"만.
|
|
47
|
-
- **라운드는 한 계열** —
|
|
47
|
+
- **라운드는 한 계열** — 뱃지(상태·건수)와 빠른 필터 칩은 pill, 태그 4px, 버튼·입력 6px, 표·카드 8px. 컴포넌트 기본값이 이미 그렇다(`Badge` 기본 pill — 2026-11-06 사용자 결정, 홀드아웃 20 "각진 상태 뱃지 vs 둥근 필터 칩"). 뱃지에 `rounded='square'` 를, 태그·버튼에 `rounded-full`·`shape='rounded'` 를 덧씌워 계열을 섞지 않는다.
|
|
48
48
|
- 행을 눌러 보는 상세는 `ListPage detail={{ title, description, onClose, children, note, footer }}` 로 **목록 오른쪽에 나란히** 연다(표가 좁아지고 목록은 계속 보인다, 닫기 버튼·Esc). `activeRowId={selected?.id ?? null}` 로 열린 행을 표시하고, 상세 필드는 `MetaList` 안에 `MetaRow` 로 세로로 쌓는다. `Stack` 은 기본 방향이 가로라 MetaRow 를 감싸면 옆으로 늘어서 잘린다. 표를 좁히면 안 될 때만 `<Drawer modal={false}>` 옆 패널(덮는 형태)을 쓴다.
|
|
49
49
|
- **`DetailPage asideTitle` 을 주면 패널 안에 같은 제목을 또 쓰지 않는다** — 패턴이 패널 제목을 그리므로 안쪽 `MetaList`·카드에 "작업 정보" 같은 머리글을 반복하면 제목이 연달아 두 번 보인다(2026-10-27 홀드아웃 12, 심판 2건). 패널 안은 항목(`MetaRow`)부터 시작한다.
|
|
50
50
|
- **"예시 데이터로 구성된 페이지입니다" 같은 개발자 면책문·'샘플 데이터' 뱃지를 화면에 넣지 않는다** — 목 데이터임은 코드 주석에만 적는다. 심판은 이를 제품 문구의 결함으로 읽는다(2026-10-27 홀드아웃 12, 문구 차원 감점).
|
|
51
|
-
- **목록의 검색 입력은 `ColumnFilter` + `ColumnFilterInput`**(스토리와 같은 조합). 필터 칩(`TableFilter*`, 채움형) 옆에 `TextField`(외곽선)를 따로 두면 두 모양이 어긋나 감점(2026-10-26 홀드아웃 11, 심판 4건). 표 셀 정렬은 `TableCell align='right'`(`className='text-right'` 도 같은 효과), 숫자 열은 `type: 'number'`(cellClass 를 더해도 정렬이 유지된다), 금액 아래 사용량 같은 두 줄 셀은 `TwoLineCellRenderer`(직접 flex 로 두 줄을 그리면 위·아래로 벌어진다).
|
|
51
|
+
- **목록의 검색 입력은 `ColumnFilter` + `ColumnFilterInput`**(스토리와 같은 조합). 필터 칩(`TableFilter*`, 채움형) 옆에 `TextField`(외곽선)를 따로 두면 두 모양이 어긋나 감점(2026-10-26 홀드아웃 11, 심판 4건). 표 셀 정렬은 `TableCell align='right'`(`className='text-right'` 도 같은 효과), 숫자 열은 `type: 'number'`(cellClass 를 더해도 정렬이 유지된다). **검색창 placeholder 는 무엇으로 찾는지만** — "모델 이름·요청 ID 검색". "…로 검색하거나 조건을 추가하세요" 처럼 조작을 설명하지 않는다(2026-11-06 홀드아웃 20, Codex copy 3/3), 금액 아래 사용량 같은 두 줄 셀은 `TwoLineCellRenderer`(직접 flex 로 두 줄을 그리면 위·아래로 벌어진다).
|
|
52
52
|
- 정렬할 수 있는 열은 `sortable: true` 를 **명시**한다 → 정렬 전에도 아이콘이 보이고, 누르면 방향이 바뀐다. 요청문에 "정렬 가능" 이 있으면 반드시.
|
|
53
|
-
- **검토 패널(`aside`)이 있는 폼의 취소·제출 버튼은 패널 바닥** — FormPage 가 그린다(폼 끝에 따로 두지 않는다). 긴 폼 옆에서 패널이 짧게 끝나고 제출이 멀어 두 심판 모두 감점했다(2026-10-20 16차). 요약 패널에는 입력값 요약과 예상 비용·시간만.
|
|
54
|
-
- **표의 진행률 열은 고정 폭 막대 + % 글자 한 줄** — `<Progress value={p} className='w-[120px]' aria-label={`GPU 사용률 ${p}%`} />` 오른쪽에 `tabular-nums` 로 `{p}%`. **막대마다 `aria-label` 로 '무엇 몇 %' 이름을 준다** — 이름 없는 progressbar 는 행마다 axe serious(17차 파이프라인 목록 21건; DS 가 기본 이름 '46%' 를 붙이지만 뜻이 약하다). 분수(816 / 1,200)는 상세나 툴팁으로. 글자 아래 막대를 쌓으면 행마다 막대 폭이 달라져 감점(2026-10-20 홀드아웃 8 Codex 목록).
|
|
53
|
+
- **검토 패널(`aside`)이 있는 폼의 취소·제출 버튼은 패널 바닥** — FormPage 가 그린다(폼 끝에 따로 두지 않는다). 긴 폼 옆에서 패널이 짧게 끝나고 제출이 멀어 두 심판 모두 감점했다(2026-10-20 16차). 요약 패널에는 입력값 요약과 예상 비용·시간만. **패널 안은 `MetaList` 하나** — 합계·예상 비용도 같은 목록의 `MetaRow` 로 잇고, 묶음을 나누려고 두 번째 `MetaList`·`border-t`·`gap` 을 두지 않는다(행 구분선 위에 선·틈이 또 생긴다, 2026-11-06 홀드아웃 21 폼).
|
|
54
|
+
- **표의 진행률 열은 고정 폭 막대 + % 글자 한 줄** — `<Progress value={p} className='w-[120px]' aria-label={`GPU 사용률 ${p}%`} />` 오른쪽에 `tabular-nums` 로 `{p}%`. **막대마다 `aria-label` 로 '무엇 몇 %' 이름을 준다** — 이름 없는 progressbar 는 행마다 axe serious(17차 파이프라인 목록 21건; DS 가 기본 이름 '46%' 를 붙이지만 뜻이 약하다). 분수(816 / 1,200)는 상세나 툴팁으로. 글자 아래 막대를 쌓으면 행마다 막대 폭이 달라져 감점(2026-10-20 홀드아웃 8 Codex 목록). **막대 색은 값의 뜻대로** — 높을수록 좋은 비율(정상 행·통과율·완료율)은 기본(primary)이나 `success`, 낮을수록 좋은 값의 경고 구간만 `warning`·`danger`. 페이지·항목이 실패 상태여도 "정상 99%" 막대를 빨갛게 칠하지 않는다(2026-11-06 홀드아웃 20, 3 run 이 `danger`).
|
|
55
55
|
- 제출 중 상태는 앱이 `const [submitting, setSubmitting] = useState(false)` 로 갖고 `FormPage submitting={submitting}` 에 넘긴다(`onSubmit` 안에서 try/finally). FormPage 가 버튼을 잠그고 스피너를 보이지만, 상태가 코드에 드러나야 중복 제출 방지가 확인된다.
|
|
56
56
|
- 빈 결과는 패턴이 아이콘·제목·설명·"필터 초기화" 버튼 상자로 그린다. `emptyState` 에는 `{ title: '조건에 맞는 모델이 없습니다', description: '검색어나 필터를 바꿔 보세요.' }` 또는 두 문장 문자열("…없습니다. …해 보세요.")만 넘긴다. 버튼·아이콘·`<div>` 를 직접 만들어 넣지 않는다.
|
|
57
57
|
- 비활성 버튼은 `disabled` 로만 둔다. DetailPage 에는 사유 안내 한 줄(`actionsNote`)을 **쓰지 않는다**(2026-10-18 11차) — 상태 자체의 안내가 필요하면 `banner`(Alert). ListPage 상세 패널의 `detail.note` 는 그대로. `disabled` 를 빼먹고 배너만 두지 않는다.
|
|
@@ -61,7 +61,8 @@ license: Proprietary. See LICENSE.md in the @t3q-ui/design-system package
|
|
|
61
61
|
- 목록의 주 식별자 열은 ListPage 가 `onRowClick` 이 있으면 `name` 열을 자동으로 강조한다(굵게, hover 밑줄). 이름 열 field 가 `name` 이 아니면 `primaryField` 로 지정하고, 셀 렌더러로 파란 링크를 직접 만들지 않는다. 표 위 제목·건수는 `tableTitle` 과 `pagination.total` 로 보인다.
|
|
62
62
|
- **상태처럼 선택지가 3~6개인 다중 선택 필터는 `ListPage quickFilter`** 로 표 왼쪽 위에 세그먼트(ToggleGroup)로 둔다 — 드롭다운을 열지 않아도 선택지가 보이고, `count` 로 항목마다 건수를 붙이고 **`countVariant` 에 표의 상태 뱃지와 같은 tone**(`STATUS[s].tone`: `success`·`secondary`·`danger`…)을 준다 — 건수는 상태색 pill 뱃지로 그려져 칩과 표가 같은 색 언어가 된다(예제의 `statusCounts`; "전체" 는 패턴이 primary, 2026-10-18 13차). 드롭다운에 숨기지 않는다. 선택지가 많거나(7개 이상) 검색이 필요한 축만 `TableFilterSingleSelect`/`TableFilterMultiSelect` 로 `filters` 에 두고, 그때는 `title`(예: '프레임워크')을 주면 "프레임워크 · 전체" 처럼 대상이 항상 보인다. placeholder 에 "전체" 만 넣지 않는다. 일반 `Select` 를 필터로 쓰면 placeholder 에 "전체 상태" 처럼 대상 이름을 넣는다. 라벨 없이 "전체" 만 보이는 필터를 만들지 않는다.
|
|
63
63
|
- 표는 `AgGridTable` 로 만든다. `ag-grid-react` 를 직접 쓰지 않는다. 행 클릭과 키보드 Enter 는 같은 행 클릭 핸들러로 들어온다.
|
|
64
|
-
- 주요 행동 버튼(생성·등록)은 `ListPage actions` 에 둔다 — 패턴이 제목 줄 오른쪽에 놓는다(직접 필터 슬롯이나 `titleRight` 에 넣지 않는다). `PageHeader actions` 도 제목 줄 오른쪽(탭이 있어도 같다, 2026-10-19 14차).
|
|
64
|
+
- 주요 행동 버튼(생성·등록)은 `ListPage actions` 에 둔다 — 패턴이 제목 줄 오른쪽에 놓는다(직접 필터 슬롯이나 `titleRight` 에 넣지 않는다). `PageHeader actions` 도 제목 줄 오른쪽(탭이 있어도 같다, 2026-10-19 14차).
|
|
65
|
+
- **제목 옆 `titleRight` 에는 정보가 있는 것만 — 모든 패턴(ListPage·DetailPage·FormPage·DashboardLayout·PageHeader) 공통.** 목록·대시보드는 `PageHeaderRefreshButton`, 상세의 상태는 `status` 슬롯, 핵심 정보는 `meta`. **폼은 비워 둔다**(새로고침할 것이 없다) — 유형("Reservation")·상태·변경 건수 `Badge` 를 두지 않는다. 화면 분류 이름("프로젝트 자원 관리")·"새 요청" 같은 장식 `Tag`·`Badge`·글자, 보기 전환 `ToggleGroup` 도 두지 않는다 — 심판이 "버튼 규칙과 다른 작은 버튼"·"길 잃은 breadcrumb"·"unclear" 로 읽었다(2026-11-06 홀드아웃 17·19~21 폼·상세).
|
|
65
66
|
- **유형·방식 선택(소스 유형·공개 범위·입력 방식처럼 선택지 2개 이상에 제목·설명이 붙는 것)은 `RadioSectionGroup` + `RadioSection`(카드형)** 으로 — dl-ui-v3 의 관례(21곳). 배치는 그룹이 개수로 정한다: 1~3개 한 줄, **4개는 2×2, 5개 이상은 세로 쌓기**(카드 안은 제목 · 설명 가로) — `className` 으로 grid 를 직접 짜지 않는다. 설명 없는 짧은 선택(예·아니오)만 `RadioGroup`, 선택지 7개 이상·검색이 필요하면 `Select`. **복수 선택에 제목·설명이 붙으면 `CheckboxSectionGroup` + `CheckboxSection`**(알림 채널), 설명 없는 짧은 옵션만 `Checkbox`. **켜기/끄기 하나의 설정(규칙 사용, 실패 시 알림, 노트북 지원)은 `SwitchSection`**(박스 + 제목·설명 + Switch, dl-ui-v3 ToggleField 관례) — 폼 제출값이어도 Checkbox 로 만들지 않는다. 예제 form-page.tsx 의 스케줄 선택(RadioSectionGroup)과 실패 알림(SwitchSection)이 본보기(`FormLabel id` + 그룹 `aria-labelledby`).
|
|
66
67
|
- 버튼 묶음 사이 간격은 8px. PageHeader·Dialog·Drawer·Form 의 버튼 슬롯이 이미 그렇게 그린다.
|
|
67
68
|
- **상태 표시는 `Badge` 에 아이콘 + 글자** — `<Badge variant='success'><LucideIcon iconComponentName='circle-check' />완료</Badge>`. 아이콘은 Badge 의 자식으로 바로 넣고(Badge 가 16px·글자색으로 맞춘다), 크기는 기본(`size` 를 주지 않는다 — 건수 뱃지와 같은 20px). **별도 상태 컴포넌트·래퍼를 만들지 않는다**(2026-10-30 사용자 결정 — 예전의 상태 전용 래퍼는 크기·여백이 달라 태그인지 뱃지인지 모를 부품이 됐다). 상태값 → `variant`·아이콘 매핑은 상수 한 곳에. **어휘**: 작업·노트북·파이프라인·서버처럼 돌아가는 프로세스의 '실행 중' `primary` + `play`(▶), 활성·사용 중·감시 중·허용·연결됨 같은 켜진 상태 `primary` + `circle-dot`(●) — ▶ 를 켜진 상태에 붙이면 재생 버튼처럼 읽힌다(2026-10-21 18차), 완료·준비됨 `success` + `circle-check`(✓) — 실행 중에 ✓ 를 붙이면 끝난 것으로 읽힌다(2026-10-20 17차), 학습 중·배포 중처럼 끝이 있는 진행 `info` + `loader-circle`(정적 아이콘 — 스피너는 정지 화면에서 점 뭉치로 보인다), 대기 `secondary` + `clock`, 중지·비활성 `secondary` + `circle-pause`, 실패 `danger` + `circle-x`, 경고·알림 발생 `warning` + `triangle-alert`. **한 화면에 같은 variant 의 상태 둘을 두지 않는다** — '준비됨'(`success` ✓)과 '사용 중'(`primary` ●)처럼 상태마다 색이 달라야 한다(2026-10-21 19차 "two different states look the same"). 기본은 연한 색 채움(`appearance` 를 주지 않는다 — 2026-10-18 10차). **lucide 이름은 정식 이름만**(`circle-pause`·`loader-circle`·`triangle-alert`; 옛 alias `pause-circle` 같은 것은 쓰지 않는다). 같은 화면의 상태 뱃지는 **전부 아이콘이 있게** — 섞이면 심사자가 불일치로 본다. 색은 `variant` 로 정하고 `className` 으로 덧씌우지 않는다.
|
|
@@ -69,7 +70,7 @@ license: Proprietary. See LICENSE.md in the @t3q-ui/design-system package
|
|
|
69
70
|
- **숫자 열(개수·크기·비율·시간)은 `type: 'number'`** — AgGridTable 이 셀과 제목을 오른쪽 정렬하고 숫자 폭을 고정한다(셀 렌더러가 있어도 적용). 폭을 안 주면 140 으로 잡는다(ag-grid 기본 200 은 숫자 열이 비어 보인다 — 2026-10-27 홀드아웃 13 layout 지적 "wide empty area"). 숫자를 왼쪽 정렬로 두지 않는다.
|
|
70
71
|
- **상세 패널·오른쪽 정보 열의 필드는 `MetaRow align='start'`** — 값이 라벨 바로 옆에 붙는다. 기본(`end`)은 값을 행 오른쪽 끝에 두어 넓은 패널에서 라벨과 값 사이가 빈다.
|
|
71
72
|
- **진행 막대의 색은 상태와 맞춘다** — 실패한 작업은 `Progress variant='danger'`, 경고는 `warning`, 완료는 `success`. 실패한 작업에 파란 막대를 두지 않는다.
|
|
72
|
-
- **헤더 `meta` 에 쓴 값(ID·버전·소유자·수정일)은 본문 Panel 에 다시 쓰지 않는다** — 같은 값이 두 번 보여도 심사자가 중복으로 본다. 키워드·분류 태그는 `Tag variant='secondary'`(기본
|
|
73
|
+
- **헤더 `meta` 에 쓴 값(ID·버전·소유자·수정일)은 본문 Panel 에 다시 쓰지 않는다** — 같은 값이 두 번 보여도 심사자가 중복으로 본다. 키워드·분류 태그는 `Tag variant='secondary'`(기본 4px). 태그는 pill(`shape='rounded'`)로 그리지 않는다 — pill 은 뱃지와 필터 칩의 모양이다.
|
|
73
74
|
- **설명 문장은 PageHeader `description` 한 곳에만.** 같은 문장을 본문 위·카드 안에 다시 쓰지 않고, 본문에 "설명" Panel 을 따로 만들지 않는다(예제 detail-page 와 같이 — 같은 문장이 두 번 보이면 심사자가 중복으로 본다). 본문의 `<p>` 안내문은 사용자가 지금 해야 할 일이 있을 때만. 본문 문단에는 `break-keep wrap-break-word` 를 붙인다(한국어가 단어 중간에서 꺾이지 않게).
|
|
74
75
|
- **상세 개요 탭의 블록(정보·표·목록)은 전부 `Panel` 안에** — 표 블록도 `Panel title='최근 실행' titleRight={<Button variant='primaryLink'>전체 보기</Button>}` 안에 넣고, 제목(`h2`) + 표를 맨몸으로 두지 않는다(이웃이 카드인데 하나만 카드 밖이면 "sits outside a card unlike its neighbors", 2026-10-21 18차 상세 4/6 run). **카드 안의 표는 예제처럼 `Table`(가벼운 표)로** — `AgGridTable`(회색 머리줄·둥근 테두리 상자)은 목록 화면과 전용 탭에만 쓰고 카드 안에 넣지 않는다("bordered table header inside the jobs card while the other cards use plain rows", 2026-10-21 19차). **개요 탭은 요약 패널 + 작은 표** — `Panel '주요 정보'`(MetaList) 아래 `Panel '최근 배포'`(또는 최근 실행·최근 버전) Table **4열·5행까지**(이름·상태·핵심 지표 1·시각) + `titleRight` 에 '전체 보기' 링크(`Button variant='primaryLink'`)로 전용 탭. 열이 더 필요한 표는 전용 탭에서 — 패턴이 `aside` 없는 탭을 **전체 폭**으로 그린다(GitHub PR 식: Conversation 은 2단, Files changed 는 전체 폭). 열 5~7개 표를 2/3 열에 넣으면 이름·날짜가 줄바꿈해 두 심판 모두 감점(2026-10-19 15차). 패널 하나만 두면 오른쪽 열 아래가 비어 "large empty area" 감점(11차).
|
|
75
76
|
- **상세 주 행동은 생산적 행동 하나** — `primaryAction` 은 배포·실행·저장처럼 되돌릴 수 있는 것. 중지·삭제·초기화는 `secondaryActions`(회색) 또는 ⋯ 메뉴(`deleteAction`) — primary 파랑도 빨간 채움도 주지 않는다(5차 빨간 버튼·15차 파란 "실험 중지" 감점).
|
|
@@ -78,7 +79,8 @@ license: Proprietary. See LICENSE.md in the @t3q-ui/design-system package
|
|
|
78
79
|
- **상세 본문 구역은 `Panel title='…'` 안에 `MetaList`** — 구역마다 상자 하나, 상자 사이 16px. `Separator`·`Card`·맨 `<h2>` 로 나누지 않는다. 옆 패널(`aside`)·목록의 상세 패널(`detail`)·폼의 검토 패널(`aside`)은 패턴이 같은 Panel 로 그리므로 안에 상자를 또 두르지 않는다. 표(`Table`·`AgGridTable`)는 자기 상자가 있으니 Panel 로 감싸지 않는다.
|
|
79
80
|
- **폼 옆 검토·요약 패널은 `FormPage aside={…} asideTitle='검토'`** 로 — 패턴이 폼 열 옆에 세로선으로 구분해 sticky 로 따라오게 그린다(좁으면 폼 아래). FormPage 를 `<div className='flex'>` 로 감싸 옆에 `<aside>` 를 두지 않는다: 헤더가 화면 폭을 다 못 써 구분선이 패널에서 끊기고, 패널이 중간에서 잘린다. Card(그림자)로 만들지도 않는다. FormPage 는 헤더 내용과 본문을 같은 가운데 폭(패널 있으면 1152, 없으면 720)에 가둔다(2026-10-26 22차) — 바깥에서 `max-w`·`mx-auto` 로 또 감싸지 않는다.
|
|
80
81
|
- **날짜·시각 입력은 `DatePicker`·`TimePicker` 로, 폼 칸을 채우게** — 기본 폭이 250·160px 라 두 칸 줄에서 절반이 빈다(2026-10-28 홀드아웃 14, Opus·Gemini "date/time fields leave a lot of unused space"). `DatePicker classNames={{ button: 'w-full' }}`·`TimePicker classNames={{ trigger: 'w-full' }}`(두 부품의 통로 이름이 다르다). `<input type='date'>` 를 직접 쓰지 않는다.
|
|
81
|
-
- **목 데이터는 의미가 맞게, 양도 화면을 채울 만큼** — 태스크·프레임워크·이름·상태가 서로 어울리는 조합(예: `yolo-v8-detector` 는 `object-detection` + `PyTorch`), 날짜는 생성일 ≤ 수정일, 건수는 목록 길이와 일치.
|
|
82
|
+
- **목 데이터는 의미가 맞게, 양도 화면을 채울 만큼** — 태스크·프레임워크·이름·상태가 서로 어울리는 조합(예: `yolo-v8-detector` 는 `object-detection` + `PyTorch`), 날짜는 생성일 ≤ 수정일, 건수는 목록 길이와 일치. **목록 데이터는 20행 이상, 한 페이지는 10행** — 데이터가 10~12행이면 표 아래가 비어 "빈 공간" 으로 깎이고(한 줄 판 12차 6쌍, 2026-10-18), 한 페이지가 20행이면 첫 화면(1440×1000)에서 표 아래가 잘려 페이지네이션이 안 보인다(2026-11-06 홀드아웃 20 "bottom row clipped"). 10행이면 첫 화면에 표와 페이지네이션이 함께 들어간다. 심사자는 데이터가 말이 되는지도 본다.
|
|
83
|
+
- **표 열 순서는 이름(주 식별자) → 상태·결과 → 숫자 → 날짜 → 동작** — 같은 화면에 표가 둘 이상이면 같은 순서로 둔다. 표마다 결과 열 자리가 다르면 "result column in different places" 로 깎인다(2026-11-06 홀드아웃 20 상세, Opus).
|
|
82
84
|
- **표 열 폭** — 이름 열 `flex: 2, minWidth: 200, maxWidth: 480`, **글자가 들어가는 열은 모두** `flex: 1, minWidth: 120`(소유자·팀·프레임워크·설명 — 하나만 고르지 않는다. 남는 폭이 여러 열로 흩어져야 이름 열만 넓어지지 않는다; 글자 열이 하나뿐이면 2:1 로 나눠도 이름 열이 500px 라 "name column leaves substantial whitespace", 2026-10-20 17차·18차), 숫자·날짜·상태·진행률 열은 `width` 로 — **숫자 열은 `type: 'number'` 만 주고 `flex` 를 주지 않는다**(폭 없으면 140). DS `Table` 의 숫자 열은 `TableHead align='right'` 로 — 폭 140 으로 고정돼 글자 열이 남는 폭을 가진다(표가 `table-fixed` 라 머리글의 `align` 이 열 폭을 정한다). **DS `Table` 의 상태 열은 `TableHead className='w-32'`, 날짜·시각 열은 `className='w-44'` 로 폭을 준다** — 폭 없는 열은 남는 폭을 똑같이 나눠 가져 뱃지 하나뿐인 상태 열이 400px 가 된다(2026-10-30 홀드아웃 17 "large empty gap between the status and GPU columns"). 남는 폭은 이름·글자 열이 나눈다. 글자 열이 하나도 없으면 `maxWidth` 를 뺀다(표 오른쪽이 빈다 — 상한 없는 flex 열이 없으면 AgGridTable 이 flex 열의 `maxWidth` 를 떼어 표를 채운다, 2026-10-28 홀드아웃 14 "row dividers stop around 1330px")(제목 글자가 들어가는 최소 폭은 AgGridTable 이 보정하고 넘치는 셀 글자는 말줄임표로 끝난다). 열은 6~7개 이내로 — **보조 정보(id·이미지·경로·설명)는 열을 늘리지 말고 `ListPage secondaryField='image'` 로 이름 아래 12px 줄에**(dl-ui-v3 목록의 이름 / 설명 셀; 패턴이 행을 56 으로 그린다). 셀마다 `cellStyle`·`width: 110` 같은 좁은 폭을 손으로 맞추지 않는다(2026-10-18 13차). **주 식별자가 짧은 ID·코드(요청 ID·커밋 해시·키)면 이름 열 공식(`flex: 2`) 대신 `width: 200` 으로 고정**하고, 남는 폭은 엔드포인트·모델·설명 같은 글자 열(`flex: 1`)이 나눈다 — ID 열이 남는 폭을 다 받으면 "request ID column is too wide"(2026-10-28 홀드아웃 14, 목록 3/3).
|
|
83
85
|
- **도움말 문구(`FormDescription`)는 규칙·전제·추천값** — "실행 경로에 쓰여 만든 뒤 바꿀 수 없습니다. 영문 소문자·숫자·하이픈 3~40자", "1~32 코어. 전처리는 2~4, 학습은 8 이상을 권합니다". placeholder 와 같은 말("1~64" / "1~64")을 되풀이하지 않고, placeholder 는 "예: 4" 같은 보기 하나. **여러 필드에 같은 안내는 필드마다 되풀이하지 않는다** — 섹션 설명(또는 `Alert`)에 한 번 쓰고, 필드 도움말은 그 필드만의 값(현재값·범위)만. **placeholder 보기는 안내와 같은 방식으로** — 안내가 "추가분이 아니라 변경 후 전체 한도" 면 보기도 전체 값("예: 8"), "4개에서 8개로 증설" 처럼 안내와 반대로 쓰지 않는다(2026-11-06 홀드아웃 19 폼, 세 심판 copy 감점). **도움말·선택지 설명·`Alert`·패널 설명까지 한 화면의 짧은 문장은 끝 마침표 없이 통일**한다(두 문장을 이을 때만 가운데 마침표 — "1~32 코어. 전처리는 2~4") — 일부만 마침표가 붙으면 심사자가 copy 불일치로 본다(2026-10-18 13차 폼). 검토·요약 패널의 **빈 값은 "—" 하나**(단위는 값이 있을 때만 — "CPU —코어 · 메모리 —GB" 처럼 이어 붙이지 않고 항목마다 한 행). 예제 form-page.tsx 의 리소스 섹션·검토 패널이 본보기(2026-10-18 13차, 기준선 copy 12쌍 벤치마킹).
|
|
84
86
|
- **글자 수("0 / 500자") 같은 보조 표시는 `FormDescription icon={false}`** — `FormDescription` 은 앞에 정보 아이콘을 그리므로 안내 문장에만 쓴다(2026-10-28 홀드아웃 15, Opus·Gemini "info icon next to a simple character counter").
|
|
@@ -269,10 +269,10 @@ export function ModelDetailPage({ fetchModel }: { fetchModel: () => Promise<Mode
|
|
|
269
269
|
<Table>
|
|
270
270
|
<TableHeader>
|
|
271
271
|
<TableRow>
|
|
272
|
+
{/* 열 순서는 이름 → 상태 → 숫자 → 날짜(같은 화면의 표끼리 같게). 짧은 열은 폭을 준다 — 폭 없는 열은 남는 폭을 똑같이 나눠 상태 열이 비어 보인다(table-fixed) */}
|
|
272
273
|
<TableHead>엔드포인트</TableHead>
|
|
273
|
-
<TableHead align='right'>레플리카</TableHead>
|
|
274
|
-
{/* 짧은 열은 폭을 준다 — 폭 없는 열은 남는 폭을 똑같이 나눠 상태 열이 비어 보인다(table-fixed) */}
|
|
275
274
|
<TableHead className='w-32'>상태</TableHead>
|
|
275
|
+
<TableHead align='right'>레플리카</TableHead>
|
|
276
276
|
<TableHead className='w-44'>갱신일</TableHead>
|
|
277
277
|
</TableRow>
|
|
278
278
|
</TableHeader>
|
|
@@ -280,13 +280,13 @@ export function ModelDetailPage({ fetchModel }: { fetchModel: () => Promise<Mode
|
|
|
280
280
|
{data.deployments.slice(0, 3).map((d) => (
|
|
281
281
|
<TableRow key={d.id}>
|
|
282
282
|
<TableCell>{d.endpoint}</TableCell>
|
|
283
|
-
<TableCell align='right'>{d.replicas}</TableCell>
|
|
284
283
|
<TableCell>
|
|
285
284
|
<Badge variant={DEPLOY_STATUS[d.status].variant}>
|
|
286
285
|
{DEPLOY_STATUS[d.status].icon}
|
|
287
286
|
{DEPLOY_STATUS[d.status].label}
|
|
288
287
|
</Badge>
|
|
289
288
|
</TableCell>
|
|
289
|
+
<TableCell align='right'>{d.replicas}</TableCell>
|
|
290
290
|
<TableCell>{d.updatedAt}</TableCell>
|
|
291
291
|
</TableRow>
|
|
292
292
|
))}
|
|
@@ -305,24 +305,24 @@ export function ModelDetailPage({ fetchModel }: { fetchModel: () => Promise<Mode
|
|
|
305
305
|
<TableHeader>
|
|
306
306
|
<TableRow>
|
|
307
307
|
<TableHead>버전</TableHead>
|
|
308
|
-
<TableHead
|
|
309
|
-
<TableHead
|
|
310
|
-
<TableHead
|
|
311
|
-
<TableHead>등록일</TableHead>
|
|
308
|
+
<TableHead className='w-32'>상태</TableHead>
|
|
309
|
+
<TableHead align='right'>정확도</TableHead>
|
|
310
|
+
<TableHead align='right'>크기</TableHead>
|
|
311
|
+
<TableHead className='w-44'>등록일</TableHead>
|
|
312
312
|
</TableRow>
|
|
313
313
|
</TableHeader>
|
|
314
314
|
<TableBody>
|
|
315
315
|
{data.versions.map((v) => (
|
|
316
316
|
<TableRow key={v.version}>
|
|
317
317
|
<TableCell>{v.version}</TableCell>
|
|
318
|
-
<TableCell>{(v.accuracy * 100).toFixed(1)}%</TableCell>
|
|
319
|
-
<TableCell>{v.sizeMb.toLocaleString()} MB</TableCell>
|
|
320
318
|
<TableCell>
|
|
321
319
|
<Badge variant={STATUS[v.status].variant}>
|
|
322
320
|
{STATUS[v.status].icon}
|
|
323
321
|
{STATUS[v.status].label}
|
|
324
322
|
</Badge>
|
|
325
323
|
</TableCell>
|
|
324
|
+
<TableCell align='right'>{(v.accuracy * 100).toFixed(1)}%</TableCell>
|
|
325
|
+
<TableCell align='right'>{v.sizeMb.toLocaleString()} MB</TableCell>
|
|
326
326
|
<TableCell>{v.createdAt}</TableCell>
|
|
327
327
|
</TableRow>
|
|
328
328
|
))}
|
|
@@ -334,22 +334,22 @@ export function ModelDetailPage({ fetchModel }: { fetchModel: () => Promise<Mode
|
|
|
334
334
|
<TableHeader>
|
|
335
335
|
<TableRow>
|
|
336
336
|
<TableHead>엔드포인트</TableHead>
|
|
337
|
-
<TableHead
|
|
338
|
-
<TableHead
|
|
339
|
-
<TableHead>갱신일</TableHead>
|
|
337
|
+
<TableHead className='w-32'>상태</TableHead>
|
|
338
|
+
<TableHead align='right'>레플리카</TableHead>
|
|
339
|
+
<TableHead className='w-44'>갱신일</TableHead>
|
|
340
340
|
</TableRow>
|
|
341
341
|
</TableHeader>
|
|
342
342
|
<TableBody>
|
|
343
343
|
{data.deployments.map((d) => (
|
|
344
344
|
<TableRow key={d.id}>
|
|
345
345
|
<TableCell>{d.endpoint}</TableCell>
|
|
346
|
-
<TableCell>{d.replicas}</TableCell>
|
|
347
346
|
<TableCell>
|
|
348
347
|
<Badge variant={DEPLOY_STATUS[d.status].variant}>
|
|
349
348
|
{DEPLOY_STATUS[d.status].icon}
|
|
350
349
|
{DEPLOY_STATUS[d.status].label}
|
|
351
350
|
</Badge>
|
|
352
351
|
</TableCell>
|
|
352
|
+
<TableCell align='right'>{d.replicas}</TableCell>
|
|
353
353
|
<TableCell>{d.updatedAt}</TableCell>
|
|
354
354
|
</TableRow>
|
|
355
355
|
))}
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* - 열 폭: 이름 열 `flex: 2, minWidth: 200, maxWidth: 480`, 글자가 들어가는 열은 **모두** `flex: 1, minWidth: 120`(여기서는 소유자 하나지만 팀·프레임워크·설명이 있으면 전부) — 남는 폭이 여러 열로 흩어져야 이름 열만 넓어지지 않는다(2026-10-20 17차·18차). 글자 열이 하나도 없으면 maxWidth 를 뺀다(표 오른쪽이 빈다). 숫자·날짜·상태·진행률 열은 `width`(제목이 들어가는 최소 폭은 표가 보정하고 넘치는 글자는 말줄임표).
|
|
23
23
|
* 보조 정보(이미지·id·경로)는 열을 늘리지 않고 `secondaryField` 로 이름 아래 줄에 — 열이 6~7개를 넘으면 잘린다
|
|
24
24
|
* - 빠른 필터의 건수 뱃지는 표의 상태 뱃지와 같은 색(`countVariant: STATUS[s].tone`)
|
|
25
|
-
* - 목 데이터는 소비 앱(App.tsx)이 `fetchEnvs` 로 넘긴다 — **20행 이상**(페이지
|
|
25
|
+
* - 목 데이터는 소비 앱(App.tsx)이 `fetchEnvs` 로 넘긴다 — **20행 이상**(한 페이지 10행이면 두세 페이지). 데이터가 10행뿐이면 표 아래가 비어 보이고, 한 페이지가 20행이면 첫 화면에서 페이지네이션이 잘린다
|
|
26
26
|
*/
|
|
27
27
|
import { type ReactNode, useEffect, useMemo, useState } from 'react'
|
|
28
28
|
import {
|
|
@@ -77,7 +77,8 @@ const STATUS_OPTIONS = (Object.keys(STATUS) as Status[]).map((s) => ({
|
|
|
77
77
|
value: s,
|
|
78
78
|
label: STATUS[s].label,
|
|
79
79
|
}))
|
|
80
|
-
|
|
80
|
+
// 한 페이지 10행 — 첫 화면(1440×1000)에 표와 페이지네이션이 함께 들어간다. 데이터는 20행 이상(두 페이지 분량, 2026-11-06)
|
|
81
|
+
const PAGE_SIZE = 10
|
|
81
82
|
|
|
82
83
|
// 남는 폭은 이름 열(flex 2, 상한 480)과 글자 열 전부(flex 1)가 나눈다 — 글자 열이 하나뿐이면 2:1 로 나눠도 이름 열이 500px 라 비어 보인다(2026-10-20 17차·18차).
|
|
83
84
|
// maxWidth 를 주면 넓은 화면에서 표 오른쪽이 빈다. 이미지는 열이 아니라 이름 아래 보조 줄(ListPage secondaryField='image')
|
|
@@ -26,7 +26,7 @@ MCP `docs-list` / `docs-show <id>` / `docs-show-story` 와 같은 내용이다.
|
|
|
26
26
|
|
|
27
27
|
| prop | 타입 | 기본값 | 설명 |
|
|
28
28
|
|---|---|---|---|
|
|
29
|
-
| `titleRight` | `ReactNode` | |
|
|
29
|
+
| `titleRight` | `ReactNode` | | 제목 오른쪽 자리. 정보가 있는 작은 행동만 둔다(예: `PageHeaderRefreshButton`). 상태는 `status`·`meta` 에 두고, 장식 Tag·Badge·글자는 넣지 않는다(2026-11-06). |
|
|
30
30
|
| `actions` | `ReactNode` | | 주요 행동 버튼. 제목 줄 오른쪽 (ListPage·DetailPage 와 같은 자리, 2026-10-19 14차) |
|
|
31
31
|
| `stats` | `DashboardStat[]` | | 맨 위 요약 타일. 2열, 넓은 화면(xl)에서 4열, 간격 12px |
|
|
32
32
|
| `loading` | `boolean` | | true 면 타일 숫자 자리에 Skeleton. 패널 로딩은 각 DashboardPanel 의 `loading` |
|
|
@@ -46,7 +46,7 @@ const Default = () => <DashboardDemo />;
|
|
|
46
46
|
| prop | 타입 | 기본값 | 설명 |
|
|
47
47
|
|---|---|---|---|
|
|
48
48
|
| `meta` | `ReactNode` | | 제목 아래 메타. 상세 화면의 핵심 정보는 여기 한 곳 — 여러 개면 `MetaGroup` + `MetaItem` — 가로로 흘려 다음 줄로(2026-10-30 사용자 결정: 메타 자리를 두 개로 나누지 않는다, 2026-11-06 격자 대신 흘림) |
|
|
49
|
-
| `titleRight` | `ReactNode` | |
|
|
49
|
+
| `titleRight` | `ReactNode` | | 제목 오른쪽 자리. 정보가 있는 작은 행동만 둔다(예: `PageHeaderRefreshButton`). 상태는 `status`·`meta` 에 두고, 장식 Tag·Badge·글자는 넣지 않는다(2026-11-06). |
|
|
50
50
|
| `primaryAction` | `DetailPageAction` | | 주 행동 1개 (예: 배포·새 추론 서비스). `primaryBold` 로 맨 오른쪽에 그린다 |
|
|
51
51
|
| `secondaryActions` | `DetailPageAction[]` | | 보조 행동들 (예: 다운로드·복제·새 버전). `secondary` 로 주 행동 왼쪽에 준 순서대로 — 2개까지만 보이고 3번째부터는 "더보기" 메뉴로 접힌다(2026-10-19 14차) |
|
|
52
52
|
| `deleteAction` | `DetailPageAction` | | 삭제. 항상 "더보기" 메뉴 안(구분선 아래 danger 항목)에 둔다 — 빨간 버튼을 첫 화면에 펼치지 않는다(2026-10-19 14차). 확인 Dialog 는 소비 앱이 onClick 에서 연다 |
|
|
@@ -149,7 +149,7 @@ const Default = () => <FormDrawerDemo />;
|
|
|
149
149
|
| `error` | `ReactNode` | | 서버 오류 등 폼 전체 오류. 버튼 위에 Alert(danger) 로 표시 |
|
|
150
150
|
| `submitMode` | `enum` | | 제출 버튼 활성 규칙. `always`(기본) = 제출 중이 아니면 항상 눌리고, 누르면 react-hook-form 이 검증해 틀린 항목마다 FormMessage 를 보인다. `whenValid` = 모든 입력이 유효해질 때까지 잠근다(2026-10-12 전 기본). "누르면 검증해 이유 표시" 가 흔한 폼 관례라 기본을 바꿨다 |
|
|
151
151
|
| `children` (필수) | `ReactNode` | | FormSection 으로 묶은 FormField 들 |
|
|
152
|
-
| `titleRight` | `ReactNode` | |
|
|
152
|
+
| `titleRight` | `ReactNode` | | 제목 오른쪽 자리. 정보가 있는 작은 행동만 둔다(예: `PageHeaderRefreshButton`). 상태는 `status`·`meta` 에 두고, 장식 Tag·Badge·글자는 넣지 않는다(2026-11-06). |
|
|
153
153
|
| `onCancel` | `(() => void)` | | 취소 버튼 핸들러. 없으면 취소 버튼을 그리지 않는다 |
|
|
154
154
|
| `requiredLegend` | `boolean` | | true 면 폼 맨 위에 "* 표시는 필수 입력 항목입니다" 안내 한 줄 |
|
|
155
155
|
| `canvas` | `enum` | `plain` | 본문 바탕. 'plain'(기본)은 흰 캔버스에 섹션 구분선. 'subtle' 은 연한 회색 캔버스 위에 폼 전체를 흰 Card 하나로 — UIClip 폼 진단(evals/metrics-spec §16.7)용 옵트인이며 캔버스 정책(DESIGN.md)은 흰색 그대로다 |
|
|
@@ -175,7 +175,7 @@ const Invalid = () => <FormPageDemo invalid />;
|
|
|
175
175
|
|---|---|---|---|
|
|
176
176
|
| `title` (필수) | `string` | | 페이지 제목 (h1) |
|
|
177
177
|
| `description` | `ReactNode` | | 제목 아래 설명 |
|
|
178
|
-
| `titleRight` | `ReactNode` | | 제목 오른쪽 슬롯 (예: 새로고침 버튼·마지막 갱신 시각) |
|
|
178
|
+
| `titleRight` | `ReactNode` | | 제목 오른쪽 슬롯 (예: 새로고침 버튼·마지막 갱신 시각). 장식 Tag·Badge·글자는 넣지 않는다 |
|
|
179
179
|
| `actions` | `ReactNode` | | 주요 행동 버튼 (보통 1개). 제목 줄 오른쪽(PageHeader `actions`) — 2026-10-19 14차: 페이지 수준 행동은 제목 줄(Polaris 인덱스 페이지·GitHub Issues·기준선과 같은 자리). 1.22.0~1.47.4 의 필터 줄 왼쪽(SharePoint 계열)은 2026-10-13 UIClip(폐기된 자) 근거였다 |
|
|
180
180
|
| `filters` | `ReactNode` | | 검색·필터 컨트롤. 헤더 오른쪽 아래 (TableFilter*, ColumnFilter 등) |
|
|
181
181
|
| `onResetFilters` | `(() => void)` | | 필터를 되돌리는 핸들러. 주면 빠른 필터 줄(표 바로 위) 오른쪽 끝에 "필터 초기화" 외곽선 버튼이 **항상** 자리를 잡는다(빠른 필터·건수가 없으면 filters 줄 끝). 검색창은 그래서 표 오른쪽 끝까지 닿는다(2026-10-18 10차). 필터가 걸렸을 때만 조건부로 넘기지 말고(빈 결과에서 버튼이 튀어나와 배치가 바뀐다) 항상 넘기고 `filtersActive` 로 보일지를 정한다 |
|
|
@@ -385,6 +385,7 @@ const WithoutIcon = () => (
|
|
|
385
385
|
|
|
386
386
|
언제 쓰나: 실행 중/중지/오류 같은 상태(`success`·`danger`·`warning`·`info`), 개수·등급 표시. 의미 variant 를 쓰고 색을 직접 바꾸지 않는다. `appearance='outline'` 은 흰 바탕 + 1px accent 테두리.
|
|
387
387
|
언제 안 쓰나: 사용자가 붙이고 뗄 수 있는 라벨(→ Tag), 클릭해 동작하는 것(→ Button), 지나가는 알림(→ Toast).
|
|
388
|
+
모양은 기본 pill — 상태·건수 뱃지와 빠른 필터 칩이 한 계열(2026-11-06). 4px 는 `rounded='square'`.
|
|
388
389
|
대신/함께: **상태 표시는 Badge 에 아이콘 + 글자** — `<Badge variant='success'><LucideIcon iconComponentName='circle-check' />완료</Badge>`(아이콘은 자식으로 바로, 16px·글자색). 별도 상태 컴포넌트는 없다(2026-10-30). 크기는 기본(`size` 를 주지 않는다). 상태값 → variant·아이콘 매핑은 소비 앱의 상수 한 곳에 둔다.
|
|
389
390
|
|
|
390
391
|
| prop | 타입 | 기본값 | 설명 |
|
|
@@ -470,7 +471,7 @@ const Playground = () => <MetaItem label="등록자" value="smkim1" />;
|
|
|
470
471
|
언제 쓰나: 0~100 의 비율을 길이로 보여 줄 때. 이름은 `aria-label` 로 준다 — 표에서는 행마다 `'GPU 사용률 46%'` 처럼 **무엇의 비율인지**.
|
|
471
472
|
이름을 안 주면 `'46%'`(값이 없으면 `'진행 중'`)를 기본 이름으로 붙인다 — 이름 없는 progressbar 는 axe serious(aria-progressbar-name, 2026-10-20 17차). 기본 이름은 숫자뿐이라 뜻이 약하니 호출자가 주는 것이 맞다.
|
|
472
473
|
언제 안 쓰나: 단계가 있는 진행(→ Stepper), 불확정 대기(→ Spinner).
|
|
473
|
-
대신/함께: 막대 옆에 % 글자를 같이 둔다(`tabular-nums`).
|
|
474
|
+
대신/함께: 막대 옆에 % 글자를 같이 둔다(`tabular-nums`). 색(`variant`)은 **값의 뜻대로** — 높을수록 좋은 비율(정상 행·통과율·완료율)은 기본(primary)이나 success, 낮을수록 좋은 값의 경고 구간만 warning·danger. 페이지·항목의 실패 상태에 맞춰 칠하지 않는다(2026-11-06 홀드아웃 20: "정상 행 99%" 막대가 빨강).
|
|
474
475
|
|
|
475
476
|
| prop | 타입 | 기본값 | 설명 |
|
|
476
477
|
|---|---|---|---|
|
|
@@ -1001,7 +1002,7 @@ const Basic = () => (
|
|
|
1001
1002
|
|---|---|---|---|
|
|
1002
1003
|
| `breadcrumbTitle` | `string \| null` | | Optional override for the breadcrumb tail (last item). - `undefined` (default): consuming apps may fall back to `title`. - `null`: explicitly suppress the auto-appended breadcrumb tail. - `string`: use the given text as the breadcrumb tail regardless of `title`. design-system itself does not render breadcrumbs (they are passed in as the `breadcrumb` slot) — this field is a contract for the consuming app's breadcrumb wrapper to read. |
|
|
1003
1004
|
| `meta` | `ReactNode` | | 제목 아래 메타. 상세 화면의 핵심 정보는 여기 한 곳 — 여러 개면 `MetaGroup` + `MetaItem` — 가로로 흘려 다음 줄로(2026-10-30 사용자 결정: 메타 자리를 두 개로 나누지 않는다, 2026-11-06 격자 대신 흘림) |
|
|
1004
|
-
| `titleRight` | `ReactNode` | |
|
|
1005
|
+
| `titleRight` | `ReactNode` | | 제목 오른쪽 자리. 정보가 있는 작은 행동만 둔다(예: `PageHeaderRefreshButton`). 상태는 `status`·`meta` 에 두고, 장식 Tag·Badge·글자는 넣지 않는다(2026-11-06). |
|
|
1005
1006
|
| `contentMaxWidth` | `string` | | 내용(빵부스러기·제목·행동·설명·탭)의 최대 폭 — CSS 길이(예: `var(--max-w-page-content)`). 주면 그 폭 안에 가운데 두고 띠(배경·아래 선)는 전폭 그대로. FormPage 가 폼 폭에 맞춰 넘긴다(2026-10-26 22차). 기본 없음 = 전폭 |
|
|
1006
1007
|
|
|
1007
1008
|
예시 — story "WithTabs — 탭 + 배지 (controlled)":
|