DEV Community

Joseph Anady
Joseph Anady

Posted on • Originally published at thatdevpro.com

Mobile SEO + mobile-first indexing

Originally published at thatdevpro.com. Part of ThatDevPro's open SEO + AI framework library. Open-source AI citation toolkit: github.com/Janady13/aio-surfaces.


Mobile First Indexing, Mobile Core Web Vitals, Touch Patterns, Foldable Devices, Apple Intelligence on iOS 19, Mobile Crawler Behavior, and Self Hosted Mobile Testing

A comprehensive installation and audit reference for mobile SEO in 2026. Mobile is no longer a special case or a secondary surface. Per StatCounter Global Stats April 2026 data (rolling 30 day sample across 3.1 million tracked sites), mobile generates 67.4 percent of global web traffic, tablets contribute another 1.9 percent, and desktop has dropped to 30.7 percent. AI search assistants on mobile (ChatGPT iOS app, Perplexity iOS and Android, Google AI Mode mobile interface, Apple Intelligence Spotlight Web) are launched from mobile devices 82.1 percent of the time per Similarweb Q1 2026 mobile app analytics. Mobile is the primary surface for both classic search and AI search. Dual purpose: installation manual and audit document.

Cross stack implementation note: the code samples in this framework are written in plain HTML for clarity. For React, Vue, Svelte, Next.js, Nuxt, SvelteKit, Astro, Hugo, 11ty, Remix, WordPress, Shopify, and Webflow equivalents of every pattern below, see framework-cross-stack-implementation.md. For pure client rendered SPAs (no SSR/SSG) see framework-react.md. For Tailwind specific concerns (purge, dynamic classes, dark mode CLS, focus accessibility) see framework-tailwind.md.


1. Document Purpose

This is the canonical 2026 reference for mobile SEO. Where framework-pageexperience.md covers Core Web Vitals as a cross device discipline, this document covers the mobile specific layer: foldable posture queries, iOS 19 Apple Intelligence registration, Googlebot Smartphone behavior in server logs, mobile schema cards, and the touch patterns that distinguish a thumb workable site from a desktop port. Where framework-localseo.md covers local search business, this document covers the mobile click to call, click to map, click to text patterns local search depends on. Where framework-aso.md covers App Store and Play Store optimization, this document covers the mobile web surface and the seams where mobile web meets native (App Intents, App Links, Universal Links, Smart Banners).

Mobile is not a special case in 2026. The conversation has shifted from "make sure the desktop site works on mobile" to "design for mobile first and progressively enhance for desktop". From "mobile Core Web Vitals must pass" to "mobile Core Web Vitals must pass under realistic field conditions including 5G uplink congestion, foldable layout changes, and AI assistant rendering paths".

What is unchanged: responsive design remains the canonical pattern. Separate m.example.com sites are a discontinued pattern. AMP is obsolete. Mobile parity with desktop is a hard requirement for indexation. Touch targets must be tap workable.

What is new in 2026: container queries replace media queries at the component level. Foldable devices require CSS posture queries (device-posture: folded and device-posture: continuous). Apple Intelligence on iOS 19 (shipped September 2025, point release 19.2 January 2026) indexes mobile web content through Spotlight Web and surfaces it through Siri. App Intents for app deep linking benefit mobile web sites with companion apps. Googlebot Smartphone has been the only Googlebot user agent for fresh crawls since 2023. AI crawlers (OAI SearchBot, PerplexityBot, ClaudeBot, Bingbot in AI mode) are all mobile aware.

1.1 Three Operating Modes

Mode A, Install Mode. Build mobile first infrastructure on a new or existing site. Follow Sections 2 through 14 in order.

Mode B, Audit Mode. Evaluate an existing site for mobile SEO posture. Skip to Section 14.

Mode C, Hybrid Mode. Audit first, then install for failing items.

1.2 Required Tools

  • Google Search Console URL Inspection tool (the canonical replacement for the deprecated Mobile Friendly Test)
  • Chrome DevTools Device Mode for lab simulation
  • Playwright with mobile emulation profiles (devices['iPhone 15 Pro'], devices['Pixel 8'], devices['Galaxy S24 Ultra'])
  • Real device pool: at minimum one current iPhone, one current Pixel or Galaxy, one foldable, one tablet
  • A self hosted Selenium Grid for headless cross device testing on a Debian or Ubuntu host
  • Server log access for crawler verification (nginx access.log or equivalent)
  • A self hosted RUM endpoint for field data collection (web-vitals JS library posting to your own nginx logging endpoint)

1.3 Relationship to Neighboring Frameworks

Concern Framework
Core Web Vitals across devices framework-pageexperience.md
HTTP/3, nginx tuning, TLS, crawler access framework-technicalseo.md
Touch and screen reader accessibility (WCAG 2.2) framework-accessibility.md
App Store and Play Store optimization framework-aso.md
Local pack, Apple Maps, Google Maps cards framework-localseo.md
UX foundations, IA, task flow framework-uxseo.md
LocalBusiness, MobileApplication, SpeakableSpecification framework-schema.md
Siri, Apple Intelligence, ChatGPT Voice, Google Assistant framework-voicesearch.md
AI citation surfaces (ChatGPT, Claude, Perplexity, Copilot) framework-aicitations.md
Google AI Overviews and AI Mode framework-aioverviews.md
Image search, lens search, mobile camera search framework-multimodalsearch.md

2. Client Variables Intake

# MOBILE SEO FRAMEWORK CLIENT VARIABLES

# --- Site Identity ---
business_name: ""
primary_domain: ""
nginx_host_path: ""                    # /var/www/sites/[domain]/
companion_app_ios_bundle_id: ""
companion_app_android_package_id: ""

# --- Mobile Traffic Baseline ---
mobile_traffic_percent_ga4: 0          # GA4 last 90 days
mobile_organic_clicks_last_28_days: 0  # GSC mobile filter
mobile_organic_impressions_last_28_days: 0

# --- Mobile Core Web Vitals Field Data ---
mobile_lcp_p75_ms: 0
mobile_inp_p75_ms: 0
mobile_cls_p75: 0
mobile_ttfb_p75_ms: 0

# --- Responsive Posture ---
viewport_meta_present: false
viewport_meta_user_scalable_disabled: false   # red flag if true
responsive_pattern: ""                 # responsive | separate_m_dot | dynamic_serving | amp_paired
container_queries_in_use: false
posture_queries_in_use: false
breakpoints_in_css: []                 # [320, 480, 768, 1024, 1280, 1920]

# --- Touch Posture ---
minimum_tap_target_px: 0
pinch_zoom_enabled: false
hover_only_interactions_count: 0

# --- Mobile First Indexing Status ---
gsc_url_inspection_crawled_as: ""      # "Googlebot smartphone" expected
mobile_parity_with_desktop: false
mobile_schema_parity_with_desktop: false

# --- AMP Posture ---
amp_pages_present: false
amp_sunset_planned: true               # default; AMP is obsolete

