Vinyl UI logoVinyl UI

Field

폼 필드를 구성하는 Label, Input, ErrorMessage 컴포넌트

개요

Field는 라벨·입력·에러 메시지를 조합하는 오픈 프리미티브입니다. Field(Context root)로 감싸고 Label·Input·ErrorMessage를 배치하면, Field가 id·에러 상태 등을 context로 각 파트에 전달해 자동으로 연결해 줍니다.

이 프리미티브는 구조·형태를 직접 조합해야 할 때 사용합니다.

각 파트는 Field 없이 단독으로도 쓸 수 있으며, 그때는 id/htmlFor 등을 직접 연결해야 합니다.

커스텀 예시

Field가 id·hasError·required·disabled를 context로 내려주므로, 각 파트는 그 값을 전달 받아 사용이 가능합니다. 만약, 중복해서 props를 지정하면 각 파트 컴포넌트의 props가 Field의 context보다 우선합니다.

import { useState } from 'react';
import { Field, Label, Input, ErrorMessage } from '@bigmobility/vinyl-ui/field';

export default function FieldExample() {
  const [field, setField] = useState({
    email: '',
    error: '',
  });

  const handleChange = ({ name, value }: {
    name: string;
    value: string;
  }) => {
    setField({
      ...field,
      [name]: value,
    });
  };

  return (
    <Field
      hasError={!!field.error}
      required
    >
      <Label>이메일</Label>
      <Input
        name="email"
        value={field.email}
        onChange={handleChange}
      />
      <ErrorMessage>{field.error}</ErrorMessage>
    </Field>
  );
}

에러 상태

Field에 hasError를 주면 Input 스타일이 바뀌고 ErrorMessage가 노출됩니다.

Field Props

PropTypeDefaultDescription
idstring—필드 id. 미지정 시 자동 생성
hasErrorbooleanfalse에러 상태 — Input 스타일·aria, ErrorMessage 노출 제어
requiredbooleanfalse필수 표시 (Label·Input에 전파)
disabledbooleanfalse비활성화 (Input에 전파)

Label Props

PropTypeDefaultDescription
requiredboolean—필수 지정 및 필수 마크 표기
htmlForstring—연결할 input 의 id

Input Props

<input>의 표준 속성(value·onChange·placeholder·disabled 등)을 그대로 사용 가능합니다.

PropTypeDefaultDescription
hasErrorboolean—에러 스타일·aria
refRef—input(HTMLInputElement) 요소 ref

ErrorMessage Props

Field 안에서는 hasError일 때만 노출됩니다. 단독 사용 시 id를 지정해야 합니다.

PropTypeDefaultDescription
idstring—단독 사용 시 필드 base id

접근성

  • 라벨 연결 — Field가 만든 inputId로 Label의 htmlFor와 Input의 id가 자동으로 이어집니다.
  • 에러 배선 — 에러 상태의 Input은 aria-invalid와 aria-describedby(에러 메시지 id를 가리킴) 속성을 갖습니다.
  • 폼 에러 포커스 — 여러 필드 중 가장 위 에러로 이동시키려면, 각 input의 aria-invalid 속성을 조회하세요. DOM 순서상 첫 매칭이 최상단입니다.
const formRef = useRef<HTMLFormElement>(null);

function focusFirstError() {
  const element = formRef.current?.querySelector('[aria-invalid="true"]');
  element?.focus();
  element?.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
  • 특정 필드를 직접 제어하려면 Input의 ref로 input 노드에 접근할 수 있습니다.
  • 단독 사용 시에는 Label의 htmlFor, Input의 id, ErrorMessage의 id에 같은 값을 주면 됩니다.

토큰 커스터마이징

Field 파트는 아래 시맨틱 토큰을 참조합니다. (테마 참고).

토큰미리보기용도
text.input입력 텍스트·라벨
text.input-soft플레이스홀더
layout.bg-light입력 배경
layout.default-line입력 테두리
layout.strong-linehover·focus 테두리
layout.bg-issue에러 배경
layout.issue-line에러 테두리
text.issue에러 메시지
layout.primary필수 표시 점
layout.bg-disable비활성 배경
text.disable비활성 텍스트