Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
erichowens avatar

Mobile Ux Optimizer

  • 349 installs
  • 178 repo stars
  • Updated July 14, 2026
  • erichowens/some_claude_skills

mobile-ux-optimizer is a Claude Code skill that applies mobile-first UX patterns—44px touch targets, 100dvh viewports, safe-area insets, and swipe navigation—for developers shipping responsive web and PWA interfaces.

About

mobile-ux-optimizer is a Claude Code skill from erichowens/some_claude_skills for touch-optimized responsive web UX, not native iOS or Android apps. It encodes Apple’s 44×44pt and Material 48×48dp minimum touch targets, fixes 100vh clipping with 100dvh plus env(safe-area-inset-*), and ships React/Next.js patterns for bottom navigation, slide-out drawers, pull-to-refresh hooks, and Tailwind breakpoints from sm (640px) through 2xl (1536px). The bundled references/ folder adds keyboard-handling.md, animations.md, and accessibility.md for virtual-keyboard and mobile a11y edge cases. Reach for mobile-ux-optimizer when mobile layouts feel broken, tap targets are too small, or notch/home-indicator padding is missing—not for React Native, PWA service workers, or desktop-only CSS.

  • Thumb-zone and tap-target sizing
  • Navigation depth reduction
  • Form and input friction fixes
  • Loading and empty-state polish
  • Platform HIG alignment checks

Mobile Ux Optimizer by the numbers

  • 349 all-time installs (skills.sh)
  • Ranked #731 of 1,880 Design & UI/UX skills by installs in the Skillselion catalog
  • Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/erichowens/some_claude_skills --skill mobile-ux-optimizer

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs349
repo stars178
Last updatedJuly 14, 2026
Repositoryerichowens/some_claude_skills

How do you fix mobile viewport and touch target UX?

Improve tap targets, navigation, and screen flows on existing mobile product surfaces.

Who is it for?

Frontend developers shipping mobile-first React, Next.js, or Tailwind web apps who need concrete touch, viewport, and navigation fixes.

Skip if: Skip mobile-ux-optimizer for native Swift/Kotlin/React Native apps, PWA install/service-worker setup, or general desktop CSS questions.

When should I use this skill?

User mentions mobile UX, touch targets, 100vh/dvh bugs, safe-area notches, bottom navigation, swipe gestures, or responsive breakpoint strategy on web.

What you get

Updated mobile CSS/Tailwind rules, bottom-nav or drawer components, gesture hooks, and a device testing checklist for notch, keyboard, and scroll behavior.

  • Mobile navigation components
  • Viewport and safe-area CSS fixes
  • Device testing checklist

By the numbers

  • Specifies 44×44pt and 48×48dp minimum touch targets
  • Documents 5 Tailwind breakpoints (sm 640px through 2xl 1536px)
  • Includes 3 supplementary reference guides in references/

Files

SKILL.mdMarkdownGitHub ↗

Mobile-First UX Optimization

Build touch-optimized, performant mobile experiences with proper viewport handling and responsive patterns.

When to Use

USE this skill for:

  • Viewport issues (100vh problems, safe areas, notches)
  • Touch target sizing and spacing
  • Mobile navigation patterns (bottom nav, drawers, hamburger menus)
  • Swipe gestures and pull-to-refresh
  • Responsive breakpoint strategies
  • Mobile performance optimization

DO NOT use for:

  • Native app development → use react-native or swift-executor skills
  • Desktop-only features → no skill needed, standard patterns apply
  • General CSS/Tailwind questions → use Tailwind docs or web-design-expert
  • PWA installation/service workers → use pwa-expert skill

Core Principles

Mobile-First Means Build Up, Not Down

/* ❌ ANTI-PATTERN: Desktop-first (scale down) */
.card { width: 400px; }
@media (max-width: 768px) { .card { width: 100%; } }

/* ✅ CORRECT: Mobile-first (scale up) */
.card { width: 100%; }
@media (min-width: 768px) { .card { width: 400px; } }

The 44px Rule

Apple's Human Interface Guidelines specify 44×44 points as minimum touch target. Google Material suggests 48×48dp.

// Touch-friendly button
<button className="min-h-[44px] min-w-[44px] px-4 py-3">
  Tap me
</button>

// Touch-friendly link with adequate padding
<a href="/page" className="inline-block py-3 px-4">
  Link text
</a>

Viewport Handling

The dvh Solution

Mobile browsers have dynamic toolbars. 100vh includes the URL bar, causing content to be cut off.

/* ❌ ANTI-PATTERN: Content hidden behind browser UI */
.full-screen { height: 100vh; }

