Design system/Patterns
Phone layout
The phone isn't a shrunken desktop. Below 640px, a few things change across the whole app at once, in CSS, so no page has to remember them.
In codeapp/globals.csslib/use-phone.tscomponents/floating.tsx
What changes
- Every .modal becomes a bottom sheet, with a handle and drag support.
- Every menu and picker from Floating becomes a .sheet-menu sheet with 48px rows.
- btn-sm grows to 40px, and page header pills split the line evenly.
- The page heading grows one step, and the top bar has no title.
- Buttons at the end of list rows each become a 38px chip with their own background.
- The dock at the bottom of the screen handles navigation (up to 900px).
- On touch screens, fields are 48px with a 16px font.
Sample
This same dialog opens centered on desktop; if you open this page on a phone or narrow the window, it rises from the bottom.
Rules
Do
Decisions CSS can't make use useIsPhone(): a menu or a sheet, how many previews in one cell.
Don't
A new breakpoint for a single component.
Do
Any information that appears on hover on desktop is always visible on phones.
Don't
An action that can only be found by hovering.
Do
Measure horizontal overflow with Playwright at 390 and 1280, in both directions.
Don't
Trusting a screenshot; get a number.
Phone and touch, side by side
Two separate conditions are at work: a width of 640px or less, and a coarse pointer (pointer: coarse). A touch tablet has the second and not the first; a narrow desktop window is the reverse. This table lists them separately.
≤640: 15px · coarse: 48px≤640: 40px · 13.5px · coarse in main: ≥ 44px≤640: 34px · 13.5px · coarse in main: ≥ 42px≤640: 40px · hover:none or coarse: 38px58px · coarse: 62px · 17px≤640: 46px · 16px · coarse: 48pxcoarse: 46 × 27px + invisible touch area≤640: 46px · in a sheet: 48px≤640: sheet · 88vh · 24px 24px 0 0≤640: 48px · radius 12pxRelated pages