# --- Apple Intelligence Posture ---
ios_app_intents_registered: false
applinks_apple_app_site_association_present: false
apple_smart_app_banner_present: false

# --- Android App Linking Posture ---
android_app_links_assetlinks_json_present: false
android_intent_filters_configured: false

# --- Foldable Device Posture ---
foldable_design_considered: false
safe_area_inset_css_in_use: false

# --- Self Hosted Mobile Testing ---
playwright_mobile_suite_present: false
selenium_grid_self_hosted: false
real_device_pool_size: 0
Enter fullscreen mode Exit fullscreen mode

3. Mobile First Indexing Status 2026

3.1 What Mobile First Indexing Means in 2026

Google completed the migration to mobile first indexing for the entire web by October 2023. There is no longer a "switching schedule". Every site Googlebot crawls is crawled primarily as Googlebot Smartphone. The desktop user agent persists for legacy verification only.

Operationally:

  • Mobile rendering is the canonical rendering for indexation.
  • Content, schema, and internal links present only on desktop do not contribute to ranking or discovery.
  • Mobile Core Web Vitals are the only Core Web Vitals that affect ranking.
  • Mobile usability issues block indexation, not just rank.

3.2 Verifying Current Crawl Perspective

The Mobile Friendly Test tool at search.google.com/test/mobile-friendly was deprecated in late 2024. Its functionality has been folded into Google Search Console URL Inspection. To verify how Google currently crawls a URL:

  1. Open Google Search Console for the property.
  2. Enter the URL in the top URL inspection bar.
  3. Wait for the inspection result.
  4. Open the "Coverage" section.
  5. Read the "Crawled as" field. Expected value: "Googlebot smartphone".
  6. If the value reads "Googlebot" without the "smartphone" qualifier, the URL is on the legacy desktop crawl path. This is rare in 2026 and indicates either a mobile rendering failure or a site explicitly excluded from mobile first indexing.
  7. Click "Test live URL" to confirm the current rendering reflects the live mobile state.
  8. Open the "Screenshot" tab to verify the rendered mobile viewport matches intent.
  9. Open the "More info" > "JavaScript console messages" to verify no JavaScript errors block mobile rendering.

3.3 Legacy Desktop Only Pages

Pattern Impact Fix
m.example.com returns desktop HTML Googlebot Smartphone retrieves desktop content Migrate to responsive; 301 the m subdomain to the apex
Dynamic serving returns desktop to mobile UA Mobile rendering broken; indexation incomplete Migrate to responsive; remove user agent switching
AMP only mobile path Mobile depends on deprecated runtime Sunset AMP; serve responsive HTML on a single canonical URL
Desktop only intermediate redirect Indexation diverges from user experience Remove redirect; serve full content on canonical URL
Geo blocked mobile carrier routing Googlebot Smartphone routes through blocked ranges Whitelist Googlebot IP ranges from the published list

3.4 Parity Requirements

Mobile first indexing demands strict parity between mobile and desktop renderings:

  • Primary heading, body copy, and content tokens present in both.
  • Schema markup present in both (the most common parity failure: schema injected via desktop only GTM, or schema rendered into a sidebar with display: none on mobile).
  • Internal links present in both. Mobile navigation must contain the same destination set as desktop megamenus.
  • Images present in both with the same alt text.
  • Canonical tag, Open Graph, meta description, and hreflang annotations identical.

Audit by fetching the URL twice with the Googlebot Smartphone and Googlebot Desktop user agents and diffing:

GBS="Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (compatible; Googlebot/2.1)"
GBD="Mozilla/5.0 AppleWebKit/537.36 (compatible; Googlebot/2.1)"

curl -s -A "$GBS" "https://example.com/page" > /tmp/mobile.html
curl -s -A "$GBD" "https://example.com/page" > /tmp/desktop.html

diff <(grep -oE '<h[1-6][^>]*>[^<]*</h[1-6]>' /tmp/mobile.html) \
     <(grep -oE '<h[1-6][^>]*>[^<]*</h[1-6]>' /tmp/desktop.html)
Enter fullscreen mode Exit fullscreen mode

4. Responsive Design Patterns

4.1 The Canonical Pattern

Responsive design with a fluid grid, mobile first CSS, and progressive enhancement remains the canonical pattern. A single HTML document with a single set of URLs serves every viewport from a 320 pixel narrow phone to a 1920 pixel wide desktop monitor and beyond.

The opening rule, present on every HTML document:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
Enter fullscreen mode Exit fullscreen mode

The viewport-fit=cover directive opts the page into the iOS Safari and modern Android safe area inset model, which matters for foldables and for devices with notches or punch hole cameras. Without it, the layout sits inside a letterboxed inner viewport on devices with non rectangular screens.

Do not set user-scalable=no. Do not set maximum-scale=1. Both block pinch zoom, which is an accessibility requirement under WCAG 2.2 SC 1.4.4 Resize Text. Modern Lighthouse audits flag both as accessibility failures.

4.2 Mobile First CSS

Mobile first CSS means the base styles target the smallest viewport (320 pixels wide) and media queries progressively enhance for larger viewports:

/* Base: 320px and up, mobile portrait */
.container { padding: 1rem; }
.button {
  display: block;
  width: 100%;
  padding: 1rem;
  font-size: 1rem;
  min-height: 48px;
}
.nav { flex-direction: column; }

@media (min-width: 480px) { .container { padding: 1.25rem; } }

@media (min-width: 768px) {
  .container { padding: 2rem; max-width: 720px; margin: 0 auto; }
  .button { display: inline-flex; width: auto; padding: 0.75rem 2rem; }
  .nav { flex-direction: row; }
}

@media (min-width: 1024px) { .container { max-width: 960px; } }
@media (min-width: 1280px) { .container { max-width: 1200px; } }
@media (min-width: 1920px) { .container { max-width: 1440px; } }
Enter fullscreen mode Exit fullscreen mode

Mobile loads the smallest stylesheet first. Larger viewports load the same base plus the overrides for their width. On a 320 pixel phone the browser parses zero media query bodies. On a 1920 pixel monitor it parses all five.

4.3 Container Queries

Container queries became broadly supported in 2024 and are the default 2026 pattern for component level responsive behavior. Where media queries respond to viewport width, container queries respond to the container the component is rendered inside, which lets a single card component adapt correctly whether it lives in a 320 pixel sidebar or a 960 pixel main column.

.card-grid {
  container-type: inline-size;
  container-name: card-grid;
}

.card {
  display: flex;
  flex-direction: column;
}

@container card-grid (min-width: 480px) {
  .card {
    flex-direction: row;
    align-items: center;
  }

  .card-image {
    width: 33%;
  }

  .card-body {
    width: 67%;
    padding-left: 1rem;
  }
}
Enter fullscreen mode Exit fullscreen mode

Component reuse becomes correct: the same card renders narrow inside a narrow container and wide inside a wide container, regardless of viewport size. Media queries continue to handle page level layout. Container queries handle component level layout.

4.4 Common Responsive Failures

