Przejdź do treści

Kompozycja

Jak łączą się komponenty prfct — części, prop render, atrybuty data do stylowania i zasady, dzięki którym własne modyfikacje da się utrzymać.

Komponenty prfct to małe części, które składa się w całość, a nie konfigurowalne monolity. Okno dialogowe to Dialog, DialogTrigger, DialogContentDialogHeader — każda z tych części to cienkie, ostylowane opakowanie, które przeczytasz w minutę.

Części zamiast propsów

<Card>
  <CardHeader>
    <CardTitle>Team members</CardTitle>
    <CardDescription>Invite people to collaborate.</CardDescription>
    <CardAction>
      <Button size="sm">Invite</Button>
    </CardAction>
  </CardHeader>
  <CardContent></CardContent>
  <CardFooter></CardFooter>
</Card>

Dzięki częściom API pozostaje małe, a każdy element możesz przestawić, pominąć lub opakować — komponent nie musi dostawać nowego propa na każdą okoliczność.

Prop render

Komponenty Base UI renderują rozsądny element domyślny. Żeby wyrenderować coś innego — własny komponent albo link routera — przekaż to w render. Zachowanie, dostępność i style zostaną z nim scalone.

<DialogTrigger render={<Button variant="outline" />}>Edit profile</DialogTrigger>

<BreadcrumbLink render={<Link href="/projects" />}>Projects</BreadcrumbLink>
Przesiadasz się z Radix?
Radix używa asChild, a Base UI — render. Gdy prymityw przycisku renderuje element, który nie jest przyciskiem, ustaw nativeButton={false} — Base UI doda wtedy do niego role="button" i aktywację z klawiatury.

Nawigacja należy do linku, nawet jeśli wygląda on jak przycisk. Nie renderuj Button jako linku — Base UI oznaczyłby go jako przycisk. Zamiast tego nałóż buttonVariants na link:

import { buttonVariants } from "@/components/ui/button"

<Link href="/pricing" className={buttonVariants({ variant: "outline" })}>
  See pricing
</Link>

Stylowanie atrybutami data

Każda część ma atrybut data-slot, a części interaktywne udostępniają swój stan w atrybutach data (data-open, data-checked, data-disabled, data-highlighted…). Możesz się do nich odwołać w stylach rodzica, nie dotykając samego komponentu:

<Field className="*:data-[slot=field-description]:text-xs"></Field>
<SelectItem className="data-highlighted:bg-brand-3"></SelectItem>

Dostosowywanie

Sięgaj po te sposoby w podanej kolejności:

  1. Warianty i rozmiary. variant="outline", size="sm".
  2. Układ przez className. Szerokość, marginesy, wyrównanie — o układzie decyduje rodzic.
  3. Nowy wariant. Jeśli potrzebujesz nowego wyglądu, dodaj go do definicji cva komponentu — wtedy jest wspólny, udokumentowany i spójny.
  4. Edytuj kod źródłowy. To twój kod. Zachowaj tokeny, style fokusu i atrybuty data-slot.
Dobrze.className do układu, warianty do wyglądu.
Źle.Nadpisywanie kolorów w pojedynczych miejscach rozbija spójność systemu.

Łączenie klas

cn() łączy nazwy klas i rozwiązuje konflikty Tailwinda, więc className przekazany do komponentu w przewidywalny sposób nadpisuje jego ustawienia domyślne. cn() w prfct zna własne tokeny systemu — text-heading-md, shadow-floating, duration-fast — które ogólne narzędzie do łączenia klas źle by sklasyfikowało i po cichu odrzuciło.

import { cn } from "@/lib/utils"

cn("text-heading-md text-foreground", "text-muted-foreground")
// → "text-heading-md text-muted-foreground"