Przejdź do treści

Field

Stabilny

System układu formularzy — etykiety, kontrolki, opisy i błędy rozmieszczone spójnie, dostępnie i przy każdej szerokości.

Instalacja

$ pnpm dlx shadcn@latest add https://www.prfct.dev/r/field.json

Użycie

import {
  Field,
  FieldDescription,
  FieldError,
  FieldGroup,
  FieldLabel,
  FieldLegend,
  FieldSet,
} from "@/components/ui/field"
<FieldSet>
  <FieldLegend>Profile</FieldLegend>
  <FieldGroup>
    <Field>
      <FieldLabel htmlFor="name">Name</FieldLabel>
      <Input id="name" />
      <FieldDescription>Shown on your public profile.</FieldDescription>
    </Field>
  </FieldGroup>
</FieldSet>

Field współpracuje z każdą kontrolką prfct — Input, Textarea, Select, Checkbox, Radio Group, Switch, Slider i innymi — dzięki temu wszystkie formularze w produkcie mają ten sam rytm.

Anatomia

  1. 1EtykietaNazywa kontrolkę. Zawsze widoczna; powiązana przez htmlFor.
  2. 2OpisOpcjonalna wskazówka: format, limity, konsekwencje.
  3. 3KontrolkaDowolna kontrolka formularza prfct — tutaj Input.
CzęśćRola
FieldSet / FieldLegendNatywny <fieldset> i jego podpis. Grupuje powiązane kontrolki i nadaje grupie nazwę dla technologii wspomagających.
FieldGroupPionowy rytm między polami — domyślnie 20 px, w grupach pól wyboru 12 px. Wyznacza też kontener dla zapytań kontenerowych, na które reagują responsywne pola.
FieldJedna kontrolka z etykietą, opisem i błędem. role="group", układ pionowy, poziomy lub responsywny.
FieldContentW polach poziomych układa etykietę i opis jedno pod drugim obok kontrolki.
FieldLabel / FieldTitleEtykieta kontrolki; FieldTitle to nagłówek kart wyboru, który nie jest etykietą.
FieldDescriptionTekst pomocniczy pod kontrolką.
FieldErrorKomunikat walidacji z ikoną, ogłaszany jako alert.
FieldSeparatorSeparator między sekcjami z opcjonalną etykietą pośrodku.

Przykłady

Formularz

Kompletny formularz: FieldSet z legendą i opisem, dwukolumnowy wiersz na imię i nazwisko, lista wyboru, wielowierszowe pole tekstowe, separator i akcje. Wszystko dziedziczy te same odstępy bez ani jednej klasy marginesu.

Profil

Walidacja

Oznacz Field atrybutem data-invalid, a kontrolkę atrybutem aria-invalid, i wyrenderuj FieldError powiązany przez aria-describedby. Przekaż listę do errors, żeby pokazać kilka reguł naraz. Wyślij ten formularz, żeby zobaczyć, jak to działa.

<Field data-invalid>
  <FieldLabel htmlFor="email">Email</FieldLabel>
  <Input id="email" aria-invalid aria-describedby="email-error" />
  <FieldError id="email-error">Enter an email address like name@company.com.</FieldError>
</Field>
Dlaczego oba atrybuty?
aria-invalid na kontrolce to atrybut, który odczytują technologie wspomagające i który barwi obramowanie na czerwono. data-invalid na Field to punkt zaczepienia dla stylów i testów całego pola — dzięki niemu dopasujesz otaczające elementy bez sięgania do wnętrza kontrolki.

Układ poziomy

Strony ustawień najlepiej czyta się jako wiersze: etykieta i opis po lewej, kontrolka po prawej. Użyj orientation="horizontal" razem z opakowaniem FieldContent.

Karty wyboru

Owiń cały FieldFieldLabel, a stanie się kartą, którą można zaznaczyć. Celem kliknięcia jest cała karta, a zaznaczenie zabarwia ją kolorem marki. Działa tak samo z grupami przycisków radiowych, jak i z polami wyboru.

Plan

Grupa pól wyboru

Zgrupuj powiązane pola wyboru w FieldSetFieldLegend — czytniki ekranu ogłaszają legendę, gdy fokus wchodzi do grupy. Nadaj wewnętrznemu FieldGroup atrybut data-slot="checkbox-group", żeby zagęścić odstępy.

Alerty o incydentach

Układ responsywny

orientation="responsive" w wąskich kontenerach układa etykietę i kontrolkę jedna pod drugą, a gdy otaczający FieldGroup jest szerszy niż kontenerowy breakpoint md — obok siebie. Reaguje na kontener, a nie na okno przeglądarki, więc ten sam formularz działa w oknie dialogowym i na pełnej stronie.

Wytyczne

Kiedy używać

  • W każdym formularzu — od pojedynczego pola po wieloetapowy proces.
  • Na stronach ustawień, z polami w układzie poziomym.
  • W kartach do zaznaczenia, które działają jak przyciski radiowe lub pola wyboru.