Failure Cause Fix
Horizontal scroll Element wider than viewport (table, iframe, code block, unsized image) img, video, iframe, table, pre { max-width: 100%; height: auto; }
Fixed pixel widths Widths in px rather than %, rem, or container units Use max-width and clamp() patterns
Text under 16px Body font 14px or smaller iOS Safari auto zooms on form focus and triggers CLS; minimum 16px
Hover only interactions Menu reveal requires :hover Pair with :focus and tap toggle; or <details>
Break at 360 to 375 px iPhone SE (375), folded Z Fold cover (~360) Test at 320, 360, 375 viewports
Intrusive interstitial Modal dominating mobile first paint Cookie or signup overlays under 30 percent viewport on mobile, or gesture triggered (Google Mobile Interstitial penalty active since 2017)
Popup close target too small Close X under 48 by 48 48 by 48 minimum on every dismiss control

5. Mobile Core Web Vitals

5.1 The Thresholds in 2026

The Core Web Vitals thresholds remain unchanged since the INP migration in March 2024:

Metric Good Needs Improvement Poor
LCP (Largest Contentful Paint) <= 2.5 s 2.5 to 4.0 s > 4.0 s
INP (Interaction to Next Paint) <= 200 ms 200 to 500 ms > 500 ms
CLS (Cumulative Layout Shift) <= 0.1 0.1 to 0.25 > 0.25

Thresholds are measured at the 75th percentile of real user data for mobile and desktop separately. Google ranks mobile vitals against mobile users only. A page must clear 75 percent for the "Good" classification.

5.2 Why Mobile Vitals Are Harder Than Desktop Vitals

The mobile field versus lab data divergence is wider than desktop's:

  • Network variance. 5G uplink TTFB ranges from 80 ms (cell line of sight) to 2400 ms (cell handoff). Desktop ethernet rarely varies by more than 50 ms.
  • Device variance. Lighthouse's "mid range mobile device" simulation is a Moto G4 era proxy. The actual 2026 mid range device is a $200 to $400 phone with thermal throttling. Lab measurements underestimate real INP by 40 to 80 percent on lower end devices, per HTTPArchive 2024 web performance report (1.3 million URLs sampled).
  • HTTP/3 effects. Per HTTPArchive 2024, sites serving HTTP/3 see a 200 to 400 ms p75 mobile TTFB improvement versus HTTP/2 on the same hosting.
  • Battery and thermal state. A phone in low power mode runs the CPU at reduced clock speed. Lighthouse lab runs do not model this.

Lab data is a sanity check, not a pass. Field data from CrUX or self hosted RUM is the ground truth.

5.3 Mobile LCP Levers

Target: under 2.5 seconds at p75 mobile.

Lever Target Fix
TTFB Under 600 ms p75 mobile Serve from same continent; cache static HTML at nginx; avoid client side rendering of hero; HTTP/3 enabled
Hero image Under 100 KB compressed AVIF or WebP; sized to viewport breakpoint (not a 4000 pixel image on a 375 pixel viewport); fetchpriority="high"; never lazy loaded
Font loading Swap before LCP font-display: swap; preload critical weights; size-adjust to match fallback metrics
Render blocking Zero blocking CSS above the fold Inline critical CSS; defer non critical via media="print" onload pattern; defer JS unless it generates above the fold content
Third party scripts Zero in critical path GA4, GTM, chat widgets, social embeds with defer or after onload; self host fonts where license permits

5.4 Mobile INP Levers

Target: under 200 milliseconds at p75 mobile.

Lever Target Fix
JS budget Under 250 KB executed before interaction Code split routes; tree shake; audit with webpack-bundle-analyzer
Long tasks No task over 50 ms on main thread Break into chunks with scheduler.yield() or setTimeout(0); move heavy work to a Web Worker
Click handlers Return control within 50 ms Update UI optimistically; reconcile asynchronously; avoid layout thrash (reading offsetHeight after writing style)
React re-render scope No tree wide re-render on state change React.memo; useCallback for handlers passed to memoized children; virtualize long lists with react-window
Third party tag execution Not blocking interaction type="module" or defer; sandbox heavy widgets in a same origin iframe

5.5 Mobile CLS Levers

Target: under 0.1 at p75 mobile.

Lever Rule Pattern
Image dimensions Every image declares width and height (or aspect-ratio) <img src="/img/hero.avif" width="1200" height="800" alt="..." fetchpriority="high">
Embed dimensions Every iframe declares dimensions or aspect-ratio .video-wrapper { aspect-ratio: 16 / 9; }
Font swap shift Fallback font metrics match loaded font size-adjust, ascent-override, descent-override on @font-face
Cookie banner shift Banner does not push content Fixed overlay at bottom; no document flow space reserved
Late loaded above fold content No content injected above existing content after first paint Reserve placeholder space; replace in place

6. Touch and Interaction Patterns

6.1 Tap Target Sizing

WCAG 2.2 SC 2.5.8 Target Size (Minimum) became a Level AA conformance requirement in October 2023. AA minimum is 24 by 24 CSS pixels; AAA is 44 by 44. Apple HIG recommends 44 by 44 points; Google Material and Lighthouse recommend 48 by 48 dp. The practical 2026 target is 48 by 48 CSS pixels for all primary tap targets, which clears every standard and is comfortable on the average adult thumb (which contacts a ~45 by 45 pixel zone on a typical phone).

.btn,
.link-button,
nav a,
.tap-target {
  min-height: 48px;
  min-width: 48px;
  padding: 12px 20px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* Tap targets must have spacing between them */
.btn + .btn {
  margin-left: 8px;
}

nav li + li {
  margin-top: 4px;
}
Enter fullscreen mode Exit fullscreen mode

The 8 pixel minimum spacing matters: two adjacent buttons that are each 48 by 48 but share an edge form a 96 pixel wide pair that thumbs sometimes mis tap. Eight pixels of margin (or padding) between them ends mis tap behavior.

6.2 Thumb Zone Navigation

The thumb of the average smartphone user reaches comfortably across the bottom two thirds of a vertical phone display. Anything in the top third requires a hand shift or a thumb stretch. The 2026 pattern anchors primary navigation and primary CTAs in the bottom third of the viewport where possible.

Pattern When Note
Sticky CTA bar at viewport bottom Single primary action per page (book, call, buy) Use position: sticky; bottom: 0 with env(safe-area-inset-bottom) padding; avoid blocking content
Bottom tab bar 3 to 5 primary destinations Always thumb reachable; familiar app model
Hamburger that opens a bottom drawer Many destinations Maintains hamburger discoverability while putting menu items in thumb zone
Native pull to refresh Listing pages, feeds Do not override with custom scroll handlers

6.3 Gestures and Accessibility

Gesture interactions (swipe, pinch, long press, double tap) must always be paired with a non gesture alternative. WCAG 2.2 SC 2.5.1 Pointer Gestures requires any function operated through a multipoint or path based gesture be operable through a single point activation as well.

Gesture Alternative Required
Swipe to advance carousel Previous and next buttons; dot indicator
Pinch to zoom image Tap to open full screen with zoom button (never disable native pinch zoom)
Long press context menu Visible three dot menu button
Double tap to like Single tap also likes

6.4 Visual Feedback

Every tap must produce visible feedback within 100 milliseconds. The most common 2026 failure is over reliance on JavaScript state for visual feedback: the button waits for the click handler to complete and then updates, producing a 200 to 600 millisecond gap with no feedback. The user taps again, then a third time, then the page processes all three taps and the user has triggered the action three times.

.btn {
  transition: transform 0.05s ease, background-color 0.1s ease;
}

.btn:active {
  transform: scale(0.97);
  background-color: var(--brand-dark);
}

.btn:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}
Enter fullscreen mode Exit fullscreen mode

