mobile-adapt
Mobile Adapt
Goal
Adapt existing interfaces to work well on phones without removing core functionality. Prioritize iPhone-class mobile web unless the user names another target.
Default target
If the user does not specify a device or browser, optimize first for:
- iPhone Pro Max class in portrait and landscape
- iOS Safari
- compact touch screens around
390-440 CSS pxwide - keyboard-open use, large text, safe areas, and reduced motion
When to research
This skill depends on current platform behavior.
- Browse official sources when the user asks for the latest/current guidance, iPhone-specific behavior, Safari/WebKit quirks, safe-area behavior, PWA/home-screen behavior, or direct citations.
- Prefer Apple Developer, WebKit, W3C/WCAG, MDN, Android Developers, and web.dev.
- Use absolute dates when citing recent platform changes.
For the current high-signal rules and sources, read references/mobile-ui-2026.md.
Core rules
-
Keep compact phones single-pane. Do not switch to desktop IA just because a landscape phone crosses a generic breakpoint. A phone rotated sideways is still held in two hands at arm's length — the user's mental model is "phone," not "tablet." Breaking into a multi-pane layout creates a disorienting experience where nothing is big enough to use comfortably.
-
Use bottom-first mobile navigation for true top-level sections only. Keep it to
3-5primary destinations; move overflow toMore, a sheet, or secondary flows. The bottom of the screen is the thumb's natural resting zone — putting primary nav there means the most common actions require the least effort. More than 5 items shrink tap targets below usable size and overwhelm scanning. -
Every interactive target should be at least
44x44 CSS pxin both dimensions. This includes secondary elements that are easy to overlook: inline action buttons (edit, delete, view), expand/collapse toggles, filter chips, icon buttons in toolbars, close buttons on sheets, and checkbox/radio rows (use the<label>as the full tap target, not the input alone). Capacitive touchscreens register the center of a finger's contact patch at roughly 44pt — smaller targets cause mispress frustration, especially in motion. Common Tailwind classes that fall short:h-8(32px),h-9(36px),p-1on an icon (~24px),p-1.5(~28px),p-2(~32px). Useh-11(44px),min-h-[44px], or equivalent as the baseline for every tappable element. This aligns with Apple HIG and exceeds WCAG 2.2's 24px minimum. -
Do not rely on hover, tiny icon taps, or hidden row actions. There is no hover state on touch — anything that requires hover to discover is invisible. Use visible controls, swipe actions with a visual hint, or a contextual sheet/menu.
-
Avoid horizontal page scroll. One-handed phone use is vertical thumb scrolling. Horizontal scroll breaks the user's spatial model and hides content off-screen with no visible affordance. Reflow, stack, paginate, or locally scroll tables and charts instead of the whole page.
-
Use
viewport-fit=cover,env(safe-area-inset-*), andmin-height: 100dvh. Modern iPhones have rounded corners, the Dynamic Island, and the home indicator — all of which eat into the rectangular viewport. Without explicit safe-area handling, content gets clipped behind hardware chrome.dvhaccounts for the collapsing Safari toolbar that oldvhdoes not. -
Keep primary actions clear of browser bars, the home indicator, rounded corners, and the on-screen keyboard. A "Submit" button hidden behind the keyboard or overlapping the home indicator is functionally broken — the user either can't see it or can't tap it without triggering a system gesture.
-
Prefer one main scroll container for form-heavy or keyboard-heavy flows. Nested scroll regions on iOS Safari create scroll-trapping: the user's thumb scrolls the inner container when they intended to scroll the page, or vice versa. WebKit has had persistent bugs here (see reference file). Use nested scroll only when there's no alternative.
-
Use semantic mobile inputs. Labels above fields (not beside — there's no horizontal space), correct
type,inputmode,autocomplete, andenterkeyhint. These attributes control which keyboard appears —inputmode="decimal"shows the number pad for currency,enterkeyhint="next"lets users tab through a form without dismissing the keyboard. -
Treat large text as real layout input. iOS users can set system-wide larger text, and many do. Wrap and reflow — do not hide key meaning behind truncation. If a truncated label makes the user guess whether they're looking at "Savings Account" or "Salary Account," the layout has failed.
-
On phones, charts support the story; they are not the whole story. A phone screen can render maybe 5-6 legible bars or one donut. That's not enough data density for the chart to be self-explanatory. Lead with the headline number and a text summary; use the chart as visual reinforcement.
-
Preserve the product's existing brand and design system unless the user asks for a broader redesign. Mobile adaptation is about layout and interaction, not reskinning.
Examples
Desktop sidebar nav → mobile bottom tab bar
Apple HIG (confirmed current as of WWDC 2024/2025) recommends 3–5 tabs on iPhone, bottom-positioned, with glyph icons and required labels. The bottom bar remains the standard on iPhone — the floating top-bar redesign in iPadOS 18 is iPad-only and not applicable to mobile web.
Before — a sidebar that works on desktop but steals half the phone screen:
<aside className="w-64 fixed left-0 top-0 h-full border-r">
<nav className="flex flex-col gap-2 p-4">
<Link href="/dashboard">Dashboard</Link>
<Link href="/transactions">Transaksjoner</Link>
<Link href="/accounts">Kontoer</Link>
<Link href="/categories">Kategorier</Link>
<Link href="/settings">Innstillinger</Link>
</nav>
</aside>
After — bottom tab bar with safe-area padding and 44x44 tap targets (WCAG 2.5.5 AAA, Apple HIG minimum). The env(safe-area-inset-bottom) value is ~34px on Face ID iPhones for the home indicator, but returns 0 without viewport-fit=cover in the viewport meta tag. Each NavTab renders at least h-14 (56px) to clear the 44px minimum with comfortable padding:
<nav className="fixed bottom-0 left-0 right-0 z-50 border-t bg-white
pb-[env(safe-area-inset-bottom)] md:hidden">
<div className="flex items-center justify-around h-14">
<NavTab href="/dashboard" icon={HomeIcon} label="Hjem" />
<NavTab href="/transactions" icon={ListIcon} label="Transaksjoner" />
<NavTab href="/accounts" icon={WalletIcon} label="Kontoer" />
<NavTab href="/categories" icon={TagIcon} label="Kategorier" />
</div>
</nav>
{/* Main content gets bottom padding to clear tab bar + safe area */}
<main className="pb-20 md:pb-0 md:ml-64">...</main>
Dense data table → card list on phones
When a 5-column table renders on a 390px screen, columns get squeezed below readable width or force horizontal document scroll. The card pattern preserves all data while using vertical space, which phones have in abundance.
Before — a multi-column table that forces horizontal scroll:
<table className="w-full">
<thead><tr>
<th>Dato</th><th>Beskrivelse</th><th>Kategori</th><th>Belop</th><th>Konto</th>
</tr></thead>
<tbody>{rows.map(r => (
<tr><td>{r.date}</td><td>{r.desc}</td><td>{r.cat}</td><td>{r.amount}</td><td>{r.account}</td></tr>
))}</tbody>
</table>
After — stacked cards that use vertical space and remain scannable. The amount gets tabular-nums and whitespace-nowrap so financial figures stay aligned and never break mid-number. If the original table must remain for accessibility (e.g., screen reader users who benefit from row/column association), keep it in the DOM and toggle visibility at the breakpoint rather than removing it:
<ul className="divide-y md:hidden" role="list">
{rows.map(r => (
<li key={r.id} className="flex items-start justify-between gap-3 px-4 py-3
min-h-[44px]" role="listitem">
<div className="min-w-0 flex-1">
<p className="font-medium truncate">{r.desc}</p>
<p className="text-sm text-muted">{r.date} · {r.cat}</p>
</div>
<span className="tabular-nums font-medium whitespace-nowrap">
{formatNOK(r.amount)}
</span>
</li>
))}
</ul>
Form with keyboard-aware submit
On iOS Safari, when the keyboard opens the Layout Viewport stays the same size but the Visual Viewport shrinks. Elements with position: fixed; bottom: 0 stay anchored to the Layout Viewport and disappear behind the keyboard (WebKit bug #202120 confirms position: sticky; bottom: 0 also fails to pin when the keyboard is open). The safest pattern is to let the submit button scroll naturally within the form flow, or use position: sticky with a visualViewport resize listener as a fallback.
Use inputmode="decimal" instead of type="number" for currency — it shows the decimal keyboard without the spinner arrows, which are a mobile UX antipattern for money fields. Chain enterkeyhint="next" on intermediate fields so users can advance without dismissing the keyboard, and enterkeyhint="done" on the last field.
Before — submit button at the bottom of a long form, hidden when the keyboard opens:
<form className="space-y-4 p-4">
<Input label="Belop" type="number" />
<Input label="Beskrivelse" />
<Select label="Kategori" options={categories} />
<Button type="submit">Lagre</Button>
</form>
After — inline submit that scrolls with form content, staying naturally reachable. The outer container uses min-h-[100dvh] (dvh tracks the visual viewport including keyboard, unlike old vh):
<form className="flex flex-col min-h-[100dvh]">
<div className="flex-1 space-y-4 p-4">
<Input label="Belop" type="text" inputMode="decimal"
enterKeyHint="next" autoComplete="off" />
<Input label="Beskrivelse" enterKeyHint="next" autoComplete="off" />
<Select label="Kategori" options={categories} />
</div>
<div className="sticky bottom-0 p-4 border-t bg-white
pb-[max(1rem,env(safe-area-inset-bottom))]">
<Button type="submit" className="w-full h-12">Lagre</Button>
</div>
</form>
Workflow
- Lock target context.
- device, browser, orientation, usage context
- default to iPhone Pro Max + iOS Safari if unspecified
- Audit the shell first.
- navigation mode
- safe areas
- sticky/fixed bars
- landscape behavior
- keyboard behavior
- Audit each screen or component.
- lists and rows
- forms
- tables
- charts
- empty/error/loading states
- Choose the hard cutover.
- what becomes single-column
- what becomes cards
- what moves into a sheet/drawer
- what becomes secondary under
More
- Implement mobile-first adaptations.
- prefer container queries for component internals
- prefer viewport queries for shell/layout mode changes
- do not keep old half-desktop/half-mobile behavior unless explicitly required
- Verify on compact portrait and compact landscape.
- Safari/WebKit first
- then Chromium
- Report the highest-risk issues first, then the concrete changes and any remaining gaps.
Screen Checklist
Shell and navigation
- Keep landscape phones in the phone shell.
- Avoid hard
md => desktop sidebarswitches for devices that are still phones. - Pad bottom UI with
env(safe-area-inset-bottom). - Make sticky actions sit above bottom nav.
- Keep mobile labels short.
Lists and rows
- Let titles, metadata, and amounts wrap into two rows when needed.
- Reserve stable space for prices, balances, and totals.
- Prefer stacked metadata over squeezed inline metadata.
- Expose actions with visible controls or a sheet/menu, not hover.
- Size row action buttons (edit, delete, view, expand) to at least
44x44 CSS px— not just primary nav and toolbar buttons.
Forms
- Use one column on compact phones.
- Keep submit visible above the keyboard.
- Use a clear full-width primary CTA.
- Break long flows into steps or progressive disclosure.
- Avoid
type="number"for currency if formatting or precision matters.
Tables and import/review flows
- Convert dense editable tables to cards/lists on phones.
- If a true table must remain, isolate horizontal scroll to the table only.
- Keep sticky headers and toolbars from obscuring focused inputs.
Charts and stats
- Lead with the key number and short narrative.
- Add compact legends and text fallback.
- Prefer horizontal scroll or paged comparisons over unreadable dense bars.
- Avoid tiny pies/donuts on compact screens.
Verification
For each check below, confirm the specific pass condition and use the suggested tooling. Real device testing is the gold standard — simulators are useful for iteration but cannot fully reproduce keyboard timing, scroll inertia, or touch precision.
-
Portrait and landscape — Phone stays in the mobile shell in both orientations. No layout breakage, no content overflow, no sudden switch to a desktop sidebar or multi-pane view. Test at 390px (iPhone Pro) and 430px (iPhone Pro Max) portrait, and their landscape equivalents. Use Chrome DevTools device toolbar or Xcode Simulator.
-
iOS Safari safe areas — Content is not clipped by the Dynamic Island, rounded corners, or home indicator. Bottom-fixed elements have visible
env(safe-area-inset-bottom)padding (~34px on Face ID iPhones). Verify thatviewport-fit=coveris set in the viewport meta tag — without it, allenv(safe-area-inset-*)values return0. Best tested in Xcode Simulator (accurate per-device insets) or Polypane 26+ (desktop browser with real safe-area inset presets). Chrome DevTools 135+ supportsEmulation.setSafeAreaInsetsOverridebut requires manual configuration. -
Keyboard-open form flow — Focus each input in sequence and verify: (1) the focused field scrolls into view, (2) the submit action is visible and tappable, (3) sticky headers don't obscure the focused field. Be aware that
position: fixed; bottom: 0elements hide behind the iOS keyboard (the Layout Viewport doesn't shrink), andposition: sticky; bottom: 0also fails to pin during keyboard display (WebKit bug #202120). Playwright and Cypress cannot trigger real on-screen keyboards — this requires real device testing or Xcode Simulator with Safari Web Inspector. -
Large text / display zoom — WCAG 1.4.4 (AA) requires text resizable to 200% without loss of content. WCAG 1.4.10 (AA) requires reflow at 400% zoom (equivalent to 320px-wide viewport) without horizontal scroll. Test by: (1) setting browser zoom to 200% and verifying no truncation or overlap, (2) setting viewport to 1280px and zooming to 400% to simulate the 320px reflow requirement, (3) enabling iOS Settings > Accessibility > Display & Text Size > Larger Text at maximum. Verify the viewport meta tag does not set
user-scalable=noormaximum-scalebelow 2.0. -
Reduced motion — Animations respect
prefers-reduced-motion: reduce. Transitions still communicate state changes but use crossfades or opacity rather than motion. iOS Safari does not automatically suppress web animations when Reduce Motion is on — the developer must handle it. Test via: Chrome DevTools Rendering tab > "Emulate prefers-reduced-motion", Safari Web Inspector preference overrides (available since late 2022), or Playwright'spage.emulateMedia({ reducedMotion: 'reduce' }). -
Empty/error/loading states — These states render properly on the compact screen. Loading skeletons match the mobile layout, not the desktop one. Error messages are readable without scrolling.
-
Standalone/PWA mode (when relevant) — If the app supports
display: standalone, verify that the status bar area, home indicator, and gesture regions are handled.svhanddvhresolve to the same value in standalone mode since there's no collapsible toolbar (WebKit bug #261185). WebKit bug #254868 can cause incorrect height values. WebKit bug #265578 causes visual viewport height to update late when Safari UI animates back into view. -
No horizontal document scroll — Swipe left/right on the page body produces no movement. Any horizontal scroll is isolated to a specific container (table, carousel) with visible overflow affordance and
overscroll-behavior: containto prevent triggering Safari UI shifts (WebKit bug #240861). For automated verification in Playwright:const hasOverflow = await page.evaluate(() => document.documentElement.scrollWidth > document.documentElement.clientWidth ); expect(hasOverflow).toBe(false);
Deliverable
When using this skill, produce:
- a short mobile adaptation plan
- the highest-risk issues first
- concrete per-screen changes
- verification performed
- any remaining device/browser uncertainty
More from s0up4200/skills
claude-code-docs
Search and reference official Claude Code documentation locally. Use whenever the user asks about Claude Code features, configuration, hooks, MCP, settings, permissions, CLI flags, IDE integrations, plugins, skills, agent teams, scheduled tasks, sub-agents, custom agents, worktrees, session management, Desktop app features, or any other Claude Code capability. Also use when you need to look something up yourself mid-task — checking hook schemas, CLI flags, environment variables, settings format, etc. Prefer this over web fetching for Claude Code docs. Claude Code evolves fast — features like custom agents (.claude/agents/), session forking (/branch), /batch, live app preview, and worktree support are newer and not reliably in model training data, so always check the docs rather than guessing.
7release-tag
Create and push a signed git release tag. Use this skill when the user says "tag a release", "new release", "cut a release", "bump version", "release tag", "tag version", "push a tag", or any variation of wanting to create a new version tag. Also use when the user types "/release-tag" or "/release".
6retro
>-
3platform-docs
Search and reference official Anthropic platform documentation locally. Use whenever the user asks about the Claude API, Anthropic SDKs (Python, TypeScript), REST API endpoints, models, pricing, tool use, computer use, MCP server protocol, prompt engineering, extended thinking, prompt caching, vision, batch processing, Agent SDK, embeddings, streaming, structured outputs, citations, context windows, token counting, files API, PDF support, multilingual support, model deprecations, rate limits, error codes, or any other Anthropic platform capability. Also use when you need to look up API details yourself mid-task — checking request/response schemas, SDK method signatures, authentication, or endpoint parameters. Prefer this over web fetching for Anthropic API and platform docs. NOT for Claude Code (the CLI tool) — use claude-code-docs for that.
2libtorrent
Answer questions about the libtorrent C++ BitTorrent library using a local scraped mirror of libtorrent.org docs and blog posts. Use this skill whenever the user asks about libtorrent settings, APIs, performance tuning, building libtorrent, the piece picker, DHT, BitTorrent v2, alerts, torrent_handle, session configuration, or any other libtorrent-specific topic. Also trigger when the user mentions libtorrent by name, references libtorrent classes like session, torrent_handle, settings_pack, or asks about BitTorrent implementation details that libtorrent covers. Invoke via /libtorrent or automatically when the context matches.
1emotional-awareness
>-
1