10 KiB
Textarea
현재 HM 구현의 기준 문서. 공통 원칙은 docs/guidelines/프로젝트-지침.md(§6, §7), Token 구조 원칙은 테마-토큰-가이드.md를 따른다. 이 문서는 Textarea 고유의 API·Token·상태·결정만 기록한다.
1. 상태
| 항목 | 내용 |
|---|---|
| 구현 상태 | HM 스타일 적용 완료 (hm-textarea class 없음) |
| 기준일 | 2026-10-02 |
| 기반 | shadcn/ui Textarea (native <textarea>) |
| 대표 Story | Components/Form/Textarea → All (이름 "Textarea") |
| 보류 | 아래 §11 |
2. 적용 근거
원본: 외부 ui_viz Reference (ZIP 미포함)의 docs/components/textarea.md (+ brand/hanmac/foundation/{color,border}.md, foundation/token-inventory.md의 입력 계열 Semantic).
- ui_viz Confirmed: border 1px / radius 4px(§2, §2.2), 면·값·Placeholder 색(§2.2), 기본 테두리
color.stroke.input.default, Focuscolor.stroke.input.focus, Errorcolor.stroke.input.error, Focus/Error 두께 1.5px(D-005 상속, §2.2), Hover = Default(§3.3 사용자 결정 TEXTAREA-05, Confirmed), Readonly 표현(§3.1), 값 typography 16/24/400(§2). - SCOPED_DESIGN_APPROVAL (2026-09-17 owner, Textarea 한정): min-height
112px, padding8px 12px(§8.1, TEXTAREA-01/02). D-093 디자인 확정값이 아니며 ui_viz Canonical은TBD다. Textarea 범위에서만 사용하고size.*/space.*로 승격하지 않는다. - Component-local:
resize정책(vertical 기본, none/both 선택, Readonly는 none 고정). - shadcn 유지: native textarea 동작,
transition-colors,outline-none, Disabled opacity와 커서. - HM 사용자 결정(2026-10-06): Error border와 Keyboard Focus ring을 독립 Semantic으로 표시한다.
3. 기본 규격
| 항목 | 값 | 상태 |
|---|---|---|
| Width | 부모 폭 100% (w-full) |
Canonical |
| Min-height | 112px | SCOPED_DESIGN_APPROVAL |
| Padding | 8px (block) / 12px (inline) | SCOPED_DESIGN_APPROVAL |
| Border / Radius | 1px --hm-sem-stroke-input-default / --hm-radius-input(4px) |
Confirmed |
| Face / 값 색 | --hm-sem-bg-surface / --hm-sem-fg-primary |
Confirmed |
| Typography | 16 / 24 / 400 | Existing role |
| Placeholder | --hm-sem-fg-placeholder, opacity: 1 |
Confirmed |
| Resize | vertical 기본 |
Component-local |
Size 체계 없음. Input의 Small/Medium/Large와 공유하지 않는다(ui_viz Textarea에 size 정의 없음). field.gap 8px 등 Field 관계는 Textarea 밖(소비 조합)이다.
4. 상태 규격
| 상태 | 값 | 근거 |
|---|---|---|
| Default | 위 기본 규격 | Confirmed |
| Hover | Default와 동일 (hover rule 없음) | Confirmed (사용자 결정 TEXTAREA-05) |
Focus (:focus-visible) |
border 1.5px --hm-sem-stroke-input-focus, shadow/glow 없음, padding으로 0.5px 보정해 글자 위치 유지 |
Confirmed (D-005 상속) |
Error ([aria-invalid="true"]) |
border 1.5px --hm-sem-action-destructive, ring 없음, 동일 padding 보정 |
Confirmed (D-005 상속) |
| Error + Keyboard Focus | Destructive border 유지 + HM Focus Semantic 3px / 50% outer ring, outline: none |
Input과 동일, 2026-10-06 사용자 결정 |
| Disabled | surface face 유지, cursor: not-allowed, opacity: 0.6 |
opacity: 사용자 결정(§10) |
Readonly ([readonly]) |
Default와 같은 면·테두리·typography, opacity: 1, resize: none 고정 |
Component-local (§3.1) |
Chromium은 1.5px 테두리를 getComputedStyle 기준 1px로 계산한다(ui_viz도 같은 관측을 기록). padding 보정(7.5px/11.5px)은 선언 1.5px 기준으로 적용된다.
5. API
Textarea:
resize?: "vertical" | "none" | "both" 기본 "vertical"
- 나머지는 native
<textarea>props 그대로(rows,placeholder,disabled,readOnly,aria-invalid등). - 출력 속성:
data-slot="textarea",data-resize. - 상태는 새 prop이 아니라 native 속성으로 준다.
- Field(Label, Required 별표, Helper/Error 문구)는 Textarea에 포함하지 않는다. 소비 조합이다.
- auto-grow, 글자 수 카운터는 범위 밖이다. shadcn의
field-sizing-content(auto-grow)는 제거했다.
6. Token
Component Mapping 위치: packages/ui/src/theme/textarea.css (--hm-textarea-*).
- 재사용한 공통 Semantic:
--hm-radius-input,--hm-sem-bg-surface,--hm-sem-fg-primary,--hm-sem-fg-placeholder,--hm-sem-stroke-input-default,--hm-sem-stroke-input-focus,--hm-sem-action-destructive(Semantic 참조). - Textarea 전용 값:
--hm-textarea-min-height,--hm-textarea-padding-{block,inline},--hm-textarea-font-{size,weight},--hm-textarea-line-height,--hm-textarea-border-width,--hm-textarea-state-border-width(1.5px). - Primitive 신규 추가 없음.
- Input의
--hm-input-*와 값이 겹쳐도 alias하지 않는다(ui_viz도 Input·Textarea를 별도 owner로 두고 값을 각각 소비).
7. shadcn / Radix에서 유지한 항목
native <textarea> 동작 전체, data-slot, cn(..., className), transition-colors, outline-none, disabled:cursor-not-allowed. Radix 사용 없음.
8. HM에서 변경한 항목
resizeprop,data-resize추가.- shadcn의 기본 min-height/padding/border/ring/shadow/
dark:/field-sizing-contentclass 제거 → theme CSS가 담당. - 일반 Focus는 border 1.5px로 표시, Error 단독은 ring 없음. Error +
:focus-visible만 독립 Focus ring을 표시한다. - Readonly 전용 표현(resize 고정) 추가.
9. Storybook
- 대표 Story
All("Textarea"): 구역default,hover,focus,error,error-message,readonly,disabled,multiline,resize. Focus는play함수로 실제 focus를 준다. Hover는 별도 pseudo 복제 없이 Default와 같다는 설명만 둔다. - verification(
tags: ["verification"]):Default,Focus,Error,InvalidFocus,ErrorMessage,Required,Readonly,Disabled,LongContent,Playground(HM 전용). - Error Message·Required 표시는 Story 안의 조합 예시이며 Textarea 컴포넌트의 일부가 아니다.
10. 사용자 확정 결정
2026-10-02: Textarea Disabled opacity 0.6 확정. ui_viz에서 Temporary지만 현재 HM 구현에서는 이 결정이 우선한다. Textarea 한정이며 전역 Disabled opacity로 확대하지 않는다 (Input·Select와 같은 값). Token이 아니라 ui/textarea.tsx의 disabled:opacity-60으로 적용.
- ui_viz에 기록된 결정을 따른다: 2026-09-17 Hover = Default(TEXTAREA-05), min-height 112 / padding 8·12 Textarea 한정 승인(TEXTAREA-01/02). 2026-10-01 필수 별표
#D32F2F(TEXTAREA-07, Textarea에 별표 없음 — Story 조합 예시는 shadcntext-destructive).
11. 미확정 / 보류
- min-height 112 / padding 8·12: Textarea 한정 owner 승인이지만 ui_viz Canonical은
TBD, D-093 미확정(TEXTAREA-01/02/06,NO_SOURCE_EVIDENCE). 변경 가능성이 있다. - Label 14/20/500 · Value 16/24 · Error 11/18 vs D-093 원본 실행본 13/700 등:
KNOWN_DEVIATION / FOLLOW_UP(TEXTAREA-08). 확정 근거 없어 ui_viz 문서값을 사용. - Error + Focus: HM 사용자 결정(2026-10-06)으로 Input과 같은 3px / 50% Focus Semantic ring을 적용한다.
- auto-grow, 글자 수 카운터(TEXTAREA-03): 범위 밖. max length 관련 표현 없음.
- 전역 focus ring 수치: TBD.
12. 관련 파일
packages/ui/src/components/ui/textarea.tsx
packages/ui/src/theme/textarea.css
packages/ui/src/theme.css (import)
packages/ui/src/index.ts (export Textarea, TextareaResize)
src/components/textarea.stories.tsx
src/components/story-board.tsx
13. 검증 상태
npm run typecheck(root + ui + center-graphic),npm run build: 통과(2026-10-02).- Storybook computed style(
components-form-textarea--all): Default min-height 112px / padding 8px 12px / border 1px rgb(222,219,210) / radius 4px / bg white / 16·24·400 / resize vertical / Placeholder rgb(153,153,153), Error border rgb(211,47,47) + padding 7.5px 11.5px, Readonly resize none · opacity 1,resize="none"적용, Disabled opacity 0.6. - 실제 클릭 focus:
:focus-visible매칭, border rgb(18,51,40), padding 7.5px 11.5px 확인. - Story ID:
All+ verification 8개 존재 확인(index.json).@hm-design/ui에서import { Textarea }로 Story가 빌드됨. - 2026-10-06 ui_viz Storybook 화면 비교(1280/768/390, Chromium): 컨트롤 computed style 일치(ui_viz Story의 114px은
rows=4때문), Error+Focus outline 유지, 긴 내용은 내부 세로 scroll·가로 넘침 없음, Readonly 편집 불가·선택 가능, Disabled focus 불가. ui_viz는 Disabled에서 Field 전체(라벨 포함)를 흐리지만 HM은 컨트롤만 흐리고 라벨은Label의 disabled 연동(소비 조합)이 맡는다 — 의도적 차이. 대표 Story는 ui_viz 5구역을 모두 포함한 상위 집합(Hover = Default, Error Message, Resize none 추가)이라 변경하지 않음. - 미확인: 브라우저별 렌더(Chromium 외).
Invalid + Focus 검증 (2026-10-07)
- Focus ring:
--ring→--hm-sem-stroke-input-focus→--hm-color-border-focus. Invalid border: 기존--hm-sem-action-destructive. 새 Runtime Role / Production Error prop / 메시지 컴포넌트 없음. - Chromium computed style: Invalid border
rgb(211, 47, 47), Invalid + Keyboard Focus box-shadowcolor(srgb 0.14902 0.4 0.317647 / 0.5) 0px 0px 0px 3px,outline-style: none. Invalid 단독은box-shadow: none. - Theme Designer UI에서 Focus
#c75b00, Destructive#d41466, Active#007f73, Accent#ffd36d로 분리. Invalid borderrgb(212, 20, 102)+ Focus shadowcolor(srgb 0.780392 0.356863 0 / 0.5) 0px 0px 0px 3px확인. npm run typecheck,npm run build,npm run build-storybook통과. 기존 문서 예시 CSS warning 및 Storybook chunk 크기 / plugin timing warning 유지.- 검증 범위: Chromium 환경. 다른 브라우저의 렌더링은 별도 확인이 필요하다.
- 대표
All과 숨김InvalidFocus확인. 일반 Focus / Invalid의 padding 7.5px 11.5px, 기본 min-height 112px / 16px 글자 / Readonly resize none / Disabled opacity 0.6 유지. Border CSS 계약 1.5px, 이 브라우저 환경의 computed border-width는 1px으로 정규화. Input과 같은 Invalid + Focus ring.