curiositech/some_claude_skills4 files

Mobile UX Optimizer

Mobile-first UX optimization for touch interfaces, responsive layouts, and performance. Use for viewport handling, touch targets, gestures, mobile navigation. Activate on mobile, touch, responsive,

Specification
Skill ID
curiositech/some_claude_skills/mobile-ux-optimizer
Publisher
curiositech
Repository
some_claude_skills
Installs
0
Files
4
Synced
Sep 23, 2026
How to use it

Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.

curiositech/some_claude_skills/mobile-ux-optimizerInstalls these files
  • .claude-plugin/plugin.json
  • SKILL.md
  • references/gestures.md
  • references/navigation.md

What this skill tells the agent

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>

Use this skill in a project

Open any RiverX project, search the Skills panel for curiositech/some_claude_skills/mobile-ux-optimizer, and install it in one click.

Use this skill