JSN UI Mobile
MobileDrawer, BottomSheet, Swipeable, ResponsiveTabs, TouchTarget.
.skills/jsn-ui-mobile.md980 charsmobile
.skills/jsn-ui-mobile.md
# JSN UI — Mobile
The mobile-first components live in `src/components/mobile/`. The
rest of the kit is responsive by default but these add touch-native
primitives that web defaults don't give you.
## Components
### `MobileDrawer`
Slide-in panel from left / right / bottom. Used for the mobile nav.
```tsx
const [open, setOpen] = useState(false);
<MobileDrawer open={open} onOpenChange={setOpen} side="left">
<nav>...</nav>
</MobileDrawer>
```
Esc closes. Body scroll is locked while open. Touch-friendly.
### `BottomSheet`
Modal that slides up from the bottom on mobile (`< sm` breakpoint),
hidden on larger screens. Has a drag handle (visual) and a close
button. Used for mobile filters, mobile actions, etc.
```tsx
<BottomSheet
open={isOpen}
onOpenChange={setIsOpen}
title="Filters"
description="3 active"
snapHeight={60}
>
{/* filter form */}
</BottomSheet>
```
### `Swipeable`
Adds swipe gesture detection to any element. Reports the gesture
direction via callbacks.
```tsx
<Swipeable
onSwipeLeft={() => goToNext()}
onSwipeRight={() => goToPrev()}
>
<Card>...</Card>
</Swipeable>
```
### `ResponsiveTabs`
Renders as tabs on `≥ sm` and as an accordion on smaller screens.
Same API, same data, different render.
```tsx
<ResponsiveTabs
items={[
{ value: "overview", label: "Overview", content: <p>...</p> },
{ value: "details", label: "Details", content: <p>...</p> },
]}
/>
```
### `TouchTarget`
Wraps any element to ensure it meets the 44×44px minimum touch
target (Apple HIG / WCAG 2.5.5).
```tsx
<TouchTarget asChild>
<button aria-label="Settings">
<Settings size={16} />
</button>
</TouchTarget>
```
## Responsive patterns
- Use `grid-cols-1 sm:grid-cols-2 lg:grid-cols-3` for card grids
- Use `flex-col sm:flex-row` for section headers (title + actions stack on mobile)
- Use `text-3xl sm:text-5xl` for hero headings (down-scale on mobile)
- Use `gap-3 sm:gap-6` for section padding
- Use `p-4 sm:p-6 lg:p-8` for card padding
- Use `hidden sm:inline` to hide labels on mobile icon buttons
- Use `hidden lg:flex` for the desktop sub-nav (we already do this in the header)
## The mobile nav pattern
The site header has a hamburger button on `< lg`. When clicked, it
opens a `MobileDrawer` with:
- The current section's sub-pages
- A "More" section with the cross-section pages
- A Get started button
When the user navigates, the drawer closes automatically (via a
`pathname` effect).
## Breakpoints
We use Tailwind's defaults:
- `sm` — 640px
- `md` — 768px
- `lg` — 1024px
- `xl` — 1280px
- `2xl` — 1536px
The mobile drawer + bottom sheet appear below `sm` / `lg`. The
desktop sub-nav appears at `lg` and up.
## Testing on mobile
- Use Chrome DevTools' device emulation
- Use Safari's Responsive Design Mode on macOS
- Test on a real iPhone (iOS Safari behaves differently from Chrome)
- Test on Android Chrome (Samsung Internet is also worth checking)
- Test landscape orientation
- Test with the keyboard open
- Test with assistive technology (VoiceOver / TalkBack)
How to use this skill
These files live in the .skills/ directory of the JSN UI repo. Open Design–compatible agents (Claude Code, Cursor, Cline, etc.) auto-detect them. You can also reference them directly:
# in your agent's config - name: jsn-ui source: https://github.com/javashn/jsn-ui/tree/main/.skills