Przejdź do treści

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:

2pxWłoskowate odstępy, korekty ikon
4pxCiasne odstępy w linii
6pxOdstęp ikona–etykieta w małych kontrolkach
8pxOdstęp ikona–etykieta, zwarte stosy
12pxWypełnienie kontrolek, powiązane elementy
16pxWypełnienie kart (kompaktowe), stosy pól
20pxOdstępy grup
24pxWypełnienie kart, stosy sekcji
32pxOddzielone grupy
40pxMarginesy strony (desktop)
48pxOdstępy sekcji
64pxGłówne sekcje
96pxRytm sekcji marketingowych
128pxOdstępy sekcji hero

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:

RozmiarWysokośćZastosowanie
xs28 pxGęste tabele i paski narzędzi. Tylko w interfejsach obsługiwanych głównie myszą.
sm32 pxKontrolki drugorzędne, kompaktowe formularze, nagłówki kart.
default36 pxWiększość kontrolek w interfejsie produktu.
lg40 pxGłówne formularze, powierzchnie projektowane z myślą o dotyku.
xl48 pxWezwania do działania w marketingu.

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ę.

PrefiksMin. szerokośćUrządzenia
sm:40rem · 640pxDuże telefony, poziomo.
md:48rem · 768pxTablety, pionowo.
lg:64rem · 1024pxTablety poziomo, małe laptopy.
xl:80rem · 1280pxLaptopy i komputery stacjonarne.
2xl:96rem · 1536pxDuże monitory.

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.

Tokenz-indexZastosowanie
z-base0Przepływ dokumentu.
z-raised10Przyklejone nagłówki tabel, pływające szyny akcji.
z-sticky20Przyklejone nagłówki stron i paski narzędzi.
z-overlay40Przyciemnienia modali.
z-modal50Okna dialogowe, panele, szuflady.
z-popover60Menu, popovery, listy wyboru (nad modalami).
z-toast70Powiadomienia.
z-tooltip80Podpowiedzi — zawsze na wierzchu.