Spacing & layout
A 4-pixel grid, a short list of steps that do real work, and layout rules that keep density consistent from a settings form to a dashboard.
Consistent spacing is invisible when it works and obvious when it doesn't. prfct builds every component and pattern on a 4px grid and uses a deliberately short list of steps, each with a job.
Invite teammate
They'll get an email with a link.
The scale
Spacing uses Tailwind's numeric scale, where one unit is 4px (p-4 is 16px). The whole scale is available, but these steps carry the system:
Rules of rhythm
Proximity is grouping. Related elements sit closer together than unrelated ones. A label is 8px from its input; fields are 20px apart; sections are 48px apart. If two things need a divider to look separate, they probably need more space instead.
Inside smaller than outside. Padding inside a container is smaller than the space around it. A card with 20px padding sits in a grid with 24px gaps or more.
Use gap, not margins. Components never set outer margins; parents arrange children with flex or grid gap. This keeps components reusable in any layout and removes margin-collapsing surprises.
// 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>Density
Controls share five heights, so anything placed on a row aligns without effort:
Choose a density per surface, not per component: a data-heavy table page may use sm throughout; an onboarding flow may use lg.
Layout
Breakpoints
prfct uses mobile-first breakpoints: unprefixed classes apply everywhere, prefixed ones apply from that width up.
Containers and gutters
- Page gutters: 16px on phones, 24px from
sm, 32–40px on wide screens. - Reading width: long text is capped at 65–75 characters (
max-w-prose,max-w-2xl). - App shell: a fixed sidebar (240–260px) and a fluid content area; content pages center at
max-w-5xl, data pages use the full width.
Container queries
Components that change layout based on their own size — not the viewport — use container queries. FieldGroup is a container, so a responsive field switches from stacked to side-by-side when its column is wide enough, whether that column is a full page or a narrow sheet.
<FieldGroup>
<Field orientation="responsive">…</Field>
</FieldGroup>Layering
Stacking is a token, too. Overlays portal to the end of the document and use the layer that matches their role, so a select opened inside a dialog always appears above it.