Responsive Mobile-First
SkillAI & modelsMobile-first responsive patterns with sticky headers, floating CTAs, accessible navigation, and touch-friendly interactions. Use when implementing responsive layouts, mobile navigation, or ensuring touch-friendly UI.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Responsive Mobile-First skill
What this skill tells your AI
The instructions your AI receives, as published by blackbelttechnology/pi-agent-dashboard in packages/frontend-patterns/.pi/skills/responsive-mobile-first/SKILL.md and read by ahel’s review.
Breakpoint Strategy
// Tailwind default breakpoints (mobile-first)
// sm: 640px
// md: 768px
// lg: 1024px
// xl: 1280px
// 2xl: 1536px
// Write base styles for mobile, add breakpoints for larger screens
<div className="
px-4 md:px-6 lg:px-8 // Padding increases
text-sm md:text-base // Font size scales
grid-cols-1 md:grid-cols-2 // Grid expands
">
Sticky Header
// components/layout/Header.tsx
'use client';
import { useState, useEffect } from 'react';
import { cn } from '@/lib/utils';
export function Header() {
const [isScrolled, setIsScrolled] = useState(false);
useEffect(() => {
const handleScroll = () => setIsScrolled(window.scrollY > 10);
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
return (
<header
className={cn(
'fixed top-0 left-0 right-0 z-50 transition-all duration-300',
isScrolled
? 'bg-background/95 backdrop-blur-sm shadow-sm'
: 'bg-transparent'
)}
>
<nav className="container mx-auto px-4 h-16 flex items-center justify-between">
<Logo />
{/* Desktop Navigation */}
<div className="hidden md:flex items-center gap-6">
<NavLinks />
<Button>Book Now</Button>
</div>
{/* Mobile Menu Button */}
<MobileMenuButton className="md:hidden" />
</nav>
</header>
);
}
Mobile Navigation Drawer
'use client';
import { useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { X, Menu } from 'lucide-react';
export function MobileNav() {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<button
onClick={() => setIsOpen(true)}
className="md:hidden p-2"
aria-label="Open menu"
>
<Menu className="w-6 h-6" />
</button>
<AnimatePresence>
{isOpen && (
<>
{/* Backdrop */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={() => setIsOpen(false)}
className="fixed inset-0 bg-black/50 z-50 md:hidden"
/>
{/* Drawer */}
<motion.div
initial={{ x: '100%' }}
animate={{ x: 0 }}
exit={{ x: '100%' }}
transition={{ type: 'tween', duration: 0.3 }}
className="fixed top-0 right-0 bottom-0 w-80 bg-background z-50 md:hidden"
>
<div className="p-4 flex justify-end">
<button
onClick={() => setIsOpen(false)}
className="p-2"
aria-label="Close menu"
>
<X className="w-6 h-6" />
</button>
</div>
<nav className="px-4 space-y-4">
<NavLink href="/" onClick={() => setIsOpen(false)}>
Home
</NavLink>
<NavLink href="/services" onClick={() => setIsOpen(false)}>
Services
</NavLink>
{/* More links */}
</nav>
<div className="absolute bottom-8 left-4 right-4">
<Button className="w-full" size="lg">
Book a Session
</Button>
</div>
</motion.div>
</>
)}
</AnimatePresence>
</>
);
}
Floating Mobile CTA
// components/FloatingCTA.tsx
'use client';
import { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import Link from 'next/link';
import { useLocale } from 'next-intl';
export function FloatingCTA() {
const [isVisible, setIsVisible] = useState(false);
const locale = useLocale();
useEffect(() => {
const handleScroll = () => {
// Show after scrolling past hero
setIsVisible(window.scrollY > 500);
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
return (
<AnimatePresence>
{isVisible && (
<motion.div
initial={{ y: 100, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
exit={{ y: 100, opacity: 0 }}
className="fixed bottom-6 left-4 right-4 z-40 md:hidden"
>
<Link
href={`/${locale}/book`}
className="block w-full bg-primary text-primary-foreground text-center py-4 rounded-full font-medium shadow-lg"
>
Book a Session
</Link>
</motion.div>
)}
</AnimatePresence>
);
}
Responsive Grid Patterns
// 1-2-3 column grid
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 md:gap-6">
{items.map(item => <Card key={item.id} {...item} />)}
</div>
// Sidebar layout
<div className="flex flex-col lg:flex-row gap-8">
<main className="flex-1">{/* Main content */}</main>
<aside className="w-full lg:w-80">{/* Sidebar */}</aside>
</div>
// Hero with text/image
<div className="flex flex-col-reverse md:flex-row items-center gap-8">
<div className="flex-1">{/* Text content */}</div>
<div className="w-full md:w-1/2">{/* Image */}</div>
</div>
Touch-Friendly Targets
// Minimum 44x44px touch targets
<button className="min-h-[44px] min-w-[44px] p-3">
<Icon className="w-5 h-5" />
</button>
// Adequate spacing between interactive elements
<div className="flex gap-3">
<Button>Primary</Button>
<Button variant="outline">Secondary</Button>
</div>
// Large tap areas for cards
<Link href={href} className="block p-4 md:p-6">
<Card />
</Link>
Responsive Typography
// Heading scale
<h1 className="text-3xl md:text-4xl lg:text-5xl xl:text-6xl font-bold">
Main Heading
</h1>
<h2 className="text-2xl md:text-3xl lg:text-4xl font-semibold">
Section Heading
</h2>
// Body text
<p className="text-base md:text-lg leading-relaxed">
Body content with comfortable reading line height.
</p>
// Small text
<span className="text-xs md:text-sm text-muted-foreground">
Caption or meta text
</span>
Responsive Spacing
// Section padding
<section className="py-12 md:py-16 lg:py-24">
// Container with responsive padding
<div className="container mx-auto px-4 md:px-6 lg:px-8">
// Stack spacing
<div className="space-y-4 md:space-y-6 lg:space-y-8">
Hide/Show Utilities
// Show only on mobile
<div className="block md:hidden">Mobile only</div>
// Show only on desktop
<div className="hidden md:block">Desktop only</div>
// Different content per breakpoint
<span className="md:hidden">Menu</span>
<span className="hidden md:inline">Navigation</span>
Responsive Images
import Image from 'next/image';
// Full-width responsive image
<div className="relative aspect-video w-full">
<Image
src="/hero.jpg"
alt="Hero"
fill
className="object-cover"
sizes="100vw"
priority
/>
</div>
// Responsive sizes attribute
<Image
src="/service.jpg"
alt="Service"
fill
sizes="(max-width: 768px) 100vw, (max-width: 1024px) 50vw, 33vw"
/>
Container Component
// components/shared/Container.tsx
import { cn } from '@/lib/utils';
interface ContainerProps {
children: React.ReactNode;
className?: string;
size?: 'default' | 'narrow' | 'wide';
}
export function Container({
children,
className,
size = 'default'
}: ContainerProps) {
return (
<div
className={cn(
'mx-auto px-4 md:px-6',
{
'max-w-7xl': size === 'default',
'max-w-4xl': size === 'narrow',
'max-w-screen-2xl': size === 'wide',
},
className
)}
>
{children}
</div>
);
}
Signals
- GitHub stars
- 283
- Forks
- 41
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
responsive-mobile-first- Source
- github.com/blackbelttechnology/pi-agent-dashboard