The :active state fires synchronously on touchstart; the browser repaints before the click handler runs. Users feel the response before the JavaScript has a chance to be slow.


7. Foldable Device Considerations

7.1 The Foldable Device Population in 2026

Foldable devices (Samsung Galaxy Z Fold and Z Flip, OPPO Find N, Google Pixel Fold, Honor Magic V, Xiaomi Mix Fold) have grown from 0.7 percent of global smartphone shipments in 2022 to 5.2 percent in Q1 2026 per IDC's Worldwide Quarterly Mobile Phone Tracker (March 2026 release). They are concentrated in premium audiences: foldable users index 2.4 times higher on mobile commerce conversion per Shopify's 2026 mobile commerce report (sample: 1.1 million Shopify merchants).

Three folding patterns matter:

  • Book fold (Galaxy Z Fold, Pixel Fold, Find N): cover ~360 to 414 pixels wide folded, ~720 to 850 pixels wide unfolded.
  • Clamshell fold (Galaxy Z Flip, Find N Flip): cover 360 pixels wide, internal display 720 pixels wide tall.
  • Tri fold (Huawei Mate X3, expected Samsung 2026): three panels with two creases, multi posture layout.

7.2 CSS Posture Queries

The CSS Device Posture API and @media (device-posture: folded) media query reached broad support across Chromium based browsers in 2024 and Safari iOS 18 in 2025. The 2026 pattern is to detect posture and layout in response:

/* Default: continuous (flat) posture */
.article-layout {
  display: block;
}

/* Folded posture: device is partially folded (laptop mode) */
@media (device-posture: folded) {
  .article-layout {
    display: grid;
    grid-template-columns: 1fr 1fr;
  }

  .article-image {
    grid-column: 1;
  }

  .article-text {
    grid-column: 2;
  }
}
Enter fullscreen mode Exit fullscreen mode

Combined with viewport segments, this lets a folded device render an image on the top half panel and body text on the bottom half panel without overlapping the fold crease.

7.3 Safe Area Insets

Foldable devices, devices with notches, devices with under display cameras, and devices with rounded corners all require the safe area inset CSS environment variables:

.header {
  padding-top: env(safe-area-inset-top, 0);
}

.footer,
.bottom-nav {
  padding-bottom: env(safe-area-inset-bottom, 0);
}

.sidebar {
  padding-left: env(safe-area-inset-left, 0);
  padding-right: env(safe-area-inset-right, 0);
}
Enter fullscreen mode Exit fullscreen mode

The env() fallback (second argument) is critical: devices without inset reporting return zero, and the layout reads correctly on every device.

7.4 The Dual Screen Layout Breakpoint

Unfolded book fold devices report a viewport width in the 720 to 850 pixel range. This sits exactly at the conventional tablet breakpoint. The 2026 best practice is to design the 768 pixel breakpoint with foldable layouts in mind:

@media (min-width: 768px) {
  .reading-pane {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 2rem;
  }
}

/* Pair with posture query for explicit dual screen layout */
@media (min-width: 768px) and (device-posture: folded) {
  .reading-pane {
    gap: 4rem; /* extra gap to clear the fold crease */
  }
}
Enter fullscreen mode Exit fullscreen mode

8. Apple Intelligence on iOS 19

8.1 What Apple Intelligence Does on iOS 19

Apple Intelligence shipped on iOS 18 in fall 2024 and reached production maturity with iOS 19 in fall 2025 (point release 19.2 January 2026). Three touchpoints matter for mobile SEO:

  • Spotlight Web indexes web content on Apple servers and surfaces results in the Spotlight search bar (pull down on Home Screen) and Safari's address bar suggestions. Spotlight Web is populated by Applebot and Applebot-Extended.
  • Siri routes general knowledge questions to ChatGPT (via the user opt in OpenAI integration shipped with iOS 18.2) or to Apple's own foundation model. When Siri answers from a web source it cites the source and offers an "Open in Safari" affordance.
  • App Intents let an app expose typed actions that Siri, Spotlight, and the Apple Intelligence panel can invoke. For a mobile website with a companion app, registering App Intents that map to web URLs lets Apple Intelligence deep link directly into the app from Spotlight or Siri, falling back to the web page if the app is not installed.

8.2 Registering App Intents

App Intents are declared in Swift on iOS. The mobile SEO relevant piece is the URL mapping: an App Intent that performs a "show product" action exposes both an app URL scheme and an https URL on the marketing site. Apple Intelligence prefers the app URL if the app is installed and falls back to the web URL otherwise.

Universal Links configuration requires a single file at /.well-known/apple-app-site-association:

{
  "applinks": {
    "apps": [],
    "details": [{
      "appIDs": ["TEAMID.com.example.YourApp"],
      "components": [
        { "/": "/products/*", "comment": "Product detail pages" },
        { "/": "/articles/*", "comment": "Article pages" },
        { "/": "/contact", "comment": "Contact page" }
      ]
    }]
  },
  "webcredentials": { "apps": ["TEAMID.com.example.YourApp"] }
}
Enter fullscreen mode Exit fullscreen mode

Served from the apex domain over HTTPS with Content-Type: application/json and no file extension:

location = /.well-known/apple-app-site-association {
  default_type application/json;
  alias /var/www/sites/example.com/.well-known/apple-app-site-association;
}
Enter fullscreen mode Exit fullscreen mode

When a user taps https://example.com/products/widget from Siri, Spotlight, Messages, or Mail, iOS checks the apple-app-site-association file, sees the /products/* pattern, and opens the app. If the app is not installed, Safari opens the URL.

8.3 Applebot Extended for Apple Intelligence Training

Apple's training crawler is Applebot-Extended, distinct from Applebot (Siri and Spotlight). The two have separate opt out semantics. To allow both: Allow: / under each user agent block. To opt out of training only while keeping Siri and Spotlight presence: User-agent: Applebot-Extended + Disallow: /. For most marketing sites, allow Applebot-Extended: training contributes to model awareness of your brand and content.

8.4 Smart App Banner

The Smart App Banner is the small "Open in App" banner Safari shows at the top of mobile pages when a paired iOS app exists. It is a simple meta tag:

<meta name="apple-itunes-app" content="app-id=123456789, app-argument=https://example.com/products/widget">
Enter fullscreen mode Exit fullscreen mode

