Odstępy i układ
Siatka 4 px, krótka lista stopni, z których każdy naprawdę pracuje, i zasady układu, które utrzymują jednolitą gęstość — od formularza ustawień po pulpit.
Spójnych odstępów nie widać, kiedy działają — i od razu widać, kiedy nie działają. prfct buduje każdy komponent i wzorzec na siatce 4 px i używa celowo krótkiej listy stopni, z których każdy ma swoje zadanie.
Zaproś do zespołu
Wyślemy e-mail z linkiem.
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
function Gap({ size, px }: { size: string; px: number }) {
return (
<span
aria-hidden="true"
className="pointer-events-none absolute left-full ml-3 flex items-center gap-1.5 font-mono text-[0.625rem] text-brand-11"
style={{ height: px }}
>
<span className="h-full w-px bg-brand-7" />
{size}
</span>
)
}
export default function SpacingRhythm() {
return (
<div className="relative w-72 rounded-xl border bg-card p-6 shadow-raised">
<div className="flex flex-col gap-1.5">
<p className="text-heading-sm">Zaproś do zespołu</p>
<p className="text-sm text-muted-foreground">
Wyślemy e-mail z linkiem.
</p>
</div>
<div className="relative h-6">
<Gap size="6 · 24px" px={24} />
</div>
<div className="flex flex-col gap-2">
<Label htmlFor="rhythm-email">E-mail</Label>
<Input id="rhythm-email" placeholder="ada@acme.co" />
</div>
<div className="relative h-6">
<Gap size="6 · 24px" px={24} />
</div>
<div className="flex justify-end gap-2">
<Button variant="outline" size="sm">
Anuluj
</Button>
<Button size="sm">Wyślij zaproszenie</Button>
</div>
</div>
)
}Skala
Odstępy korzystają z numerycznej skali Tailwinda, w której jedna jednostka to 4 px (p-4 to 16 px). Dostępna jest cała skala, ale system opiera się na tych stopniach:
Zasady rytmu
Bliskość to grupowanie. Powiązane elementy stoją bliżej siebie niż niepowiązane. Etykieta jest 8 px od swojego pola, pola dzieli 20 px, a sekcje — 48 px. Jeśli dwie rzeczy potrzebują separatora, żeby wyglądać na osobne, prawdopodobnie potrzebują raczej więcej miejsca.
W środku nie więcej niż na zewnątrz. Wypełnienie wewnątrz kontenera jest najwyżej tak duże jak przestrzeń wokół niego, dzięki czemu kontener czyta się jako jedną grupę. Card ma wypełnienie 24 px, a przy size="sm" 16 px, więc karty w siatce dzieli co najmniej taki odstęp.
Używaj gap, nie marginesów. Komponenty nigdy nie ustawiają zewnętrznych marginesów; to rodzic rozmieszcza dzieci za pomocą gap we flexboksie albo gridzie. Dzięki temu komponenty pasują do każdego układu, a zapadanie się marginesów przestaje zaskakiwać.
// Preferred
<div className="flex flex-col gap-5">…</div>
// Avoid — spacing that belongs to the parent leaks into children
<div className="space-y-5">…</div>Gęstość
Przyciski mają pięć wysokości. Pola, listy wyboru i przyciski przełączające dzielą trzy środkowe, sm, default i lg, więc wszystko, co ustawisz w jednym rzędzie, wyrównuje się bez wysiłku:
Gęstość wybieraj dla całej powierzchni, a nie dla pojedynczego komponentu: strona z rozbudowaną tabelą danych może wszędzie używać sm, a ścieżka onboardingu — lg.
Układ
Breakpointy
prfct używa breakpointów w podejściu mobile-first: klasy bez prefiksu działają wszędzie, a klasy z prefiksem — od danej szerokości w górę.
Kontenery i marginesy
- Marginesy strony: 16 px na telefonach, 24 px od
sm, 32–40 px na szerokich ekranach. - Szerokość tekstu: długi tekst ma 45–75 znaków w wierszu (
max-w-prose,max-w-2xl). - Układ aplikacji: stały pasek boczny (240–260 px) i płynny obszar treści; strony z treścią są wyśrodkowane w
max-w-5xl, a strony z danymi zajmują całą szerokość.
Zapytania kontenerowe
Komponenty, które zmieniają układ w zależności od własnego rozmiaru — a nie rozmiaru okna przeglądarki — korzystają z zapytań kontenerowych. FieldGroup jest kontenerem, więc pole responsive przechodzi z układu pionowego w poziomy, gdy jego kolumna jest wystarczająco szeroka — niezależnie od tego, czy tą kolumną jest cała strona, czy wąski panel.
<FieldGroup>
<Field orientation="responsive">…</Field>
</FieldGroup>Warstwy
Kolejność nakładania to także token. Nakładki trafiają przez portal na koniec dokumentu i używają warstwy odpowiadającej ich roli, więc lista wyboru otwarta w oknie dialogowym zawsze pojawia się nad nim.