Text Field
한 줄 텍스트를 입력합니다. 라벨·도움말·오류 메시지가 하나의 필드 단위로 묶입니다.
1. Default / Error
오류 시 .error 를 필드에 붙이고 help 를 오류 메시지로 바꿉니다. 원인과 해결 방법을 함께 씁니다.
IPv4 형식
올바른 IPv4 형식이 아닙니다. 예: 10.0.0.12
HTML
<div class="field"><label for="ip1">허용 IP</label><input id="ip1" value="10.0.0.12"><span class="help">IPv4 형식</span></div>
<div class="field error"><label for="ip2">허용 IP</label><input id="ip2" value="10.0.0" aria-invalid="true" aria-describedby="ip2-err"><span class="help" id="ip2-err">올바른 IPv4 형식이 아닙니다. 예: 10.0.0.12</span></div>CSS · .field
/* JS Design System v0.6 · .field · 토큰(var(--*))은 assets/style.css :root 참고 */
.field{display:flex;flex-direction:column;gap:6px;min-width:260px;font-size:14px}
.field label{font-weight:600}
.field label em{color:var(--danger-text);font-style:normal;margin-left:2px}
.field input,.field textarea{height:44px;border:1px solid var(--border-strong);border-radius:var(--radius-md);padding:0 14px;font:inherit;font-size:14px;background:var(--bg-surface);color:var(--text-primary);width:100%;transition:border-color var(--motion-fast)}
.field textarea{height:auto;min-height:96px;padding:10px 14px;resize:vertical;line-height:1.5}
.field input::placeholder,.field textarea::placeholder{color:var(--text-disabled)}
.field input:hover,.field textarea:hover{border-color:var(--border-strong)}
.field input:focus,.field textarea:focus-within{border-color:var(--border-focus)}
.field input:disabled,.field textarea:disabled{background:var(--bg-panel);color:var(--text-disabled);cursor:not-allowed}
.field input:read-only{background:var(--bg-subtle)}
.field .help{color:var(--text-tertiary);font-size:12px}
.field.error input,.field.error textarea,.field.error .affix{border-color:var(--danger)}
.field.error .help{color:var(--danger-text)}
.field.sm input{height:36px;font-size:12px;padding:0 12px}
.field.lg input{height:52px;font-size:16px;padding:0 16px}
.field .affix{display:flex;align-items:center;gap:8px;height:44px;padding:0 12px;border:1px solid var(--border-strong);border-radius:var(--radius-md);background:var(--bg-surface)}
.field .affix input{border:0;height:auto;padding:0;flex:1;background:transparent;outline:0;min-width:0}
.field .affix:focus-within{border-color:var(--border-focus)}
.field .affix span{color:var(--text-tertiary);font-size:12px;white-space:nowrap}
.field .counter{margin-left:auto;font-size:12px;color:var(--text-tertiary)}
.field .row{display:flex;justify-content:space-between;align-items:baseline}
.form .form-row .field{min-width:0}
.form .field{min-width:0}React · @jiran/ds-react
import { TextField } from "@jiran/ds-react";
export default function Example() {
return (
<>
<TextField id="ip1" label="허용 IP" defaultValue="10.0.0.12" help="IPv4 형식" />
<TextField id="ip2" label="허용 IP" defaultValue="10.0.0" error="올바른 IPv4 형식이 아닙니다. 예: 10.0.0.12" />
</>
);
}2. Size
sm 36 · md 44 · lg 52. 버튼과 같은 높이로 나란히 배치합니다.
HTML
<div class="field sm"><label>Small</label><input placeholder="36px"></div>
<div class="field"><label>Medium</label><input placeholder="44px"></div>
<div class="field lg"><label>Large</label><input placeholder="52px"></div>CSS · .field
/* JS Design System v0.6 · .field · 토큰(var(--*))은 assets/style.css :root 참고 */
.field{display:flex;flex-direction:column;gap:6px;min-width:260px;font-size:14px}
.field label{font-weight:600}
.field label em{color:var(--danger-text);font-style:normal;margin-left:2px}
.field input,.field textarea{height:44px;border:1px solid var(--border-strong);border-radius:var(--radius-md);padding:0 14px;font:inherit;font-size:14px;background:var(--bg-surface);color:var(--text-primary);width:100%;transition:border-color var(--motion-fast)}
.field textarea{height:auto;min-height:96px;padding:10px 14px;resize:vertical;line-height:1.5}
.field input::placeholder,.field textarea::placeholder{color:var(--text-disabled)}
.field input:hover,.field textarea:hover{border-color:var(--border-strong)}
.field input:focus,.field textarea:focus-within{border-color:var(--border-focus)}
.field input:disabled,.field textarea:disabled{background:var(--bg-panel);color:var(--text-disabled);cursor:not-allowed}
.field input:read-only{background:var(--bg-subtle)}
.field .help{color:var(--text-tertiary);font-size:12px}
.field.error input,.field.error textarea,.field.error .affix{border-color:var(--danger)}
.field.error .help{color:var(--danger-text)}
.field.sm input{height:36px;font-size:12px;padding:0 12px}
.field.lg input{height:52px;font-size:16px;padding:0 16px}
.field .affix{display:flex;align-items:center;gap:8px;height:44px;padding:0 12px;border:1px solid var(--border-strong);border-radius:var(--radius-md);background:var(--bg-surface)}
.field .affix input{border:0;height:auto;padding:0;flex:1;background:transparent;outline:0;min-width:0}
.field .affix:focus-within{border-color:var(--border-focus)}
.field .affix span{color:var(--text-tertiary);font-size:12px;white-space:nowrap}
.field .counter{margin-left:auto;font-size:12px;color:var(--text-tertiary)}
.field .row{display:flex;justify-content:space-between;align-items:baseline}
.form .form-row .field{min-width:0}
.form .field{min-width:0}React · @jiran/ds-react
import { TextField } from "@jiran/ds-react";
export default function Example() {
return (
<>
<TextField label="Small" size="sm" placeholder="36px" />
<TextField label="Medium" placeholder="44px" />
<TextField label="Large" size="lg" placeholder="52px" />
</>
);
}3. Prefix / Suffix / Counter
단위·접두어는 .affix 컨테이너 안에 span 으로. 글자 수 제한은 .counter.
일
https://
12 / 80
HTML
<div class="field"><label>보관 기간</label><div class="affix"><input value="90"><span>일</span></div></div>
<div class="field"><label>관리 콘솔 주소</label><div class="affix"><span>https://</span><input placeholder="console.example.com"></div></div>
<div class="field"><div class="row"><label>설명</label><span class="counter">12 / 80</span></div><input value="외부 저장장치 차단 정책" maxlength="80"></div>CSS · .field
/* JS Design System v0.6 · .field · 토큰(var(--*))은 assets/style.css :root 참고 */
.field{display:flex;flex-direction:column;gap:6px;min-width:260px;font-size:14px}
.field label{font-weight:600}
.field label em{color:var(--danger-text);font-style:normal;margin-left:2px}
.field input,.field textarea{height:44px;border:1px solid var(--border-strong);border-radius:var(--radius-md);padding:0 14px;font:inherit;font-size:14px;background:var(--bg-surface);color:var(--text-primary);width:100%;transition:border-color var(--motion-fast)}
.field textarea{height:auto;min-height:96px;padding:10px 14px;resize:vertical;line-height:1.5}
.field input::placeholder,.field textarea::placeholder{color:var(--text-disabled)}
.field input:hover,.field textarea:hover{border-color:var(--border-strong)}
.field input:focus,.field textarea:focus-within{border-color:var(--border-focus)}
.field input:disabled,.field textarea:disabled{background:var(--bg-panel);color:var(--text-disabled);cursor:not-allowed}
.field input:read-only{background:var(--bg-subtle)}
.field .help{color:var(--text-tertiary);font-size:12px}
.field.error input,.field.error textarea,.field.error .affix{border-color:var(--danger)}
.field.error .help{color:var(--danger-text)}
.field.sm input{height:36px;font-size:12px;padding:0 12px}
.field.lg input{height:52px;font-size:16px;padding:0 16px}
.field .affix{display:flex;align-items:center;gap:8px;height:44px;padding:0 12px;border:1px solid var(--border-strong);border-radius:var(--radius-md);background:var(--bg-surface)}
.field .affix input{border:0;height:auto;padding:0;flex:1;background:transparent;outline:0;min-width:0}
.field .affix:focus-within{border-color:var(--border-focus)}
.field .affix span{color:var(--text-tertiary);font-size:12px;white-space:nowrap}
.field .counter{margin-left:auto;font-size:12px;color:var(--text-tertiary)}
.field .row{display:flex;justify-content:space-between;align-items:baseline}
.form .form-row .field{min-width:0}
.form .field{min-width:0}React · @jiran/ds-react
import { TextField } from "@jiran/ds-react";
export default function Example() {
return (
<>
<TextField label="보관 기간" defaultValue="90" suffix="일" />
<TextField label="관리 콘솔 주소" prefix="https://" placeholder="console.example.com" />
<TextField label="설명" defaultValue="외부 저장장치 차단 정책" maxLength={80} showCount />
</>
);
}4. Textarea
여러 줄 입력. 높이는 세로 리사이즈 허용, 최소 96px.
감사 로그에 기록됩니다
HTML
<div class="field" style="min-width:420px"><label for="memo">메모</label><textarea id="memo" placeholder="정책 변경 사유를 남겨 주세요"></textarea><span class="help">감사 로그에 기록됩니다</span></div>CSS · .field
/* JS Design System v0.6 · .field · 토큰(var(--*))은 assets/style.css :root 참고 */
.field{display:flex;flex-direction:column;gap:6px;min-width:260px;font-size:14px}
.field label{font-weight:600}
.field label em{color:var(--danger-text);font-style:normal;margin-left:2px}
.field input,.field textarea{height:44px;border:1px solid var(--border-strong);border-radius:var(--radius-md);padding:0 14px;font:inherit;font-size:14px;background:var(--bg-surface);color:var(--text-primary);width:100%;transition:border-color var(--motion-fast)}
.field textarea{height:auto;min-height:96px;padding:10px 14px;resize:vertical;line-height:1.5}
.field input::placeholder,.field textarea::placeholder{color:var(--text-disabled)}
.field input:hover,.field textarea:hover{border-color:var(--border-strong)}
.field input:focus,.field textarea:focus-within{border-color:var(--border-focus)}
.field input:disabled,.field textarea:disabled{background:var(--bg-panel);color:var(--text-disabled);cursor:not-allowed}
.field input:read-only{background:var(--bg-subtle)}
.field .help{color:var(--text-tertiary);font-size:12px}
.field.error input,.field.error textarea,.field.error .affix{border-color:var(--danger)}
.field.error .help{color:var(--danger-text)}
.field.sm input{height:36px;font-size:12px;padding:0 12px}
.field.lg input{height:52px;font-size:16px;padding:0 16px}
.field .affix{display:flex;align-items:center;gap:8px;height:44px;padding:0 12px;border:1px solid var(--border-strong);border-radius:var(--radius-md);background:var(--bg-surface)}
.field .affix input{border:0;height:auto;padding:0;flex:1;background:transparent;outline:0;min-width:0}
.field .affix:focus-within{border-color:var(--border-focus)}
.field .affix span{color:var(--text-tertiary);font-size:12px;white-space:nowrap}
.field .counter{margin-left:auto;font-size:12px;color:var(--text-tertiary)}
.field .row{display:flex;justify-content:space-between;align-items:baseline}
.form .form-row .field{min-width:0}
.form .field{min-width:0}React · @jiran/ds-react
import { TextArea } from "@jiran/ds-react";
export default function Example() {
return <TextArea id="memo" label="메모" placeholder="정책 변경 사유를 남겨 주세요" help="감사 로그에 기록됩니다" wrapperStyle={{ minWidth: 420 }} />;
}5. Disabled / Read-only
Disabled 는 값을 바꿀 수도 복사할 수도 없을 때, Read-only 는 참조만 할 때.
자동 생성
HTML
<div class="field"><label>정책 ID</label><input value="POL-2041" readonly><span class="help">자동 생성</span></div>
<div class="field"><label>생성자</label><input value="admin@jiran.com" disabled></div>CSS · .field
/* JS Design System v0.6 · .field · 토큰(var(--*))은 assets/style.css :root 참고 */
.field{display:flex;flex-direction:column;gap:6px;min-width:260px;font-size:14px}
.field label{font-weight:600}
.field label em{color:var(--danger-text);font-style:normal;margin-left:2px}
.field input,.field textarea{height:44px;border:1px solid var(--border-strong);border-radius:var(--radius-md);padding:0 14px;font:inherit;font-size:14px;background:var(--bg-surface);color:var(--text-primary);width:100%;transition:border-color var(--motion-fast)}
.field textarea{height:auto;min-height:96px;padding:10px 14px;resize:vertical;line-height:1.5}
.field input::placeholder,.field textarea::placeholder{color:var(--text-disabled)}
.field input:hover,.field textarea:hover{border-color:var(--border-strong)}
.field input:focus,.field textarea:focus-within{border-color:var(--border-focus)}
.field input:disabled,.field textarea:disabled{background:var(--bg-panel);color:var(--text-disabled);cursor:not-allowed}
.field input:read-only{background:var(--bg-subtle)}
.field .help{color:var(--text-tertiary);font-size:12px}
.field.error input,.field.error textarea,.field.error .affix{border-color:var(--danger)}
.field.error .help{color:var(--danger-text)}
.field.sm input{height:36px;font-size:12px;padding:0 12px}
.field.lg input{height:52px;font-size:16px;padding:0 16px}
.field .affix{display:flex;align-items:center;gap:8px;height:44px;padding:0 12px;border:1px solid var(--border-strong);border-radius:var(--radius-md);background:var(--bg-surface)}
.field .affix input{border:0;height:auto;padding:0;flex:1;background:transparent;outline:0;min-width:0}
.field .affix:focus-within{border-color:var(--border-focus)}
.field .affix span{color:var(--text-tertiary);font-size:12px;white-space:nowrap}
.field .counter{margin-left:auto;font-size:12px;color:var(--text-tertiary)}
.field .row{display:flex;justify-content:space-between;align-items:baseline}
.form .form-row .field{min-width:0}
.form .field{min-width:0}React · @jiran/ds-react
import { TextField } from "@jiran/ds-react";
export default function Example() {
return (
<>
<TextField label="정책 ID" defaultValue="POL-2041" readOnly help="자동 생성" />
<TextField label="생성자" defaultValue="admin@jiran.com" disabled />
</>
);
}Props
HTML 에서는 클래스와 속성으로, React(@jiran/ds-react)에서는 같은 이름의 prop(variant · size …)으로 변형·상태를 지정합니다.
| Prop / Class | Type · Values | Default | Description |
|---|---|---|---|
| .field | container | — | label + input(+ .help) 묶음. 폼 안에서 min-width 해제 |
| size | sm | (md) | lg | md | 입력 높이 36 / 44 / 52 |
| .error | boolean | false | 보더 danger, help 를 오류 메시지로. input 에 aria-invalid |
| .affix | wrapper | — | 접두어/접미어 span 과 input 을 감싸는 컨테이너 |
| .counter | element | — | 글자 수 표시. .row 안에서 라벨 우측 정렬 |
| disabled / readonly | attribute | — | 비활성 / 읽기 전용(배경 subtle) |
Usage
React: @jiran/ds-react 패키지를 설치하고 style.css 를 한 번 import 하면 위 React 탭 코드를 그대로 쓸 수 있습니다(React Package). HTML: assets/style.css 를 연결하고(일부만 필요하면 아래 컴포넌트 CSS 복사), 동작이 필요하면 assets/ds.js 를 함께 넣습니다 — 이 페이지의 프리뷰가 그 스크립트로 동작합니다. 색은 전부 토큰(var(--accent) 등)을 참조하므로 --accent-* 만 제품 컬러로 바꾸면 됩니다.
React @jiran/ds-react
npm i @jiran/ds-react
// main.tsx — 앱 진입점에서 한 번
import "@jiran/ds-react/style.css";
// 컴포넌트
import { Field, TextField, TextArea } from "@jiran/ds-react";HTML
<!-- 1) 토큰 + 전체 컴포넌트 스타일 한 번에 연결 -->
<link rel="stylesheet" href="assets/style.css">
<!-- 동작(드롭다운·탭·달력·표 정렬/선택·토스트 등) — 순수 JS 한 파일. React 제품은 불필요 -->
<script src="assets/ds.js" defer></script>
<!-- 2) 또는 아래 "Component CSS" 만 프로젝트 CSS 에 붙이고,
:root 토큰은 Foundations › Design Token 에서 복사 -->Component CSS .field
/* JS Design System v0.6 · .field · 토큰(var(--*))은 assets/style.css :root 참고 */
.field{display:flex;flex-direction:column;gap:6px;min-width:260px;font-size:14px}
.field label{font-weight:600}
.field label em{color:var(--danger-text);font-style:normal;margin-left:2px}
.field input,.field textarea{height:44px;border:1px solid var(--border-strong);border-radius:var(--radius-md);padding:0 14px;font:inherit;font-size:14px;background:var(--bg-surface);color:var(--text-primary);width:100%;transition:border-color var(--motion-fast)}
.field textarea{height:auto;min-height:96px;padding:10px 14px;resize:vertical;line-height:1.5}
.field input::placeholder,.field textarea::placeholder{color:var(--text-disabled)}
.field input:hover,.field textarea:hover{border-color:var(--border-strong)}
.field input:focus,.field textarea:focus-within{border-color:var(--border-focus)}
.field input:disabled,.field textarea:disabled{background:var(--bg-panel);color:var(--text-disabled);cursor:not-allowed}
.field input:read-only{background:var(--bg-subtle)}
.field .help{color:var(--text-tertiary);font-size:12px}
.field.error input,.field.error textarea,.field.error .affix{border-color:var(--danger)}
.field.error .help{color:var(--danger-text)}
.field.sm input{height:36px;font-size:12px;padding:0 12px}
.field.lg input{height:52px;font-size:16px;padding:0 16px}
.field .affix{display:flex;align-items:center;gap:8px;height:44px;padding:0 12px;border:1px solid var(--border-strong);border-radius:var(--radius-md);background:var(--bg-surface)}
.field .affix input{border:0;height:auto;padding:0;flex:1;background:transparent;outline:0;min-width:0}
.field .affix:focus-within{border-color:var(--border-focus)}
.field .affix span{color:var(--text-tertiary);font-size:12px;white-space:nowrap}
.field .counter{margin-left:auto;font-size:12px;color:var(--text-tertiary)}
.field .row{display:flex;justify-content:space-between;align-items:baseline}
.form .form-row .field{min-width:0}
.form .field{min-width:0}