Making a Web App Feel Native on a Phone
The gap between a web app and a native app is mostly a handful of details — touch targets, momentum, safe areas and instant feedback. None of them are hard.
Users cannot tell you why an app feels cheap, but they feel it instantly. It is almost never one big thing — it is a stack of small ones.
Touch Targets: 44px Minimum
Apple's guideline is 44×44pt, Google's is 48dp. A 30px icon button that works fine with a mouse is genuinely frustrating with a thumb. Give small controls invisible padding rather than shrinking them to fit.
Kill the Tap Highlight and the 300ms Delay
* { -webkit-tap-highlight-color: transparent; }
html { touch-action: manipulation; }
The grey flash on tap is the single most obvious "this is a website" tell.
Respect the Safe Areas
.navbar { padding-top: env(safe-area-inset-top); }
.bottom-bar { padding-bottom: env(safe-area-inset-bottom); }
Without this your header sits under the notch and your bottom bar under the home indicator.
Give Feedback Within 100ms
Every tap needs a visible response immediately, even if the real work takes a second. An :active state, a spinner in the button, a disabled state — anything that says "received".
Skeletons Beat Spinners
A skeleton that matches the shape of the incoming content feels faster than a spinner, because the layout does not jump when data arrives.
Momentum Scrolling and No Overscroll Chaining
.scroll-area {
overflow-y: auto;
-webkit-overflow-scrolling: touch;
overscroll-behavior: contain;
}
overscroll-behavior: contain stops a scrolled modal from dragging the page behind it — a classic hybrid-app annoyance.
Prevent the iOS Zoom-on-Focus
iOS zooms in when a focused input has a font size below 16px. Set inputs to at least 16px and the zoom disappears.
Add Haptics Where They Belong
In a Capacitor app, a light impact on a successful submit costs one line and adds a lot of perceived polish. Use it sparingly — on confirmations, not on every tap.