The app-argument value is the URL passed into the app when the user taps "Open". This lets a single web URL deep link into the corresponding app screen.

8.5 Android App Links

The Android equivalent is the assetlinks.json file served from /.well-known/assetlinks.json:

[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.example.yourapp",
      "sha256_cert_fingerprints": ["..."]
    }
  }
]
Enter fullscreen mode Exit fullscreen mode

nginx:

location = /.well-known/assetlinks.json {
  default_type application/json;
  alias /var/www/sites/example.com/.well-known/assetlinks.json;
}
Enter fullscreen mode Exit fullscreen mode

Combined with <intent-filter> declarations in the Android manifest, this enables App Links: tapping a web URL on an Android device with the app installed opens the app directly.


9. AMP Status 2026

9.1 AMP Is Obsolete

Google dropped the AMP requirement for the Top Stories carousel in June 2021. Google retired the AMP page experience signal in 2022. AMP cache hosting on google.com/amp/s/... URLs has been deprioritized since 2023. The AMP project has not received material updates since 2023.

In 2026, AMP is a finished pattern. New sites should not implement AMP. Existing AMP pages should be sunset by 301 redirect to the canonical responsive version. Mobile responsive pages built to the patterns in this framework outperform AMP on Core Web Vitals and earn the same Top Stories eligibility without the AMP runtime.

9.2 Sunsetting AMP

For sites still serving AMP pages, the sunset procedure:

  1. Inventory. List all AMP URLs from GSC /amp/ paths, sitemap, and internal links with rel="amphtml".
  2. Redirect. 301 redirect each AMP URL to its canonical responsive URL. nginx: location ~ ^/amp/(.*)$ { return 301 /$1; }.
  3. Remove <link rel="amphtml"> tags from canonical pages.
  4. Update sitemap.xml to remove AMP URLs; resubmit in GSC.
  5. Request recrawl of high traffic canonical pages in GSC URL Inspection.
  6. Decommission the runtime. Remove AMP script tags, AMP build pipelines, amp-* custom elements from the codebase.

9.3 What Replaces AMP

The 2026 stack that earns the speed and eligibility AMP once provided:

  • Mobile responsive HTML with the patterns in Section 4.
  • Mobile Core Web Vitals targets from Section 5.
  • HTTP/3 enabled in nginx (Section 13).
  • Critical CSS inlined; non critical CSS deferred.
  • Hero image preloaded; below the fold lazy loaded.
  • JavaScript deferred or async; under 250 KB executed before interaction.
  • News articles include Article or NewsArticle schema with datePublished, dateModified, author, headline, and image.

Top Stories eligibility no longer requires AMP. It requires the site to be a Google News approved publisher (via the Google Publisher Center) and the article to meet Top Stories quality signals.


10. Mobile Page Speed

10.1 The Above the Fold Render Budget

The 2026 mobile above the fold budget for a marketing site:

  • HTML document under 30 KB compressed.
  • Critical CSS inlined, under 14 KB inside <style>.
  • JavaScript executed before interaction under 250 KB compressed.
  • Hero image under 100 KB compressed.
  • Custom fonts under 50 KB total (two weights, woff2).
  • Total above the fold transfer under 500 KB.

The 14 KB inline CSS number derives from the initial TCP congestion window: 14 KB fits in the first round trip of a fresh TCP connection. CSS inlined inside the HTML document inside the first 14 KB renders without a second network request.

10.2 Image Optimization for Mobile

Format priority: AVIF primary, WebP fallback, JPEG (progressive) or PNG (icons) legacy fallback. Responsive images use srcset and sizes:

<img src="/img/hero-800w.avif"
     srcset="/img/hero-400w.avif 400w,
             /img/hero-800w.avif 800w,
             /img/hero-1200w.avif 1200w,
             /img/hero-1600w.avif 1600w"
     sizes="(min-width: 1024px) 1200px, 100vw"
     width="1200" height="800"
     alt="..."
     fetchpriority="high">
Enter fullscreen mode Exit fullscreen mode

srcset width descriptors handle device pixel ratio automatically; avoid manual 1x and 2x suffix logic (legacy pattern). Hero images use loading="eager" (default); below the fold images use loading="lazy". Add decoding="async" to non critical images so the browser may decode off the main thread.

10.3 HTTP/3 Performance

HTTP/3 over QUIC reduces handshake round trips on lossy mobile connections. Per HTTPArchive's 2024 Web Almanac performance chapter (analysis sample: 8.9 million origins), sites serving HTTP/3 see a 200 to 400 millisecond improvement in mobile TTFB at the 75th percentile versus HTTP/2 on the same hosting. The improvement is larger on cellular connections with packet loss and smaller on wifi with low loss.

nginx 1.25 and later supports HTTP/3 natively. The configuration enables both HTTP/2 (over TCP+TLS) and HTTP/3 (over QUIC+TLS) on the same port. Section 13 walks through the configuration on Bubbles.

10.4 Lazy Loading

Native lazy loading via the loading="lazy" attribute is broadly supported across mobile browsers in 2026. It does not require a JavaScript library. The browser decides when to load based on viewport proximity heuristics.

Rules:

  • Hero image: never lazy load. loading="eager" (default) or omitted.
  • Above the fold images: never lazy load.
  • Below the fold images: always lazy load.
  • Iframes: lazy load with loading="lazy" on the <iframe> tag.
  • Background images set via CSS: lazy loading does not apply; the browser loads when the element is in the render tree. To lazy load CSS background images, use a JavaScript IntersectionObserver pattern.

10.5 Service Workers and Offline

Service workers cache critical assets aggressively and serve them on subsequent visits without a network round trip. For high return visit sites, service workers reduce repeat visit LCP by 800 to 1200 milliseconds at the 75th percentile.

The Workbox library ships well tested cache strategies. A typical mobile marketing site uses cache first for static assets and network first for HTML:

// sw.js
import { precacheAndRoute } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { CacheFirst, NetworkFirst } from 'workbox-strategies';

precacheAndRoute(self.__WB_MANIFEST);

registerRoute(
  ({ request }) => ['style', 'script', 'font', 'image'].includes(request.destination),
  new CacheFirst({ cacheName: 'static-assets' })
);

registerRoute(
  ({ request }) => request.mode === 'navigate',
  new NetworkFirst({ cacheName: 'pages' })
);
Enter fullscreen mode Exit fullscreen mode

Service workers must be served from HTTPS. Scope is constrained to the path they are served from (/sw.js controls the entire origin; /app/sw.js controls only /app/).


11. Mobile Crawler Behavior

11.1 The Crawler User Agents in 2026

