148 lines
10 KiB
Markdown
148 lines
10 KiB
Markdown
# Input
|
|
|
|
현재 HM 구현의 기준 문서. 공통 원칙은 `docs/guidelines/프로젝트-지침.md`(§6, §7), Token 구조 원칙은 `테마-토큰-가이드.md`를 따른다. 이 문서는 Input 고유의 API·Token·상태·결정만 기록한다.
|
|
|
|
## 1. 상태
|
|
|
|
| 항목 | 내용 |
|
|
|---|---|
|
|
| 구현 상태 | HM 스타일 적용 완료 (`hm-input` class 없음) |
|
|
| 기준일 | 2026-10-02 |
|
|
| 기반 | shadcn/ui Input (native `<input>`) |
|
|
| 대표 Story | `Components/Form/Input` → `All` (이름 "Input") |
|
|
| 보류 | 아래 §11 |
|
|
|
|
## 2. 적용 근거
|
|
|
|
- **ui_viz Confirmed**: size·padding-x(input.md §24, §16), border/radius/색(§10, §11, D-005), Focus/Error 표현.
|
|
- **ui_viz owner decision**: size별 value·placeholder 글자(§9, §21 `INPUT_VALUE_ROLE_FOLLOWS_SIZE`).
|
|
- **HM 사용자 결정**: Disabled opacity `0.6`(2026-10-02, §10).
|
|
- **shadcn 유지**: native input 동작, `file:` 스타일 class, disabled 포인터/커서 처리.
|
|
- **ui_viz 미정의**: 아래 §11 참조.
|
|
|
|
## 3. 기본 규격
|
|
|
|
| Size | Height(min) | Padding-x | Font / Line-height |
|
|
|---|---|---|---|
|
|
| `small` | 28 | 10 | 12 / 18 |
|
|
| `medium` | 34 | 12 | 14 / 20 |
|
|
| `large` (기본) | 42 | 12 | 16 / 24 |
|
|
|
|
- 높이는 `min-height`, 한 줄 텍스트는 가운데.
|
|
- Border 1px(`--hm-sem-stroke-input-default`), radius `--hm-radius-input`, face `--hm-sem-bg-surface`, 글자색 `--hm-sem-fg-primary`, weight 400.
|
|
- Placeholder: `--hm-sem-fg-placeholder`, `opacity: 1`.
|
|
|
|
## 4. 상태 규격
|
|
|
|
| 상태 | 값 | 근거 |
|
|
|---|---|---|
|
|
| Default | 위 기본 규격 | ui_viz Confirmed |
|
|
| Focus (`:focus-visible`) | border 1.5px `--hm-sem-stroke-input-focus`, shadow/glow 없음, padding으로 0.5px 보정해 값이 움직이지 않음 | ui_viz Confirmed |
|
|
| Error (`[aria-invalid="true"]`) | border 1.5px `--hm-sem-action-destructive`, ring 없음 | ui_viz Confirmed |
|
|
| Error + Keyboard Focus | Destructive border 유지 + HM Focus Semantic 3px / 50% outer ring, `outline: none` | 2026-10-06 사용자 결정 |
|
|
| Disabled | White face 유지, `opacity: 0.6`, `cursor: not-allowed`, `pointer-events: none` | face: ui_viz Confirmed / opacity: 사용자 결정 |
|
|
| Readonly (`[readonly]`) | 박스 없음(border 0, padding-x 0, 투명 face), dim 없음, 항상 Large 치수 | ui_viz |
|
|
| Hover / Active | 정의 없음(별도 스타일 없음) | ui_viz 미정의 |
|
|
|
|
Focus ring chain은 `--ring` → `--hm-sem-stroke-input-focus` → `--hm-color-border-focus`다. Error border는 `--hm-input-error-border-color` → `--hm-sem-action-destructive`를 따른다. 일반 Focus / Readonly의 기존 표현은 유지하며, editable Invalid + Focus에서 UA outline 의존을 제거했다.
|
|
|
|
## 5. API
|
|
|
|
```
|
|
Input:
|
|
size?: native <input size> (number)
|
|
→
|
|
size?: "small" | "medium" | "large" 기본 "large"
|
|
```
|
|
|
|
- native `size`(글자 수 폭)를 대체한다. native 속성이 CSS 폭과 무관해 쓰임이 없다는 판단(지침 §6.7).
|
|
- 출력 속성: `data-slot="input"`, `data-size`.
|
|
- 상태는 새 prop이 아니라 native 속성으로 준다: `aria-invalid`, `disabled`, `readOnly`.
|
|
- Field(Label, Required 별표, Helper/Error 문구)는 Input에 포함하지 않는다. 소비 조합이다.
|
|
|
|
## 6. Token
|
|
|
|
Component Mapping 위치: `packages/ui/src/theme/input.css` (`--hm-input-*`).
|
|
|
|
- 재사용한 공통 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 참조).
|
|
- Input 전용 값: `--hm-input-height-{sm,md,lg}`, `--hm-input-padding-x-*`, `--hm-input-font-size-*`, `--hm-input-line-height-*`, `--hm-input-state-border-width`(1.5px).
|
|
- Primitive 신규 추가 없음.
|
|
- Button의 높이 Token(`--hm-button-height-*`)·Select 높이와 값이 겹쳐도 alias하지 않는다. 각 ui_viz 규격이 따로 확정한 값이다.
|
|
- Disabled opacity는 Token이 아니라 `ui/input.tsx`의 `disabled:opacity-60`으로 적용된다(§11).
|
|
|
|
## 7. shadcn / Radix에서 유지한 항목
|
|
|
|
native `<input>` 동작 전체(`type`, `file:` 스타일 class, `placeholder`), `data-slot`, `cn(..., className)`, `transition-colors`, `outline-none`, disabled의 `pointer-events-none`/`cursor-not-allowed`. Radix 사용 없음.
|
|
|
|
## 8. HM에서 변경한 항목
|
|
|
|
- `size` API 교체, 기본 Large, `data-size` 추가.
|
|
- shadcn의 기본 높이/padding/border/ring/dark class 제거 → theme CSS가 담당.
|
|
- 일반 Focus는 border 1.5px로 표시, Error 단독은 ring 없음. Error + `:focus-visible`만 독립 Focus ring을 표시한다.
|
|
- Readonly 전용 표현 추가.
|
|
- `hm-input` class 제거.
|
|
|
|
## 9. Storybook
|
|
|
|
- 대표 Story `All`("Input"): 구역 `sizes`, `default`, `focus`, `error`, `error-message`, `disabled`, `readonly`. Focus는 `play` 함수로 실제 focus를 준다.
|
|
- verification(`tags: ["verification"]`): `Default`, `Focus`, `Error`, `InvalidFocus`, `ErrorMessage`, `Required`, `Disabled`, `Readonly`, `Sizes`("Compare Sizes"), `Small`, `Medium`, `Large`, `Playground`(HM 전용).
|
|
- Error Message·Required 표시는 Story 안의 조합 예시이며 Input 컴포넌트의 일부가 아니다.
|
|
- sidebar는 `All` 하나만 보이는 1뎁스. 운영 규칙은 지침 §6.3, §7 참조.
|
|
|
|
## 10. 사용자 확정 결정
|
|
|
|
**2026-10-11 최종 사용자 결정 — Disabled Input Label**
|
|
|
|
- Input Story 소비 조합(`src/components/input.stories.tsx`의 `Specimen`)의 Disabled Label은 기존 `text-muted-foreground` → `--muted-foreground` → `--hm-sem-fg-secondary` 색(`#6E6759`)을 유지하고, `input.disabled`일 때만 `opacity-60`을 적용한다. HEX·Semantic Token·공개 Input API는 변경하지 않는다.
|
|
- Disabled Input 컨트롤 `0.6`, 소비 Label `0.6`, 부모 Field `1`이다. 형제 요소에 각각 적용하여 중첩 opacity를 만들지 않는다. 기본·readOnly·Error Label은 기존 `1`을 유지한다(Disabled가 아닌 경우).
|
|
- 공통 [Label Component](label.md#4-상태-규격)의 Disabled `0.5`는 유지한다. Helper Text(`#6E6759`)와 Error Message(`#D32F2F`)도 기존 구현을 유지하며 이번 변경 대상이 아니다.
|
|
- 이전 검토의 “Label 100% 유지”는 당시 선택이며 현재 최종 결정은 Disabled Input Story Label `60%`다. 아래 2026-10-02 승인과 2026-10-06 검증은 당시 기록으로 보존하며, 이번 Label 정책의 현재 검증을 대신하지 않는다.
|
|
- 실제 소비 화면은 자동으로 확대 수정하지 않는다. 신규 화면의 공개 Label 재사용·control 연결 계약은 유지하며, 공통 Label의 Disabled 정책을 이 Story 결정으로 대체하지 않는다.
|
|
|
|
**2026-10-02**
|
|
|
|
- Input Disabled opacity `0.6` 확정.
|
|
- ui_viz에서 Temporary로 기록된 이력이 있으나, 현재 HM 구현에서는 사용자 결정이 우선한다. 이전의 "Temporary → 미적용" 판단은 이 결정으로 superseded.
|
|
- 이 값은 Input에 대한 현재 최종값이다. 전체 디자인 시스템 공통 Disabled opacity로 자동 확대하지 않는다.
|
|
- 근거: Select 작업에서 확정된 Disabled 시각 intent와의 정합. Select Trigger도 `0.6`(select.md 참조).
|
|
|
|
## 11. 미확정 / 보류
|
|
|
|
- ui_viz 미정의: Hover, Active 표현, Error 메시지 컴포넌트, Input 내부 아이콘/clear 버튼.
|
|
- Disabled opacity `0.6`은 Input 한정 사용자 결정이다. 다른 컴포넌트에 적용하지 않는다.
|
|
- `file:` 타입 스타일은 shadcn 기본이며 HM 규격 없음.
|
|
- Readonly에서 Disabled 조합 동작은 별도로 확인하지 않았다.
|
|
|
|
## 12. 관련 파일
|
|
|
|
```
|
|
packages/ui/src/components/ui/input.tsx
|
|
packages/ui/src/theme/input.css
|
|
src/components/input.stories.tsx
|
|
src/components/story-board.tsx
|
|
```
|
|
|
|
## 13. 검증 상태
|
|
|
|
- TypeScript, `npm run build`: 통과(2026-10-02).
|
|
- Storybook computed style: `Components/Form/Input` Disabled `opacity` = `0.6` 확인.
|
|
- Story: `All`의 구역 7개와 verification Story ID 존재 확인.
|
|
- 2026-10-06 ui_viz Storybook 화면 비교(1280/768/390, Chromium): 대표 Story 7구역의 컨트롤·Label·Error 문구 computed style 일치, placeholder 색, Focus·Error 경계, Error+Focus의 outline 유지, 긴 값 가로 스크롤 일치. Readonly는 ui_viz `<p>` 대신 native `input[readonly]`로 같은 42px·16/24 표시. Error Message·Required Story에 ui_viz처럼 placeholder를 넣음(Story만 변경).
|
|
- 미확인: 브라우저별 렌더(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는 border만 변경, Small/Medium/Large 28/34/42px 및 12/14/16px 유지. Readonly border 0 / padding 0, Disabled opacity 0.6 유지. Border CSS 계약 1.5px, 이 브라우저 환경의 computed border-width는 1px으로 정규화; padding 보정은 11.5px 유지. 기존 non-invalid Readonly의 UA outline 정책은 이번 범위에서 유지.
|
|
|
|
### Disabled Input Label 검증 (2026-10-11)
|
|
|
|
- 실제 Storybook(Chromium/Edge): Disabled Label `rgb(110, 103, 89)` / opacity `0.6`, Input `0.6`, 부모와 모든 조상 opacity `1`. 공통 Label Disabled `0.5`, Input Default/readOnly/Error Label `1` 유지.
|
|
- 실제 배경 `rgb(245, 240, 229)`에서 합성색 약 `#A49E91`, 대비 약 `2.35:1`(원본 100% 약 `4.93:1`). 일반 텍스트 `4.5:1`보다 낮으며, 실제 disabled 컨트롤에 연결된 비활성 UI 라벨의 WCAG 1.4.3 예외와 구분한다. 사용자 확정 정책은 유지하고 가독성 우려를 기록한다.
|
|
- Default/Disabled/Readonly/Error/Error Message/Playground 및 대표 All 390/768/1280/1440px, `htmlFor`/control 연결·편집·readOnly·disabled focus·문서 overflow 확인. Typecheck, UI build, Storybook build 통과. Guard FAIL 0, 원본 `NOT VERIFIED` 보존. [검증 범위와 제한](../reports/disabled-input-label-2026-10-11.md).
|