Files
HMDesign/docs/components/textarea.md
T

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, Focus color.stroke.input.focus, Error color.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, padding 8px 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에서 변경한 항목

  • resize prop, data-resize 추가.
  • shadcn의 기본 min-height/padding/border/ring/shadow/dark:/field-sizing-content class 제거 → 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 조합 예시는 shadcn text-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-shadow color(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 border rgb(212, 20, 102) + Focus shadow color(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.