/* ✅ CORRECT: Responds to browser chrome */
.full-screen { height: 100dvh; }

/* Fallback for older browsers */
.full-screen {
  height: 100vh;
  height: 100dvh;
}

Safe Area Insets (Notches & Home Indicators)

/* Handle iPhone notch and home indicator */
.bottom-nav {
  padding-bottom: env(safe-area-inset-bottom, 0);
}

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

/* Full safe area padding */
.safe-container {
  padding: env(safe-area-inset-top)
           env(safe-area-inset-right)
           env(safe-area-inset-bottom)
           env(safe-area-inset-left);
}

Required meta tag:

<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">

Tailwind Safe Area Classes

// Custom Tailwind utilities (add to globals.css)
@layer utilities {
  .pb-safe { padding-bottom: env(safe-area-inset-bottom); }
  .pt-safe { padding-top: env(safe-area-inset-top); }
  .h-screen-safe { height: calc(100dvh - env(safe-area-inset-top) - env(safe-area-inset-bottom)); }
}

// Usage
<nav className="fixed bottom-0 pb-safe bg-leather-900">
  <BottomNav />
</nav>

Mobile Navigation Patterns

Bottom Navigation (Recommended for Mobile)

// components/BottomNav.tsx
'use client';

import { usePathname } from 'next/navigation';
import Link from 'next/link';

const navItems = [
  { href: '/', icon: HomeIcon, label: 'Home' },
  { href: '/meetings', icon: CalendarIcon, label: 'Meetings' },
  { href: '/tools', icon: ToolsIcon, label: 'Tools' },
  { href: '/my', icon: UserIcon, label: 'My Recovery' },
];

export function BottomNav() {
  const pathname = usePathname();

  return (
    <nav className="fixed bottom-0 left-0 right-0 bg-leather-900 border-t border-leather-700 pb-safe">
      <div className="flex justify-around">
        {navItems.map(({ href, icon: Icon, label }) => {
          const isActive = pathname === href || pathname.startsWith(`${href}/`);
          return (
            <Link
              key={href}
              href={href}
              className={`
                flex flex-col items-center py-2 px-3 min-h-[56px] min-w-[64px]
                ${isActive ? 'text-ember-400' : 'text-leather-400'}
              `}
            >
              <Icon className="w-6 h-6" />
              <span className="text-xs mt-1">{label}</span>
            </Link>
          );
        })}
      </div>
    </nav>
  );
}

Slide-Out Drawer (Side Menu)

'use client';

import { useState, useEffect } from 'react';
import { createPortal } from 'react-dom';

interface DrawerProps {
  isOpen: boolean;
  onClose: () => void;
  children: React.ReactNode;
}

export function Drawer({ isOpen, onClose, children }: DrawerProps) {
  // Prevent body scroll when open
  useEffect(() => {
    if (isOpen) {
      document.body.style.overflow = 'hidden';
    }
    return () => {
      document.body.style.overflow = '';
    };
  }, [isOpen]);

  // Close on escape
  useEffect(() => {
    const handleEscape = (e: KeyboardEvent) => {
      if (e.key === 'Escape') onClose();
    };
    document.addEventListener('keydown', handleEscape);
    return () => document.removeEventListener('keydown', handleEscape);
  }, [onClose]);

  if (!isOpen) return null;

  return createPortal(
    <div className="fixed inset-0 z-50">
      {/* Backdrop */}
      <div
        className="absolute inset-0 bg-black/60 backdrop-blur-sm"
        onClick={onClose}
        aria-hidden="true"
      />

      {/* Drawer */}
      <div
        className="absolute left-0 top-0 h-full w-[280px] max-w-[80vw]
                   bg-leather-900 shadow-xl transform transition-transform
                   animate-slide-in-left"
        role="dialog"
        aria-modal="true"
      >
        <div className="h-full overflow-y-auto pt-safe pb-safe">
          {children}
        </div>
      </div>
    </div>,
    document.body
  );
}

Touch Gestures

Full implementations in `references/gestures.md`
HookPurpose
useSwipe()Directional swipe detection with configurable threshold
usePullToRefresh()Pull-to-refresh with visual feedback and resistance

Quick usage:

// Swipe to dismiss
const { handleTouchStart, handleTouchEnd } = useSwipe({
  onSwipeLeft: () => dismiss(),
  threshold: 50,
});

// Pull to refresh
const { containerRef, pullDistance, isRefreshing, handlers } = 
  usePullToRefresh(async () => await refetchData());

Mobile Performance

Image Optimization

import Image from 'next/image';