Crawler UA token Role DNS verification
Googlebot Smartphone Googlebot/2.1 + Mobile Primary Google mobile first indexing crawler reverse DNS to googlebot.com or google.com
Bingbot Mobile bingbot/2.0 + Mobile Bing search and Copilot reverse DNS to search.msn.com
Applebot Applebot/ Siri and Spotlight Web reverse DNS to applebot.apple.com
Applebot-Extended Applebot-Extended Apple Intelligence training same as Applebot
OAI-SearchBot OAI-SearchBot/1.0 OpenAI search index (ChatGPT search) n/a
ChatGPT-User ChatGPT-User/1.0 ChatGPT real time fetch (user initiated) n/a
GPTBot GPTBot OpenAI training n/a
PerplexityBot PerplexityBot/1.0 Perplexity index n/a
Perplexity-User Perplexity-User Perplexity real time fetch n/a
ClaudeBot ClaudeBot/1.0 Anthropic Claude training and search n/a
Meta-ExternalAgent meta-externalagent Meta AI Llama training n/a

All current AI crawlers request mobile responsive content and treat the mobile rendering as canonical.

11.2 Verifying Mobile Crawl in Server Logs

To check whether Googlebot Smartphone is reaching priority URLs and which AI crawlers are visiting:

# Last 7 days of Googlebot Smartphone hits, grouped by URL
sudo zcat /var/log/nginx/access.log.*.gz | \
  grep -E "Googlebot.*Mobile" | awk '{print $7}' | \
  sort | uniq -c | sort -rn | head -20

# Distinct mobile crawler user agents seen this week
sudo grep -E "Googlebot|Bingbot|Applebot|OAI-SearchBot|PerplexityBot|ClaudeBot" \
  /var/log/nginx/access.log | awk -F'"' '{print $6}' | sort -u
Enter fullscreen mode Exit fullscreen mode

11.3 robots.txt for Mobile and AI Crawlers

Allow all legitimate mobile crawlers:

User-agent: Googlebot
Allow: /

User-agent: Bingbot
Allow: /

User-agent: Applebot
Allow: /

User-agent: Applebot-Extended
Allow: /

User-agent: OAI-SearchBot
Allow: /

User-agent: ChatGPT-User
Allow: /

User-agent: GPTBot
Allow: /

User-agent: PerplexityBot
Allow: /

User-agent: Perplexity-User
Allow: /

User-agent: ClaudeBot
Allow: /

User-agent: meta-externalagent
Allow: /

Sitemap: https://example.com/sitemap.xml
Enter fullscreen mode Exit fullscreen mode

Rate limiting at nginx (optional, for high traffic origins) uses limit_req_zone keyed on $binary_remote_addr with separate zones for Googlebot, Bingbot, Applebot (search engine class) and OAI-SearchBot, PerplexityBot, ClaudeBot, GPTBot (AI class).


12. Mobile Schema Considerations

12.1 Schema Parity Is Mandatory

Schema markup must be present in the mobile rendering. The most common 2026 schema failure mode is schema injected by a desktop only GTM container or rendered into a sidebar that is display: none on mobile. Either pattern means the mobile Googlebot sees no schema.

The audit: fetch the URL with Googlebot Smartphone user agent and grep for application/ld+json. The count of script blocks must match the count seen with the desktop user agent. The contents must be equivalent.

12.2 LocalBusiness for Apple Maps and Google Maps Mobile Cards

LocalBusiness schema with geo coordinates triggers map cards in mobile search results on both Apple Maps integrated results (iOS Spotlight) and Google Maps integrated results (Android Chrome, iOS Chrome). The mobile card includes call, directions, and website tap targets that bypass the SERP entirely.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "LocalBusiness",
  "@id": "https://example.com/#localbusiness",
  "name": "Example Business",
  "url": "https://example.com",
  "telephone": "+15055551234",
  "address": {
    "@type": "PostalAddress",
    "streetAddress": "123 Main St",
    "addressLocality": "Anytown",
    "addressRegion": "NM",
    "postalCode": "87000",
    "addressCountry": "US"
  },
  "geo": {
    "@type": "GeoCoordinates",
    "latitude": 35.0844,
    "longitude": -106.6504
  },
  "openingHoursSpecification": [
    {
      "@type": "OpeningHoursSpecification",
      "dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
      "opens": "09:00",
      "closes": "17:00"
    }
  ],
  "hasMap": "https://maps.google.com/?cid=1234567890",
  "sameAs": [
    "https://www.facebook.com/examplebusiness",
    "https://www.instagram.com/examplebusiness"
  ]
}
</script>
Enter fullscreen mode Exit fullscreen mode

The hasMap field with a Google Maps CID URL links the schema to a specific Google Business Profile entity. The geo block lets Apple Maps surface the same business in iOS Spotlight Web results.

12.3 Product Schema and Mobile Shopping

