Common Small Touch Targets in Vpn Apps: Causes and Fixes

These root causes are amplified in VPN apps because the UI is dense (server lists, protocol selectors, kill‑switch toggles) and often built quickly to keep up with rapid market competition.

May 22, 2026 · 6 min read · Common Issues

1. What causes small touch targets in VPN apps

Root causeWhy it happens in a VPN UITypical symptom
Pixel‑perfect design ported from desktopDesigners copy a web or desktop control panel (e.g., toggle switches, server list rows) without scaling for 5‑mm finger size.Buttons that look fine on a 1080p tablet become 8 dp wide on a 720 dp phone.
Hard‑coded dimensionsUI elements are given fixed dp/px values (width=48dp, height=24dp) instead of wrap_content plus minWidth/minHeight.The “Connect” button stays 24 dp tall even on large screens, violating the 48 dp minimum touch target recommended by Android.
Over‑crowded server listVPN apps often display dozens of servers in a single scrollable list. To fit more rows, row height is reduced below the recommended 48 dp.Users tap the wrong server; the list scrolls instead of selecting.
Dynamic UI generated from JSONServer data (name, latency badge, country flag) is injected into a template that does not recalculate row height after the data is bound.Long server names push other controls off‑screen, shrinking the tap area of the “Connect” icon.
In‑app overlay for ads or promosA banner or “upgrade now” card is placed over existing controls with absolute positioning (position: absolute; top: 0).The overlay intercepts taps meant for the underlying “Disconnect” button.
Neglect of accessibility guidelinesPersonas such as “elderly” or “accessibility” are not considered during design reviews, so minimum target size (48 dp) is ignored.Small toggle switches are hard to manipulate for users with reduced motor dexterity.
Custom gesture handlingDevelopers replace native buttons with custom Views that listen for onTouchEvent and treat any tap within a narrow bounding box as a click.A 30 dp wide icon only reacts when the finger lands exactly on the graphic, not the surrounding padding.

These root causes are amplified in VPN apps because the UI is dense (server lists, protocol selectors, kill‑switch toggles) and often built quickly to keep up with rapid market competition.

---

2. Real‑world impact

---

3. Concrete examples of small touch targets in VPN apps

  1. Connect/Disconnect icon – A 24 dp × 24 dp power‑button icon placed at the end of each server row.
  2. Protocol selector chips – Horizontal chips for “OpenVPN”, “WireGuard”, “IKEv2” that are only 36 dp high.
  3. Kill‑switch toggle – Custom switch rendered as a 30 dp wide thumb with no surrounding padding.
  4. Server‑list row height – Rows compressed to 40 dp to show more servers per screen.
  5. “Upgrade” banner close button – An “X” icon of 20 dp placed in the corner of an overlay.
  6. In‑app help tooltip trigger – A small “i” icon (16 dp) that must be tapped to reveal a tooltip.
  7. Multi‑factor authentication (MFA) “Resend code” link – Text link with a hit‑area equal to the text width only (≈ 60 dp).

---

4. How to detect small touch targets

Detection methodWhat to look forHow SUSA helps
Automated UI exploration (SUSA)Record every tappable element’s bounds and compute its area in dp. Flag any element < 48 dp × 48 dp.SUSA crawls the APK, extracts view hierarchies, and produces a *Touch Target Violation* report with screen‑shot overlays.
Accessibility Scanner (Android)Highlights “Touch target size < 48 dp”.Export the scanner’s JSON and feed it to SUSA’s persona‑based accessibility testing (elderly & accessibility personas).
Appium / Playwright visual regressionCompare baseline screenshots with a generated “heat map” of tap zones.SUSA auto‑generates Appium scripts that click every element; failures where the click lands outside the element indicate a too‑small target.
Manual heuristic testingUse a finger (or a 9 mm stylus) on a physical device; note missed taps.SUSA can simulate the “impatient” persona with rapid, imprecise taps to surface missed targets.
Static analysis of layout XML / SwiftUISearch for android:layout_width/height < 48dp or missing minWidth/minHeight.SUSA’s CI/CD plugin parses resources and flags hard‑coded dimensions before the build artifact is produced.
Crowd‑sourced telemetryCapture onClick failure rates from production (e.g., click events that fire onTouchCancel).SUSA’s cross‑session learning aggregates telemetry across runs and surfaces high‑failure hotspots.

A practical detection workflow:

  1. Run SUSA on the APK (or web URL) with the *“elderly”* and *“impatient”* personas.
  2. Review the *Touch Target Violation* section – it lists element IDs, screen names, and the exact dp size.
  3. Export the failing element list to a spreadsheet and map each to a design mockup.