// Responsive images with proper sizing
<Image
  src="/hero.jpg"
  alt="Hero"
  fill
  sizes="(max-width: 768px) 100vw, 50vw"
  priority // For above-the-fold images
  className="object-cover"
/>

// Lazy load below-fold images
<Image
  src="/feature.jpg"
  alt="Feature"
  width={400}
  height={300}
  loading="lazy"
/>

Reduce Bundle Size

// Dynamic imports for heavy components
const HeavyChart = dynamic(() => import('@/components/Chart'), {
  loading: () => <ChartSkeleton />,
  ssr: false, // Skip server render for client-only
});

// Lazy load below-fold sections
const Comments = dynamic(() => import('@/components/Comments'));

Skeleton Screens (Not Spinners)

// Skeleton that matches final content layout
function MeetingCardSkeleton() {
  return (
    <div className="p-4 bg-leather-800 rounded-lg animate-pulse">
      <div className="h-4 bg-leather-700 rounded w-3/4 mb-2" />
      <div className="h-3 bg-leather-700 rounded w-1/2 mb-4" />
      <div className="flex gap-2">
        <div className="h-6 w-16 bg-leather-700 rounded" />
        <div className="h-6 w-16 bg-leather-700 rounded" />
      </div>
    </div>
  );
}

// Usage
{isLoading ? (
  <div className="space-y-4">
    {[...Array(5)].map((_, i) => <MeetingCardSkeleton key={i} />)}
  </div>
) : (
  meetings.map(m => <MeetingCard key={m.id} meeting={m} />)
)}

Responsive Patterns

Tailwind Breakpoint Strategy

sm: 640px   - Large phones (landscape)
md: 768px   - Tablets
lg: 1024px  - Small laptops
xl: 1280px  - Desktops
2xl: 1536px - Large screens
// Mobile: stack, Tablet+: side-by-side
<div className="flex flex-col md:flex-row gap-4">
  <aside className="w-full md:w-64">Sidebar</aside>
  <main className="flex-1">Content</main>
</div>

// Mobile: bottom nav, Desktop: sidebar
<nav className="md:hidden fixed bottom-0 left-0 right-0">
  <BottomNav />
</nav>
<aside className="hidden md:block w-64">
  <SidebarNav />
</aside>

Container Queries (CSS-only Responsive Components)

/* Component responds to its container, not viewport */
@container (min-width: 400px) {
  .card { flex-direction: row; }
}
<div className="@container">
  <div className="flex flex-col @md:flex-row">
    {/* Responds to parent container width */}
  </div>
</div>

Testing on Real Devices

Chrome DevTools Mobile Emulation

1. Open DevTools (F12) 2. Toggle device toolbar (Ctrl+Shift+M) 3. Select device or set custom dimensions 4. Throttle network/CPU for realistic performance

Must-Test Scenarios

  • [ ] Content doesn't get cut off by notch/home indicator
  • [ ] Touch targets are at least 44×44px
  • [ ] Scrolling is smooth (no jank)
  • [ ] Bottom nav doesn't block content
  • [ ] Forms work with virtual keyboard visible
  • [ ] Landscape orientation works
  • [ ] Pull-to-refresh doesn't fight with scroll

BrowserStack/Real Device Testing

# Expose local dev server to internet
npx localtunnel --port 3000
# or
ngrok http 3000

Quick Reference

IssueSolution
Content cut off at bottomUse 100dvh instead of 100vh
Notch overlaps contentAdd pt-safe / pb-safe
Touch targets too smallMin 44×44px
Scroll lockedCheck overflow: hidden on body
Keyboard covers inputUse visualViewport API
Janky scrollingUse will-change: transform
Double-tap zoomAdd touch-action: manipulation

References

See /references/ for detailed guides:

  • keyboard-handling.md - Virtual keyboard and form UX
  • animations.md - Touch-friendly animations
  • accessibility.md - Mobile a11y requirements

Related skills

How it compares

Pick mobile-ux-optimizer for responsive web touch and viewport fixes; use react-native or stream-swift skills for native mobile SDK integration.

FAQ

What touch target size does mobile-ux-optimizer recommend?

mobile-ux-optimizer follows Apple Human Interface Guidelines at 44×44 points and Google Material guidance at 48×48dp. The skill ships Tailwind min-h-[44px] min-w-[44px] button and link examples so tap areas meet platform minimums.

Does mobile-ux-optimizer cover native mobile apps?

mobile-ux-optimizer targets mobile-first responsive web and PWA surfaces using CSS, Tailwind, and React/Next.js. The skill explicitly directs native iOS or Android work to react-native or swift-executor skills instead.

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.