Design system/Foundations
Spacing and layout
One 740px column, three breakpoints, and a short ruler of spacing values. Layout starts on the phone and opens up on larger screens.
In codeapp/globals.csslib/use-phone.ts
Spacing ruler
Spacing comes from a small set, and most values are multiples of 4 or 2. Small gaps keep things that belong together close, and large gaps separate groups.
4Gap between icon and counter; adjacent buttons in a stepper6Gap between icon and label in a button; gap between a menu and its field8Gap between pills in a row; page header buttons on phones10Inner padding of menu rows and sidebar items12Gap between columns in a list row16Page side margin on phones and card inner padding20Dialog and dialog header inner padding24Page margin on desktop and gap between the column and the sidebar32Confirmation dialog inner paddingPage column
Most internal app pages have a centered column with max-w-185, which is 740px (--page-col). On phones the side margin is 16px, and on desktop it's 24. The public site has a wider column.
When the sidebar is open, the body shifts only as far as the sidebar actually reaches into the column, not the full sidebar width, so the page is still centered at 1600px. A strip that needs to break out of the column, like board columns, starts exactly at the sidebar edge with .bleed-to-nav.
740pxmax-w-185 · --page-col16pxpx-424pxsm:px-684px--side-rail-w220px (200–440)--side-panel-wBreakpoints
The same number in CSS and JavaScript: PHONE_QUERY in lib/use-phone.ts is the same max-width: 640px the stylesheet uses, so they never disagree at the edge. Code is written for phones first, and sm: or min-width restores the desktop size.
≤ 640pxMenus become bottom sheets, dialogs slide up from the bottom, the dock handles navigation, and btn-sm grows to 40px.641 – 899pxStill the dock; the two-column sidebar needs about 300px that a tablet doesn't have.≥ 900pxThe two-column sidebar replaces the dock, and the page column stays centered.≥ 1000pxThe docs sidebar and this page's table of contents stay fixed beside the text.Resize the window; the indicator shows the current width.
Rules
Do
useIsPhone() for decisions CSS can't make.
Don't
window.innerWidth with a different number; it disagrees with CSS at the edge.
Do
A flex child that needs to fit has flex-1 and min-w-0.
Don't
A flex child without flex-1; it shrinks to zero and only its icon remains.
Spacing at each breakpoint
gap 8px · mt 12pxPhone: each pill flex 1 1 autogap 12pxstart 20px · bottom 20pxPhone: bottom max(16px, safe-area) + 24pxbottom max(22px, safe-area)Width up to 100vw − 32px6pxFloating gap; 8px from the screen edgepadding 20px.modal-overlay on desktopgap 12px · 16px × 10pxPhone gap 8px- The device safe area is accounted for with env(safe-area-inset-bottom) at the bottom of sheets, the phone dialog, the dock, and toasts.
- On phones, scrolling down moves the dock down 150px and fades it out (0.35 seconds) so the text underneath can be read.
Do
Side padding with px-4 sm:px-6.
Don't
A fixed 24px padding on a 360px phone.
Do
Account for the safe area at the bottom of anything pinned to the edge.
Don't
A bare bottom: 0; it slides under the iPhone home bar.
Related pages