Help us improve
Share bugs, ideas, or general feedback.
From product-playbook-for-agentic-coding
Patterns for debugging mobile-specific issues on iOS Safari and Android Chrome. Use this skill when encountering viewport, keyboard, or touch-related bugs that only reproduce on real mobile devices. Don't use for general debugging (use /playbook:debug instead), or for desktop browser issues.
npx claudepluginhub daviswhitehead/product-playbook-for-agentic-coding-plugin --plugin product-playbook-for-agentic-codingHow this skill is triggered — by the user, by Claude, or both
Slash command
/product-playbook-for-agentic-coding:mobile-debuggingThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill provides patterns for debugging mobile-specific issues, particularly viewport and keyboard handling differences between iOS Safari and Android Chrome.
Guides technical evaluation of code review feedback: read fully, restate for understanding, verify against codebase, respond with reasoning or pushback before implementing.
Share bugs, ideas, or general feedback.
This skill provides patterns for debugging mobile-specific issues, particularly viewport and keyboard handling differences between iOS Safari and Android Chrome.
Use this skill when:
Many mobile bugs cannot be reproduced in:
Always test on real physical devices for:
iOS Safari and Android Chrome handle viewports completely differently:
| Behavior | Android Chrome | iOS Safari |
|---|---|---|
interactiveWidget: resizes-content | ✅ Resizes viewport | ❌ Ignored |
100dvh on keyboard open | ✅ Shrinks correctly | ⚠️ Layout viewport unchanged |
scrollIntoView() with keyboard | ✅ Works immediately | ❌ Needs 350ms delay |
| Keyboard dismiss detection | Via resize event | Via focusout/blur |
| Overscroll prevention | overscroll-behavior works | May need additional handling |
Key Implication: CSS-only solutions (dvh units, viewport meta) work on Android but require JavaScript workarounds on iOS Safari.
Symptom: When focusing an input, the keyboard covers it instead of scrolling into view.
Root Cause: iOS Safari doesn't resize the layout viewport when the keyboard appears.
Solution: Add scrollIntoView on focus with delay:
const handleFocus = () => {
// Wait for iOS keyboard animation (~300ms)
setTimeout(() => {
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
}, 350);
};
Apply to: All inputs that could be near the bottom of the screen (chat inputs, login forms, comment boxes).
Symptom: User can scroll past content to reveal gray/white space below.
Root Cause: Usually a wrapper element with min-h-screen or min-height: 100vh that extends beyond the viewport.
Debugging Steps:
layout.tsx or root layout for wrapper divsmin-h-screen, min-h-full, or min-height propertiesoverflow: auto instead of overflow: hiddenSolution:
/* globals.css */
html, body {
height: 100%;
overflow: hidden;
overscroll-behavior: none;
}
And: Remove wrapper divs with min-h-screen. Let pages handle their own height with h-dvh.
Symptom: Components don't fill available vertical space properly.
Root Cause: Broken flex layout cascade - using h-full instead of flex-1.
Solution: Ensure flex layout cascades through component tree:
// ❌ Wrong
<View className="flex-1">
<ChildComponent className="h-full" /> {/* Won't fill properly */}
</View>
// ✅ Correct
<View className="flex-1 flex flex-col">
<ChildComponent className="flex-1" /> {/* Fills remaining space */}
</View>
Symptom: After deleting all text (select all + delete), textarea stays expanded.
Root Cause: scrollHeight doesn't immediately reflect empty content.
Solution: Explicitly check for empty value:
const adjustHeight = () => {
if (!value || value.length === 0) {
element.style.height = `${minHeight}px`;
return;
}
// ... normal scrollHeight calculation
};
Symptom: Page bounces when scrolling past content edges.
Solution:
body {
overscroll-behavior: none;
}
For scroll containers:
<ScrollView
bounces={false} // iOS
overScrollMode="never" // Android
/>
When encountering a mobile-specific bug:
layout.tsxmin-h-screen wrappersoverflow: hidden on html/bodyinteractiveWidget settingflex-1 not h-full)scrollIntoView with 350ms delayinteractiveWidget: resizes-contentoverscroll-behavior: none// layout.tsx
export const viewport: Viewport = {
width: 'device-width',
initialScale: 1,
maximumScale: 1, // Prevents zoom on input focus
// Android: Make keyboard resize viewport instead of overlay
interactiveWidget: 'resizes-content',
};
If your mobile fix uses requestAnimationFrame, tests may fail because Jest doesn't execute rAF callbacks synchronously.
Solution: Mock rAF in tests:
beforeEach(() => {
jest.spyOn(window, 'requestAnimationFrame').mockImplementation((cb) => {
cb(0);
return 0;
});
});
afterEach(() => {
jest.restoreAllMocks();
});
Mobile debugging requires:
scrollIntoView with delaysThis skill works with:
/playbook:debug - Use this skill during mobile debugging sessions/playbook:learnings - Capture mobile-specific fixes for future referencedebugging-agent - Provides mobile-specific debugging patternsWhen in doubt: check on a real device, check the root layout, and remember iOS Safari needs special handling.