mcpField
M
mobile-feel

mobile-feel

github.com/paragonhqDesign & UI049 installsUpdated 5w ago
Repository

About mobile-feel

mobile-feel is a guide for designing and building mobile web interfaces, covering touch, viewports, inputs, layout, and performance. It helps developers review an interface against the conditions of phone use.

Key features

  1. 01Touch target and gesture guidance
  2. 02Viewport and safe-area handling
  3. 03Keyboard and form input guidance
  4. 04Bottom sheet and navigation patterns
  5. 05Scrolling, feedback, and performance checks

Use cases

  1. 01Reviewing touch sizes and layouts on phones
  2. 02Keeping fields visible when the keyboard opens
  3. 03Planning bottom sheet and navigation interactions
  4. 04Testing mobile scrolling and load behavior

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/paragonhq/stealth-skills --skill mobile-feel

After installing, type/mobile-feelin Claude Code to confirm it loaded.

Details

From the project README

mobile-feel

A phone is not a small desktop. The pointer is a thumb, the viewport moves, the keyboard eats half the screen, and every mistake is felt in the hand.

Targets

  • 44×44px minimum for anything tappable, even if it draws at 20px. Extend the hit area with padding or a pseudo-element, not by making the icon bigger.
  • 8px minimum between adjacent targets.
  • Primary actions in the bottom third of the screen, where a thumb rests. Destructive actions away from it.
  • Rows are the target, not the text inside them.

Viewport

  • 100dvh, never 100vh. vh includes the browser chrome; dvh is what is actually visible. Use svh for things that must not resize when the chrome hides.
  • <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">. viewport-fit=cover unlocks safe-area insets. Never user-scalable=no or maximum-scale=1.
  • Safe areas on every fixed or sticky edge element:
.bottom-bar { padding-bottom: max(12px, env(safe-area-inset-bottom)); }
.top-bar { padding-top: env(safe-area-inset-top); }

Touch behaviour

* { -webkit-tap-highlight-color: transparent; }
button, a, [role="button"] { touch-action: manipulation; } /* no 300ms delay, no double-tap zoom */
.drag-handle { touch-action: none; }   /* only on the handle */
.scroll-region { overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }
  • Replace the removed tap highlight with your own :active state, or taps feel dead.
  • :hover styles stick after a tap on touch devices. Gate every hover with @media (hover: hover).
  • :active is unreliable on iOS for elements without a touchstart listener; a data attribute set on pointerdown and cleared on pointerup/pointercancel is more dependable.
  • Long-press selects text and shows a callout on links; -webkit-touch-callout: none and user-select: none on controls only, never on content.

Inputs

  • Font size 16px or larger on inputs, or iOS zooms the page on focus and does not zoom back.
  • inputmode="numeric", "email", "tel", "decimal", "search" so the right keyboard appears.
  • autocomplete values set (email, one-time-code, cc-number…). autocapitalize="none" on usernames and codes.
  • enterkeyhint="send" / "search" / "done" on the last field.
  • Focused inputs must be visible above the keyboard. Listen to visualViewport resize and scroll the field into view, or anchor the composer to visualViewport.height.
  • Fixed elements move when the keyboard opens on iOS. Anything that must stay put uses position: absolute inside a container sized to the visual viewport.

Sheets

Bottom sheets are the phone's dialog:

  • Drag to dismiss, following the finger 1:1. Release past ~35% or with downward velocity dismisses; otherwise snap back.
  • Snap points where the content has natural stops (peek, half, full).
  • The backdrop's opacity tracks the drag.
  • Scroll inside the sheet only when it is at its full snap; before that, a drag scrolls the sheet, not the content.
  • Body scroll locked without layout shift.
  • A grabber at the top, 36×5px, at --fg-4.
  • Use a library that has solved the physics (Vaul) rather than writing it.

Full-screen dialogs on a phone slide up from the bottom; on desktop they fade in centred. It is the same component with two presentations.

Bottom navigation

  • 3–5 items, icon and label, 49–56px tall plus safe area.
  • Active item in --fg, others in --fg-3. No accent fill.
  • Tapping the active item scrolls its view to top.
  • It hides on scroll-down and reappears on scroll-up only if the content needs the space; otherwise it stays.

Scroll

  • One scrolling region per screen where possible. Nested scroll on a phone is a trap.
  • Pull-to-refresh: either implement it properly or set overscroll-behavior-y: contain on the scroller so it does not fire the browser's.
  • Horizontal carousels: scroll-snap-type: x mandatory, scroll-padding for the gutters, and a visible cue that there is more.
  • Sticky headers under 56px. Anything taller steals the screen.
  • No smooth-scroll libraries on touch. Native momentum is the reference; do not compete with it.

Feedback

  • Press states are larger on touch than on desktop: scale 0.96 instead of 0.98, or a visible fill, because there is no hover to prepare the eye.
  • Haptics are unavailable on the web (navigator.vibrate is Android-only and coarse). Compensate with visual feedback on the same frame.
  • Toasts at the bottom-centre, above the bottom bar and the keyboard.
  • Loading indicators near the thumb, not at the top of a screen the thumb is covering.

Layout at 375px

  • Side gutters 16px. Never 0, never 24+.
  • One column. Cards full width with 12px radius, or edge-to-edge rows with dividers; not both.
  • Text at 15–16px body, 13px secondary. 13px body is too small for a phone held at arm's length.
  • Tables become cards or a horizontal scroll with a pinned first column. Never a squashed table.
  • Truncate less; wrap more. Names wrap to two lines on a phone.
  • Test with the largest dynamic type setting and with 200% zoom.

Performance on a real phone

  • Test on a mid-range Android, not the newest iPhone. Throttle 4× CPU in DevTools as a proxy.
  • backdrop-filter and large box-shadows are the first things to cut when scroll stutters.
  • Animate only transform and opacity. Never height, width or top on a phone.
  • Images sized for the device (srcset), lazy below the fold, decoded async.
  • First interaction ready under 2s on 4G: defer everything that is not the first screen.

PWA touches

  • manifest.webmanifest with name, icons (192, 512, maskable), display: standalone, theme_color, background_color.
  • apple-mobile-web-app-capable and the status bar style.
  • A splash that matches the app's frame colour, not white.

Checklist

  • 44px targets, 8px apart, primary actions low on the screen
  • dvh, viewport-fit=cover, safe areas on fixed edges
  • Tap highlight removed and replaced with an :active state
  • Hover gated; no sticky hover after tap
  • Inputs at 16px with inputmode, autocomplete, enterkeyhint
  • Focused field visible above the keyboard
  • Sheets drag, snap, and lock body scroll
  • One scroll region; overscroll contained
  • Toasts above the bottom bar
  • Tested on a mid-range Android with throttling

From the catalogue

Comments

Markdown supported