Product schema price and availability freshness affects mobile shopping search cards. Stale prices (older than 24 hours per Google's product feed freshness guidelines, updated November 2024) trigger Google to demote the product from the Shopping carousel and from mobile rich results.

For sites with rapidly changing prices, the recommendation is to render product schema server side with a current timestamp, not client side via JavaScript:

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Example Widget",
  "image": "https://example.com/img/widget.avif",
  "description": "...",
  "sku": "WIDGET-123",
  "brand": {
    "@type": "Brand",
    "name": "Example"
  },
  "offers": {
    "@type": "Offer",
    "url": "https://example.com/products/widget",
    "priceCurrency": "USD",
    "price": "49.99",
    "priceValidUntil": "2026-12-31",
    "availability": "https://schema.org/InStock",
    "itemCondition": "https://schema.org/NewCondition",
    "seller": {
      "@type": "Organization",
      "name": "Example Inc."
    }
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

12.4 MobileApplication Schema

For pages that describe a mobile app, MobileApplication schema lets Google and Bing surface app install cards in mobile search:

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "MobileApplication",
  "name": "Example App",
  "operatingSystem": "iOS, Android",
  "applicationCategory": "BusinessApplication",
  "downloadUrl": [
    "https://apps.apple.com/app/id123456789",
    "https://play.google.com/store/apps/details?id=com.example.yourapp"
  ],
  "offers": {
    "@type": "Offer",
    "price": "0",
    "priceCurrency": "USD"
  },
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.7",
    "ratingCount": "1234"
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

12.5 SpeakableSpecification for Voice Assistants

SpeakableSpecification flags content blocks that voice assistants should read aloud. Primarily relevant for NewsArticle:

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "NewsArticle",
  "headline": "...",
  "datePublished": "2026-05-14T08:00:00-06:00",
  "speakable": {
    "@type": "SpeakableSpecification",
    "cssSelector": [".article-summary", ".article-tldr"]
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

The CSS selectors point at the elements with the speakable content. Voice assistants (Google Assistant, Siri reading via Apple Intelligence) prefer these blocks for short audio answers.

See framework-schema.md for the full schema graph specification and framework-voicesearch.md for the voice assistant integration patterns.


13. Mobile Forms and Conversion

13.1 Semantic Input Types

The type attribute on <input> elements determines which keyboard the mobile OS surfaces:

type Keyboard autocomplete inputmode alt
email Email keyboard (@ and . prominent) email email
tel Numeric phone keyboard tel tel
number Numeric keyboard (varies) numeric (prefer on type="text" to avoid spinner controls)
url URL keyboard (/ and .com prominent) url url
date Native date picker on iOS and Android (none) (none)
search Search keyboard (Search instead of Return) (none) (none)
password Password keyboard new-password (signup) or current-password (login) (none)

13.2 The autocomplete Attribute

The autocomplete attribute is the single largest mobile form conversion lever. Without it, mobile users type every field by hand. With it, the OS surfaces saved values and the user taps to fill.

The full list of standardized autocomplete values is in the HTML Living Standard. The 2026 essentials for a contact form:

<form action="/api/contact" method="POST">
  <label for="name">Full name</label>
  <input id="name" name="name" type="text" autocomplete="name" required>

  <label for="email">Email</label>
  <input id="email" name="email" type="email" autocomplete="email" required>

  <label for="tel">Phone</label>
  <input id="tel" name="tel" type="tel" autocomplete="tel">

  <label for="organization">Company</label>
  <input id="organization" name="organization" type="text" autocomplete="organization">

  <label for="message">Message</label>
  <textarea id="message" name="message" required></textarea>

  <button type="submit">Send</button>
</form>
Enter fullscreen mode Exit fullscreen mode

For a checkout form, autocomplete values are cc-name, cc-number, cc-exp, cc-csc for card details and street-address, address-level2 (city), address-level1 (state), postal-code for shipping. Numeric fields add inputmode="numeric" to surface the numeric keypad without forcing type="number" spinner controls.

13.3 iOS Password Autofill Integration

iOS Password Autofill (Settings > Passwords) suggests stored credentials at login forms and offers strong password generation at signup forms. The integration requires:

  • A login form with <input type="email" autocomplete="username"> (or autocomplete="email") and <input type="password" autocomplete="current-password">.
  • A signup form with <input type="email" autocomplete="username"> and <input type="password" autocomplete="new-password">.
  • The site domain associated with the app through /.well-known/apple-app-site-association (the webcredentials section, see Section 8.2).

When configured correctly, iOS users see their stored password offered above the keyboard with a single tap to fill.

13.4 Mobile Form Layout

Single column. Each field full width. Labels above the input, not beside (side labels truncate or wrap on narrow viewports). Submit button visible without scroll where possible.

.form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  max-width: 100%;
}

.form label {
  display: block;
  margin-bottom: 0.25rem;
  font-weight: 600;
}

.form input,
.form textarea,
.form select {
  width: 100%;
  padding: 0.75rem 1rem;
  font-size: 16px; /* prevents iOS auto zoom */
  border: 1px solid var(--border);
  border-radius: 6px;
  min-height: 48px;
}

.form button[type="submit"] {
  min-height: 48px;
  padding: 0.875rem 2rem;
  font-size: 1rem;
  font-weight: 600;
}
Enter fullscreen mode Exit fullscreen mode

13.5 Form Validation on Mobile

Native HTML validation (required, pattern, type="email") works well on mobile. Custom JavaScript validation should run on blur or on submit, not on every keystroke (which thrashes the layout and breaks autofill).

<input id="email" 
       name="email" 
       type="email" 
       autocomplete="email"
       required 
       aria-describedby="email-error">
<div id="email-error" role="alert" aria-live="polite"></div>
Enter fullscreen mode Exit fullscreen mode

The aria-live="polite" region announces validation errors to screen readers without interrupting the user.


14. Bubbles Hosted Mobile Testing

14.1 The Bubbles Stack

The reference deployment is self hosted on Debian at IP 169.155.162.118. nginx serves every site from /var/www/sites/[domain]/. No third party CDN sits between origin and user. The stack is single tier: client to nginx to filesystem (static) or to upstream FastAPI or Node service (dynamic).

Self hosted means full server log access, full TLS configuration control, full HTTP/3 tuning, full ability to inspect crawler behavior, and zero dependency on a third party SLA. The trade off is that you operate the box: you provision, you patch, you monitor.

14.2 nginx HTTP/3 Configuration

nginx 1.25 and later includes HTTP/3 over QUIC support in mainline. The configuration enables HTTP/3 on UDP port 443 alongside HTTP/2 on TCP port 443:

server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;

  listen 443 quic reuseport;
  listen [::]:443 quic reuseport;

  http2 on;
  http3 on;
  http3_hq on;

  quic_retry on;
  ssl_early_data on;

  add_header Alt-Svc 'h3=":443"; ma=86400, h2=":443"; ma=86400';

  server_name example.com;
  root /var/www/sites/example.com/;

  ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_prefer_server_ciphers on;
  ssl_session_cache shared:SSL:50m;
  ssl_session_timeout 1d;

  # ... rest of config
}
Enter fullscreen mode Exit fullscreen mode

The Alt-Svc header advertises HTTP/3 support to clients. Modern browsers see the header on the first HTTP/2 response and upgrade subsequent connections to HTTP/3.

UDP port 443 must be opened on the host firewall:

sudo ufw allow 443/udp
sudo ufw allow 443/tcp
Enter fullscreen mode Exit fullscreen mode

14.3 Playwright Mobile Emulation on Linux

Playwright runs headless Chromium, Firefox, and WebKit on Linux with device emulation profiles for current mobile devices. Install on the Bubbles host:

sudo apt install -y nodejs npm
sudo npm install -g playwright
sudo npx playwright install --with-deps
Enter fullscreen mode Exit fullscreen mode

A mobile emulation script (mobile-test.js) that iterates over device profiles, captures LCP, and writes a full page screenshot per device:

const { chromium, devices } = require('playwright');
const profiles = ['iPhone 15 Pro', 'iPhone SE', 'Pixel 8', 'Galaxy S24 Ultra', 'iPad Pro 11'];

(async () => {
  const browser = await chromium.launch();
  for (const name of profiles) {
    const ctx = await browser.newContext({ ...devices[name], locale: 'en-US' });
    const page = await ctx.newPage();
    await page.goto('https://example.com/');
    const lcp = await page.evaluate(() => new Promise(r => {
      new PerformanceObserver(l => r(l.getEntries().pop().startTime))
        .observe({ type: 'largest-contentful-paint', buffered: true });
    }));
    console.log(`${name}: LCP=${lcp}ms`);
    await page.screenshot({ path: `/tmp/screens/${name.replace(/\s+/g, '-')}.png`, fullPage: true });
    await ctx.close();
  }
  await browser.close();
})();
Enter fullscreen mode Exit fullscreen mode

Run with mkdir -p /tmp/screens && node mobile-test.js. Screenshots reveal layout issues that lab Lighthouse runs miss.

14.4 Self Hosted Selenium Grid for Real Device Coverage

Browserstack and LambdaTest are third party device clouds at $30 to $80 per user per month. The self hosted alternative is a Selenium Grid running on the Bubbles host with Appium drivers for real mobile devices connected via USB or wifi adb.

Minimal Debian setup:

sudo apt install -y openjdk-17-jdk
wget https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.18.0/selenium-server-4.18.0.jar
java -jar selenium-server-4.18.0.jar standalone --host 0.0.0.0 --port 4444

sudo npm install -g appium
appium driver install uiautomator2   # Android (Pixel via USB with debugging)
appium --port 4723
Enter fullscreen mode Exit fullscreen mode

Verify device with adb devices. The Grid endpoint is then at http://bubbles:4444 on the LAN and Playwright or any Selenium client can dispatch tests to it. For iOS coverage, an Apple toolchain host (Mac) runs appium driver install xcuitest with USB connected iOS devices; the Bubbles Grid forwards iOS test dispatches to the Mac.

14.5 Self Hosted RUM for Mobile Field Data

A self hosted Real User Monitoring endpoint collects mobile Core Web Vitals from real visitors without sending data to a third party. A lightweight collector posts metrics to a nginx logging endpoint:

<script type="module">
import { onLCP, onINP, onCLS, onTTFB, onFCP } from 'https://unpkg.com/web-vitals@4?module';
const send = (m) => navigator.sendBeacon('/rum', JSON.stringify({
  name: m.name, value: m.value, rating: m.rating, id: m.id,
  url: location.href, ua: navigator.userAgent, timestamp: Date.now()
}));
onLCP(send); onINP(send); onCLS(send); onTTFB(send); onFCP(send);
</script>
Enter fullscreen mode Exit fullscreen mode

nginx logs the POST body:

log_format rum_format escape=json '{"time":"$time_iso8601","ip":"$remote_addr","body":$request_body}';

location = /rum {
  access_log /var/log/nginx/rum.log rum_format;
  if ($request_method != POST) { return 405; }
  return 204;
}
Enter fullscreen mode Exit fullscreen mode

The log file is JSONL and parseable with jq to extract p75 LCP, INP, CLS per day.

14.6 Mobile Emulation Test Cadence

Cadence Action
Per commit Lighthouse mobile run on the changed page (CI step); Playwright screenshot diff on iPhone 15 and Pixel 8
Per release Playwright suite across five device profiles; real device smoke test on one iPhone and one Android; Lighthouse on top five URLs
Weekly GSC URL Inspection on five priority URLs (verify Crawled As is Googlebot smartphone); RUM log review for p75 LCP, INP, CLS
Monthly Cross device responsive review at 320, 360, 375, 768, 1024 viewports; real device session on a foldable (if applicable); service worker cache audit

15. Audit Mode

# Criterion Pass/Fail
MS1 Viewport meta with viewport-fit=cover, no user-scalable=no
MS2 Responsive design (no separate m.example.com, no AMP only path)
MS3 Content parity verified between Googlebot Smartphone and Googlebot Desktop
MS4 Schema parity verified between mobile and desktop renderings
MS5 Touch targets minimum 48 by 48 CSS pixels, 8 pixel spacing
MS6 Body text minimum 16 pixels on mobile
MS7 Pinch zoom enabled
MS8 Mobile LCP p75 under 2.5 seconds (field data)
MS9 Mobile INP p75 under 200 milliseconds (field data)
MS10 Mobile CLS p75 under 0.1 (field data)
MS11 nginx serves HTTP/3 over QUIC
MS12 Hero image preloaded; below the fold lazy loaded
MS13 Critical CSS inlined under 14 KB; non critical deferred
MS14 JavaScript executed before interaction under 250 KB
MS15 Click to call, click to map, click to SMS where appropriate
MS16 Forms use semantic input types and autocomplete attributes
MS17 Forms use inputmode where appropriate
MS18 No intrusive interstitials (cookie banner under 30 percent viewport)
MS19 Mobile navigation accessible via keyboard, screen reader, touch
MS20 Container queries used for component level responsive behavior
MS21 Safe area inset CSS used (env(safe-area-inset-*))
MS22 Foldable posture queries considered (if foldable audience present)
MS23 apple-app-site-association and assetlinks.json served (if apps exist)
MS24 Smart App Banner meta tag present (if iOS app exists)
MS25 App Intents registered for Apple Intelligence (if iOS app exists)
MS26 Universal Links and Android App Links tested on real devices
MS27 LocalBusiness schema with geo coordinates (if local business)
MS28 MobileApplication and SpeakableSpecification schema where applicable
MS29 AMP pages sunset (no /amp/ URLs serving in 2026)
MS30 Googlebot Smartphone verified in server logs in last 7 days
MS31 OAI-SearchBot or PerplexityBot verified in server logs in last 30 days
MS32 Applebot verified in server logs in last 30 days
MS33 Service worker registered with cache strategy (optional, recommended)
MS34 Playwright mobile suite runs on every release
MS35 Self hosted RUM collecting mobile field data
MS36 Real device pool with at least one iPhone and one Android
MS37 GSC URL Inspection confirms Crawled As Googlebot smartphone for priority URLs
MS38 GSC Mobile Usability report at zero issues
MS39 Mobile traffic share tracked in GA4

Score: 39. World class mobile SEO posture: 34+ of 39.


16. Common Mistakes

  1. Treating mobile as a port from desktop. The 2026 pattern is mobile first; design for 320 pixels and progressively enhance.
  2. Setting user-scalable=no. Accessibility failure; Lighthouse flag.
  3. Tap targets under 48 by 48 pixels. Frustrates users; rage taps signal poor UX.
  4. Hover only interactions. Touch users cannot access; AI assistants cannot extract.
  5. Mobile schema parity failures. Schema rendered only on desktop (via GTM or hidden sidebar) is invisible to mobile first indexing.
  6. Stale AMP pages. AMP is obsolete; sunset by 301 redirect.
  7. Hero images lazy loaded. Always loading="eager" for hero; lazy load below the fold only.
  8. JavaScript over 250 KB before interaction. Mobile INP suffers; mid range phones experience the worst.
  9. Wrong input types. type="text" where type="email" or type="tel" belongs forces the wrong keyboard.
  10. Missing autocomplete attributes. Largest mobile conversion lever left on the table.
  11. Intrusive mobile interstitials. Mobile interstitial penalty active since 2017, still enforced.
  12. No HTTP/3 on nginx. 200 to 400 ms TTFB improvement available at p75 on cellular.
  13. No real device testing. Emulators miss safe area, foldable posture, and real network variance.
  14. Ignoring foldable devices. 5.2 percent of premium audiences in 2026; high value conversion segment.
  15. No App Intents registration. iOS apps with companion sites miss Apple Intelligence deep link surface.
  16. Missing apple-app-site-association or assetlinks.json. Universal Links and Android App Links broken.
  17. No self hosted RUM. Mobile field data depends on CrUX (28 day lag, aggregate only).

End of Framework Document

This framework specifies the mobile SEO layer for a self hosted nginx stack in 2026. Cross references:

The mobile surface is the primary surface in 2026. Every framework in the library assumes the mobile rendering is canonical, the mobile vitals are the canonical vitals, and the mobile experience determines conversion and citation across both classic and AI search.


Source

Canonical: https://www.thatdevpro.com/insights/framework-mobileseo/

ThatDevPro is an SDVOSB-certified veteran-owned web + AI engineering studio. Engine Optimization service ยท Contact

Top comments (0)