Foundation
Responsive
The rule
One codebase, two breakpoints, and nothing is done until it has been seen at both.
BTS ships desktop and mobile from the same components. There is no separate mobile app and no separate mobile stylesheet — there is one component with a max-md: / md: pair on the properties that must change.
430px
Mobile
The width the mobile conventions are written against.
768px
md — the flip
Stock Tailwind md, and it must stay stock.
1440px
Desktop
The width desktop sign-off happens at.
See it
Switch this preview between Mobile and Desktop. The control physically changes size — that is the rule, rendered.
Button across both breakpoints
Three of the four sizes go to a 48px tap target below md, and the label flips from 12px to 14px.
Why the preview is an iframe
A 430px div inside a 1440px window still renders the desktop tree, because these utilities respond to the viewport and not to their container. A reviewer looking at that div would be signing off something that never ships. An iframe gets its own viewport, so the mobile toggle shows the real mobile component.
The JavaScript hazard
21 components branch on a hook rather than on CSS, and that branch behaves differently.
useIsMobile() is a 768px matchMedia hook that returns false on first render. Components using it paint the desktop tree and then correct themselves — which is a flash in production and a wrong snapshot in review.
Both trees must ship, and each of those components takes an isMobile / forceMobile override so either can be rendered deliberately at any viewport. Where a CSS pair will do the job, use the CSS pair.
Sign-off checklist
What 'done' means for anything carrying a responsive pair.
- 01Rendered at 430px and at 1440px, both captured.
- 02Tap targets are at least 48px below md — the sole known exception is RadioGroup, which has no mobile bump and is logged as a defect.
- 03The type flip is present: text-sm below md, text-xs at and above it.
- 04Any max-md:hidden / md:hidden pair has both branches reviewed, not just the one the window happened to show.
- 05If the component uses useIsMobile(), both trees are reachable through a prop and both were looked at.
Known gap: the breakpoint is not pinned
This entire contract rides Tailwind's default md of 768px, but the preset only extends the theme and never fixes `screens`. A consumer that overrides them silently breaks every component in the system. Recorded as audit gap #5.