Kiedy nie używać

  • Przy edycji w miejscu w tabelach lub paskach narzędzi, gdzie etykieta byłaby wizualnym szumem — nazwij wtedy kontrolkę przez aria-label.
  • Przy filtrach i polach wyszukiwania poza formularzem — zwykle wystarczy sam Input Group.

Jedna kolumna

Układaj formularze w jednej kolumnie. Formularze wielokolumnowe przerywają pionową ścieżkę, którą podąża wzrok, i łatwo wypełnić je w złej kolejności. Stawiaj pola obok siebie tylko wtedy, gdy stanowią całość, np. imię i nazwisko albo miejscowość i kod pocztowy.

Najpierw sprawdź, potem wyjaśnij

Waliduj przy wysłaniu formularza i po opuszczeniu pola, nigdy przy pierwszym naciśnięciu klawisza. Umieść błąd tuż pod polem, którego dotyczy, napisz, co poszło nie tak i jak to naprawić, i zachowaj to, co ktoś wpisał — nie czyść pola. W długich formularzach przenieś też fokus na pierwsze błędne pole.

Dobrze.Konkretny komunikat: mówi, co jest nie tak i jak to naprawić, a oprócz koloru pokazuje ikonę.

Nieprawidłowe dane

Źle.Ogólnikowy komunikat, a jedynym sygnałem jest kolor — dla wielu osób niewidoczny.

Opis musi zasłużyć na swoje miejsce

Dodawaj FieldDescription tylko wtedy, gdy opis wpływa na to, co ludzie zrobią: podaje format, limit albo konsekwencję. Opis, który powtarza etykietę, to szum. Jeśli każde pole potrzebuje opisu, prawdopodobnie trzeba poprawić etykiety.

Dostępność

  • Grupowanie. Field renderuje role="group"; FieldSetFieldLegend to natywne <fieldset><legend>, a technologie wspomagające ogłaszają legendę jako nazwę grupy.
  • Powiązania. Połącz każdy FieldLabel z jego kontrolką przez htmlFor/id, a opisy i błędy — przez aria-describedby.
  • Błędy są alertami. FieldError renderuje role="alert", więc jest ogłaszany w chwili pojawienia się. Gdy kilka pól ma błędy jednocześnie, rozważ zamiast tego ogłoszenie jednego podsumowania.
  • Nie tylko kolorem. Błędne pola łączą czerwone obramowanie, ikonę i komunikat tekstowy (WCAG 1.4.1).
  • Duże cele. W kartach wyboru klikalna jest cała karta — znacznie więcej niż minimalne 24 × 24 px (WCAG 2.5.8).
  • Wyłączone pola. Dodaj data-disabled do Fielddisabled do kontrolki; etykieta i opis przygasają razem z nią.

Dokumentacja API

Field

Renderuje <div role="group">. Przyjmuje wszystkie propsy div.

PropTypDomyślnie
orientation

Układa części jedna pod drugą, w jednym wierszu albo zmienia układ, gdy FieldGroup osiągnie kontenerową szerokość md.

"vertical" | "horizontal" | "responsive""vertical"
data-invalid

Oznacza pole jako błędne na potrzeby stylów i testów. Łącz z aria-invalid na kontrolce.

booleanBrak wartości domyślnej
data-disabled

Przygasza etykietę i opis. Łącz z disabled na kontrolce.

booleanBrak wartości domyślnej

FieldSet i FieldLegend

FieldSet renderuje <fieldset>, a FieldLegend — jego <legend>.

PropTypDomyślnie
variant

Tylko dla FieldLegend. Wartość legend daje tytuł sekcji w rozmiarze 16 px, a label wygląda jak etykieta pola — do małych grup.

"legend" | "label""legend"

FieldGroup

Renderuje <div>, który układa pola w stos z odstępami 20 px i tworzy kontener field-group dla responsywnych pól. Ustaw data-slot="checkbox-group", żeby uzyskać odstępy 12 px.

FieldLabel, FieldTitle i FieldContent

FieldLabel renderuje Label i przyjmuje jego propsy. Gdy owija Field, staje się kartą wyboru. FieldTitle renderuje <div> wystylizowany jak etykieta — na nagłówki kart, które nie są dostępną nazwą kontrolki. FieldContent renderuje <div>, który układa etykietę i opis jedno pod drugim.

FieldDescription

Renderuje <p> z tekstem pomocniczym w rozmiarze 13 px i kolorze muted-foreground. Linki w środku mają kolor link.

FieldError

Renderuje <div role="alert"> z ikoną ostrzeżenia. Gdy nie ma treści, nie renderuje niczego.

PropTypDomyślnie
children

Komunikat. Ma pierwszeństwo przed errors.

ReactNodeBrak wartości domyślnej
errors

Komunikaty z biblioteki do walidacji. Duplikaty są usuwane; kilka komunikatów wyświetla się jako lista.

Array<{ message?: string } | undefined>Brak wartości domyślnej

FieldSeparator

Renderuje poziomą linię między sekcjami.

PropTypDomyślnie
children

Opcjonalna etykieta pośrodku linii, np. „lub”.

ReactNodeBrak wartości domyślnej