---

5. How to fix each example (code‑level guidance)

1️⃣ Connect/Disconnect icon

*Problem*: 24 dp × 24 dp icon, no padding.

*Fix* (Android XML):


<ImageButton
    android:id="@+id/btnConnect"
    android:layout_width="48dp"
    android:layout_height="48dp"
    android:padding="12dp"
    android:src="@drawable/ic_power"
    android:contentDescription="@string/connect" />

*Fix* (iOS SwiftUI):


Button(action: connect) {
    Image(systemName: "power")
        .frame(width: 48, height: 48)
        .contentShape(Rectangle()) // expands tappable area
}

2️⃣ Protocol selector chips

*Problem*: 36 dp high chips, text clipped on large screens.

*Fix* (Android Material Components):


<com.google.android.material.chip.ChipGroup
    android:id="@+id/chipGroup"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:singleSelection="true">

    <com.google.android.material.chip.Chip
        style="@style/Widget.MaterialComponents.Chip.Choice"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:minHeight="48dp"
        android:paddingHorizontal="12dp"
        android:text="WireGuard"/>
</com.google.android.material.chip.ChipGroup>

3️⃣ Kill‑switch toggle

*Problem*: Custom view with 30 dp thumb, no hit‑area.

*Fix*: Wrap the custom view in a FrameLayout with minWidth/minHeight 48 dp, and forward clicks.


class KillSwitchView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null
) : FrameLayout(context, attrs) {

    init {
        layoutParams = LayoutParams(48.dp, 48.dp)
        setOnClickListener { toggle() }
        // draw the 30dp thumb inside
    }
}

4️⃣ Server‑list row height

*Problem*: Row height forced to 40 dp via android:layout_height="40dp".

*Fix*: Use minHeight instead of fixed height.


<LinearLayout
    android:id="@+id/serverRow"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:minHeight="48dp"
    android:orientation="horizontal"
    android:paddingVertical="8dp">
    <!-- server name, flag, connect button -->
</LinearLayout>

5️⃣ “Upgrade” banner close button

*Problem*: 20 dp “X” icon in corner of overlay.

*Fix*: Increase touch area with invisible padding.


<ImageButton
    android:id="@+id/btnClose"
    android:layout_width="48dp"
    android:layout_height="48dp"
    android:background="?attr/selectableItemBackgroundBorderless"
    android:src="@drawable/ic_close"
    android:contentDescription="@string/close"
    android:scaleType="center"/>

6️⃣ Help tooltip trigger

*Problem*: 16 dp “i” icon, no surrounding tappable region.

*Fix*: Use TouchDelegate to enlarge the region without changing visual size.


val infoIcon = findViewById<ImageView>(R.id.infoIcon)
post {
    val delegateArea = Rect()
    infoIcon.getHitRect(delegateArea)
    delegateArea.inset(-16, -16) // expand by 16dp each side
    (infoIcon.parent as View).touchDelegate = TouchDelegate(delegateArea, infoIcon)
}

7️⃣ “Resend code” link

*Problem*: Text link only 60 dp wide.

*Fix*: Wrap link in a Button styled as a text link.


<Button
    android:id="@+id/btnResend"
    style="@style/Widget.AppCompat.Button.Borderless"
    android:layout_width="wrap_content"
    android:layout_height="48dp"
    android:minWidth="48dp"
    android:paddingHorizontal="12dp"
    android:text="@string/resend_code"/>

All fixes respect the 48 dp × 48 dp minimum recommended by Android and iOS Human Interface Guidelines, while preserving visual density through internal padding and transparent hit‑areas.

---

6. Prevention: catching small touch targets before release

  1. Design‑phase checklist
  1. Automated CI gate

   name: UI Quality Gate
   on: [pull_request]
   jobs:
     susatest:
       runs-on: ubuntu-latest
       steps:
         - uses: actions/checkout@v3
         - name: Install SUSA Agent
           run: pip install susatest-agent
         - name: Run Touch‑Target Scan
           run: susatest-agent scan apk ./app/build/outputs/apk/release/app-release.apk --persona elderly --rule touch-target
  1. Persona‑based exploratory testing
  1. Static lint rule
  1. Regression script review
  1. Telemetry guard

By embedding these safeguards into design, build, and post‑release monitoring, VPN teams can eliminate small touch targets early, improve accessibility scores, and protect conversion funnels from avoidable friction.

Test Your App Autonomously

Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.

Try SUSA Free