mirror of
https://github.com/cloud-shuttle/leptos-shadcn-ui.git
synced 2026-05-31 10:50:41 +00:00
drover: Merge task-1767765269702426639
This commit is contained in:
@@ -18,12 +18,13 @@ Deep dive into the technical design:
|
||||
- [Coverage Analysis](./architecture/coverage/) - Test coverage documentation
|
||||
- [Performance Analysis](./architecture/performance/) - Performance benchmarks and optimization
|
||||
|
||||
### 🧩 [Components](./components/README.md)
|
||||
### 🧩 [Components](./components/)
|
||||
Component library reference:
|
||||
- [API Reference](./components/api-reference/)
|
||||
- [Examples](./components/examples/)
|
||||
- [Accessibility Guide](./components/accessibility/)
|
||||
- [Theming Guide](./components/theming/)
|
||||
- [Mobile Design Guidelines](./components/mobile-design-guidelines.md) - Responsive design patterns and best practices
|
||||
|
||||
### 🎓 [Tutorials](./tutorials/README.md)
|
||||
Step-by-step video and written tutorials:
|
||||
|
||||
821
docs/components/mobile-design-guidelines.md
Normal file
821
docs/components/mobile-design-guidelines.md
Normal file
@@ -0,0 +1,821 @@
|
||||
# Mobile Design Guidelines
|
||||
|
||||
This guide documents the mobile-first design patterns and responsive design principles used throughout the Leptos ShadCN UI component library.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Overview](#overview)
|
||||
- [Design Philosophy](#design-philosophy)
|
||||
- [Breakpoint System](#breakpoint-system)
|
||||
- [Core Patterns](#core-patterns)
|
||||
- [Component-Specific Guidelines](#component-specific-guidelines)
|
||||
- [Layout Patterns](#layout-patterns)
|
||||
- [Typography](#typography)
|
||||
- [Spacing](#spacing)
|
||||
- [Touch Targets](#touch-targets)
|
||||
- [Performance Considerations](#performance-considerations)
|
||||
- [Testing Responsive Designs](#testing-responsive-designs)
|
||||
- [Examples](#examples)
|
||||
|
||||
## Overview
|
||||
|
||||
Leptos ShadCN UI follows a **mobile-first** approach using Tailwind CSS's responsive utility classes. All components are designed to work seamlessly on mobile devices (320px+) and progressively enhance for larger screens.
|
||||
|
||||
### Key Principles
|
||||
|
||||
1. **Mobile First**: Start with mobile layouts, enhance for larger screens
|
||||
2. **Touch Friendly**: All interactive elements meet minimum touch target sizes
|
||||
3. **Progressive Enhancement**: Add complexity only when screen real estate allows
|
||||
4. **Performance First**: Optimize for mobile network conditions and device constraints
|
||||
5. **Accessible**: Maintain WCAG 2.1 AA compliance across all viewport sizes
|
||||
|
||||
## Design Philosophy
|
||||
|
||||
### Mobile-First Approach
|
||||
|
||||
We design for the smallest screen first, then add complexity for larger screens. This ensures:
|
||||
|
||||
- Core functionality works on all devices
|
||||
- Performance is optimized for mobile constraints
|
||||
- Content is prioritized over decoration
|
||||
- Progressive enhancement is natural
|
||||
|
||||
### Responsive Breakpoints
|
||||
|
||||
Our breakpoint system aligns with common device sizes:
|
||||
|
||||
| Breakpoint | Min Width | Target Devices | Use Case |
|
||||
|------------|-----------|----------------|----------|
|
||||
| (default) | 0px | Mobile phones | Base styles, single column layouts |
|
||||
| `sm:` | 640px | Large phones, landscape | Two-column grids, enhanced spacing |
|
||||
| `md:` | 768px | Tablets | Multi-column layouts, sidebars |
|
||||
| `lg:` | 1024px | Laptops, small desktops | Full navigation, complex grids |
|
||||
| `xl:` | 1280px | Desktops | Maximum content width, 4+ columns |
|
||||
| `2xl:` | 1536px | Large desktops | Maximum layout width |
|
||||
|
||||
## Breakpoint System
|
||||
|
||||
### Default Configuration
|
||||
|
||||
```javascript
|
||||
// tailwind.config.js
|
||||
theme: {
|
||||
screens: {
|
||||
'sm': '640px',
|
||||
'md': '768px',
|
||||
'lg': '1024px',
|
||||
'xl': '1280px',
|
||||
'2xl': '1536px',
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Usage Pattern
|
||||
|
||||
Always write mobile styles first (no prefix), then enhance:
|
||||
|
||||
```rust
|
||||
// ✅ CORRECT: Mobile-first
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
|
||||
// ❌ WRONG: Desktop-first thinking
|
||||
<div class="grid grid-cols-3 md:grid-cols-2 lg:grid-cols-1 gap-4">
|
||||
```
|
||||
|
||||
## Core Patterns
|
||||
|
||||
### 1. Responsive Grids
|
||||
|
||||
The most common pattern for responsive layouts:
|
||||
|
||||
```rust
|
||||
// Single column mobile, 2 columns tablet, 3-4 columns desktop
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
|
||||
```
|
||||
|
||||
#### Examples by Use Case
|
||||
|
||||
**Card Grids** (components/api/card.md:189):
|
||||
```rust
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
// Card components
|
||||
</div>
|
||||
```
|
||||
|
||||
**Feature Sections** (comprehensive_demo.rs:156):
|
||||
```rust
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-8">
|
||||
// Feature items
|
||||
</div>
|
||||
```
|
||||
|
||||
**Dashboard Layouts** (dashboard-demo/src/lib.rs:195):
|
||||
```rust
|
||||
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
||||
// Stats cards
|
||||
</div>
|
||||
```
|
||||
|
||||
### 2. Responsive Spacing
|
||||
|
||||
Adjust padding and margins based on screen size:
|
||||
|
||||
```rust
|
||||
// Minimal padding on mobile, more on larger screens
|
||||
<div class="px-4 sm:px-6 lg:px-8">
|
||||
|
||||
// Spacing between sections
|
||||
<div class="space-y-4 md:space-y-6 lg:space-y-8">
|
||||
```
|
||||
|
||||
### 3. Responsive Typography
|
||||
|
||||
Scale text size appropriately:
|
||||
|
||||
```rust
|
||||
// Headings
|
||||
<h1 class="text-2xl md:text-4xl lg:text-5xl font-bold">
|
||||
<h2 class="text-xl md:text-2xl lg:text-3xl font-semibold">
|
||||
|
||||
// Body text
|
||||
<p class="text-sm md:text-base lg:text-lg">
|
||||
```
|
||||
|
||||
### 4. Hiding/Showing Content
|
||||
|
||||
Conditionally display elements by screen size:
|
||||
|
||||
```rust
|
||||
// Mobile-only elements
|
||||
<div class="block md:hidden lg:hidden">
|
||||
|
||||
// Tablet and desktop only
|
||||
<div class="hidden md:block">
|
||||
|
||||
// Desktop only
|
||||
<div class="hidden lg:block">
|
||||
```
|
||||
|
||||
### 5. Responsive Dialogs/Modals
|
||||
|
||||
Size dialogs appropriately for the viewport:
|
||||
|
||||
```rust
|
||||
// Mobile: full width, Desktop: constrained width
|
||||
<DialogContent class="sm:max-w-[425px]">
|
||||
```
|
||||
|
||||
## Component-Specific Guidelines
|
||||
|
||||
### Navigation Components
|
||||
|
||||
#### Navigation Menu
|
||||
|
||||
- **Mobile**: Collapsed into hamburger menu
|
||||
- **Tablet+**: Full horizontal navigation
|
||||
|
||||
```rust
|
||||
// Mobile menu trigger
|
||||
<button class="md:hidden" aria-label="Open menu">
|
||||
|
||||
// Desktop navigation
|
||||
<nav class="hidden md:flex">
|
||||
```
|
||||
|
||||
#### Breadcrumb
|
||||
|
||||
- **Mobile**: Truncate with ellipsis, show last item
|
||||
- **Tablet+:** Show full path
|
||||
|
||||
```rust
|
||||
<div class="flex items-center space-x-2 text-sm">
|
||||
<BreadcrumbItem class="hidden sm:inline-block">Home</BreadcrumbItem>
|
||||
<BreadcrumbItem class="hidden sm:inline-block">Products</BreadcrumbItem>
|
||||
<BreadcrumbItem>Current Page</BreadcrumbItem>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Form Components
|
||||
|
||||
#### Forms
|
||||
|
||||
- **Mobile**: Single column, stacked labels
|
||||
- **Tablet+**: Two-column grids for related fields
|
||||
- **Desktop**: Can use 3-4 columns for complex forms
|
||||
|
||||
```rust
|
||||
<form class="space-y-4 md:space-y-6">
|
||||
// Single column on mobile
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<FormField>
|
||||
<FormLabel>First Name</FormLabel>
|
||||
<Input />
|
||||
</FormField>
|
||||
<FormField>
|
||||
<FormLabel>Last Name</FormLabel>
|
||||
<Input />
|
||||
</FormField>
|
||||
</div>
|
||||
</form>
|
||||
```
|
||||
|
||||
#### Input Fields
|
||||
|
||||
- **Minimum width**: Never constrain on mobile
|
||||
- **Maximum width**: Constrain on desktop for readability
|
||||
|
||||
```rust
|
||||
<Input class="w-full md:max-w-md" />
|
||||
```
|
||||
|
||||
#### Select/Dropdown
|
||||
|
||||
- **Mobile**: Native select or full-screen picker
|
||||
- **Desktop**: Dropdown with constrained width
|
||||
|
||||
### Layout Components
|
||||
|
||||
#### Cards
|
||||
|
||||
- **Mobile**: Full width, stacked content
|
||||
- **Tablet+**: Grid layouts (2-3 columns)
|
||||
- **Desktop**: 4 columns maximum
|
||||
|
||||
```rust
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Title</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>Content</CardContent>
|
||||
</Card>
|
||||
// More cards...
|
||||
</div>
|
||||
```
|
||||
|
||||
#### Sheet/Drawer
|
||||
|
||||
- **Mobile**: Full width from bottom
|
||||
- **Tablet+:** Fixed width from side
|
||||
|
||||
```rust
|
||||
<Sheet>
|
||||
<SheetContent class="w-full sm:max-w-sm">
|
||||
// Content
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
```
|
||||
|
||||
#### Dialog
|
||||
|
||||
- **Mobile**: Full viewport with margins
|
||||
- **Tablet+**: Constrained max-width
|
||||
|
||||
```rust
|
||||
<DialogContent class="sm:max-w-[425px]">
|
||||
// Dialog content
|
||||
</DialogContent>
|
||||
```
|
||||
|
||||
### Data Display
|
||||
|
||||
#### Tables
|
||||
|
||||
- **Mobile**: Stack rows or enable horizontal scroll
|
||||
- **Tablet+**: Standard table layout
|
||||
|
||||
```rust
|
||||
// Mobile: Card-based rows
|
||||
<div class="md:hidden">
|
||||
// Repeated card layout for each row
|
||||
</div>
|
||||
|
||||
// Desktop: Standard table
|
||||
<div class="hidden md:block overflow-x-auto">
|
||||
<Table>
|
||||
// Table content
|
||||
</Table>
|
||||
</div>
|
||||
```
|
||||
|
||||
#### Tabs
|
||||
|
||||
- **Mobile**: Scrollable horizontal tabs
|
||||
- **Desktop**: Full-width tabs
|
||||
|
||||
```rust
|
||||
<TabsList class="w-full justify-start overflow-x-auto">
|
||||
<TabsTrigger>Tab 1</TabsTrigger>
|
||||
<TabsTrigger>Tab 2</TabsTrigger>
|
||||
</TabsList>
|
||||
```
|
||||
|
||||
### Feedback Components
|
||||
|
||||
#### Alerts/Toasts
|
||||
|
||||
- **Mobile**: Full width at bottom/top
|
||||
- **Desktop**: Constrained width, positioned
|
||||
|
||||
```rust
|
||||
<Alert class="mx-4 md:mx-0 md:max-w-md">
|
||||
<AlertDescription>Message</AlertDescription>
|
||||
</Alert>
|
||||
```
|
||||
|
||||
#### Progress Indicators
|
||||
|
||||
- **Mobile**: Full width
|
||||
- **Desktop**: Constrained width or centered
|
||||
|
||||
```rust
|
||||
<Progress class="w-full md:w-96" value={progress} />
|
||||
```
|
||||
|
||||
## Layout Patterns
|
||||
|
||||
### Container Pattern
|
||||
|
||||
Center content with responsive max-width:
|
||||
|
||||
```rust
|
||||
<div class="container mx-auto px-4 sm:px-6 lg:px-8">
|
||||
// Content
|
||||
</div>
|
||||
```
|
||||
|
||||
### Sidebar Layout
|
||||
|
||||
Responsive sidebar that collapses on mobile:
|
||||
|
||||
```rust
|
||||
<div class="flex flex-col md:flex-row">
|
||||
<aside class="w-full md:w-64 mb-4 md:mb-0 md:mr-6">
|
||||
// Sidebar content
|
||||
</aside>
|
||||
<main class="flex-1">
|
||||
// Main content
|
||||
</main>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Stacked to Side-by-Side
|
||||
|
||||
Move from stacked layout to side-by-side:
|
||||
|
||||
```rust
|
||||
<div class="flex flex-col lg:flex-row gap-6">
|
||||
<div class="flex-1">
|
||||
// Primary content
|
||||
</div>
|
||||
<div class="w-full lg:w-80">
|
||||
// Secondary content
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Typography
|
||||
|
||||
### Responsive Font Sizes
|
||||
|
||||
```rust
|
||||
// Display headings
|
||||
<h1 class="text-3xl md:text-5xl lg:text-7xl font-bold">
|
||||
|
||||
// Section headings
|
||||
<h2 class="text-2xl md:text-3xl lg:text-4xl font-bold">
|
||||
|
||||
// Subsection headings
|
||||
<h3 class="text-xl md:text-2xl font-semibold">
|
||||
|
||||
// Body text
|
||||
<p class="text-sm md:text-base">
|
||||
|
||||
// Small text
|
||||
<small class="text-xs md:text-sm">
|
||||
```
|
||||
|
||||
### Line Height
|
||||
|
||||
Maintain readability across devices:
|
||||
|
||||
```rust
|
||||
// Headings: tighter
|
||||
<h1 class="leading-tight">
|
||||
|
||||
// Body: comfortable
|
||||
<p class="leading-relaxed md:leading-normal">
|
||||
```
|
||||
|
||||
### Text Truncation
|
||||
|
||||
Handle long text on mobile:
|
||||
|
||||
```rust
|
||||
// Truncate with ellipsis
|
||||
<p class="truncate">Long text that needs truncation</p>
|
||||
|
||||
// Line clamping
|
||||
<p class="line-clamp-2 md:line-clamp-3">
|
||||
Long content that should be limited to specific lines
|
||||
</p>
|
||||
```
|
||||
|
||||
## Spacing
|
||||
|
||||
### Spacing Scale
|
||||
|
||||
Use consistent spacing units:
|
||||
|
||||
| Mobile | Tablet | Desktop | Use Case |
|
||||
|--------|--------|---------|----------|
|
||||
| `p-2` | `p-3` | `p-4` | Tight spacing |
|
||||
| `p-4` | `p-6` | `p-8` | Comfortable spacing |
|
||||
| `gap-2`| `gap-4`| `gap-6` | Grid gaps |
|
||||
|
||||
### Responsive Margins
|
||||
|
||||
```rust
|
||||
// Section margins
|
||||
<section class="my-8 md:my-12 lg:my-16">
|
||||
|
||||
// Element spacing
|
||||
<div class="space-y-4 md:space-y-6 lg:space-y-8">
|
||||
```
|
||||
|
||||
### Padding
|
||||
|
||||
```rust
|
||||
// Container padding
|
||||
<div class="p-4 sm:p-6 lg:p-8">
|
||||
|
||||
// Card padding
|
||||
<Card class="p-4 md:p-6">
|
||||
```
|
||||
|
||||
## Touch Targets
|
||||
|
||||
### Minimum Sizes
|
||||
|
||||
Ensure all interactive elements meet minimum touch targets:
|
||||
|
||||
- **Minimum size**: 44x44 pixels (iOS) / 48x48 pixels (Android)
|
||||
- **Recommended**: 48x48 pixels for better accessibility
|
||||
|
||||
### Button Sizing
|
||||
|
||||
```rust
|
||||
// Default buttons meet requirements
|
||||
<Button class="h-10 px-4">Click</Button> // 40px height - acceptable
|
||||
|
||||
// Better: Larger touch targets
|
||||
<Button class="h-12 px-6 md:h-10 md:px-4">Click</Button> // 48px on mobile
|
||||
```
|
||||
|
||||
### Icon Buttons
|
||||
|
||||
```rust
|
||||
// Add padding to meet minimum
|
||||
<IconButton class="p-3 md:p-2">
|
||||
<Icon class="w-6 h-6" />
|
||||
</IconButton>
|
||||
```
|
||||
|
||||
### Spacing Between Targets
|
||||
|
||||
Maintain minimum 8px between touch targets:
|
||||
|
||||
```rust
|
||||
<div class="flex gap-4 md:gap-2">
|
||||
<Button>Button 1</Button>
|
||||
<Button>Button 2</Button>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Image Optimization
|
||||
|
||||
```rust
|
||||
// Use responsive images
|
||||
<img
|
||||
class="w-full h-auto"
|
||||
src="image-small.jpg"
|
||||
srcset="image-small.jpg 640w, image-medium.jpg 1024w, image-large.jpg 1536w"
|
||||
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
|
||||
/>
|
||||
|
||||
// Or use aspect ratio containers
|
||||
<AspectRatio ratio={16 / 9}>
|
||||
<img src="image.jpg" class="object-cover w-full h-full" />
|
||||
</AspectRatio>
|
||||
```
|
||||
|
||||
### Lazy Loading
|
||||
|
||||
```rust
|
||||
// Load components only when needed
|
||||
<Show when=move || is_large_screen()>
|
||||
<DesktopOnlyComponent />
|
||||
</Show>
|
||||
```
|
||||
|
||||
### CSS Optimization
|
||||
|
||||
```rust
|
||||
// Prefer utility classes over inline styles
|
||||
<div class="flex flex-col md:flex-row"> // ✅ Good
|
||||
|
||||
// Avoid heavy computations
|
||||
<div style={move || format!("width: {}px", width.get())}> // ❌ Bad
|
||||
```
|
||||
|
||||
### Reduce Bundle Size
|
||||
|
||||
```rust
|
||||
// Only import what you need
|
||||
use leptos_shadcn_ui::{Button, Input}; // ✅ Good
|
||||
|
||||
use leptos_shadcn_ui::prelude::*; // ❌ Bad - tree-shaking limited
|
||||
```
|
||||
|
||||
## Testing Responsive Designs
|
||||
|
||||
### Browser DevTools
|
||||
|
||||
1. Open DevTools (F12)
|
||||
2. Toggle device toolbar (Ctrl+Shift+M / Cmd+Shift+M)
|
||||
3. Test common breakpoints:
|
||||
- 375px (iPhone SE)
|
||||
- 390px (iPhone 12/13)
|
||||
- 768px (iPad)
|
||||
- 1024px (iPad Pro, small laptop)
|
||||
- 1440px (desktop)
|
||||
|
||||
### Automated Testing
|
||||
|
||||
Use Playwright for responsive testing:
|
||||
|
||||
```typescript
|
||||
// tests/e2e/responsive.spec.ts
|
||||
test.describe('Responsive Design', () => {
|
||||
const sizes = [
|
||||
{ width: 375, height: 667 }, // Mobile
|
||||
{ width: 768, height: 1024 }, // Tablet
|
||||
{ width: 1440, height: 900 }, // Desktop
|
||||
];
|
||||
|
||||
sizes.forEach(size => {
|
||||
test(`Layout at ${size.width}px`, async ({ page }) => {
|
||||
await page.setViewportSize(size);
|
||||
await page.goto('/');
|
||||
|
||||
// Test mobile-specific behavior
|
||||
if (size.width < 768) {
|
||||
await expect(page.locator('.mobile-menu')).toBeVisible();
|
||||
} else {
|
||||
await expect(page.locator('.desktop-nav')).toBeVisible();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Accessibility Testing
|
||||
|
||||
Ensure touch targets are accessible:
|
||||
|
||||
```bash
|
||||
# Use axe DevTools
|
||||
npm run test:a11y
|
||||
|
||||
# Manual checklist
|
||||
- [ ] All buttons >= 44x44px on mobile
|
||||
- [ ] Touch targets have 8px spacing
|
||||
- [ ] No horizontal scrolling on mobile (except intentional)
|
||||
- [ ] Text is readable without zooming
|
||||
- [ ] Forms are easy to complete on mobile
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Complete Mobile-First Component
|
||||
|
||||
```rust
|
||||
use leptos::*;
|
||||
use leptos_shadcn_ui::*;
|
||||
|
||||
#[component]
|
||||
pub fn ResponsiveDashboard() -> impl IntoView {
|
||||
view! {
|
||||
<div class="min-h-screen bg-background">
|
||||
// Responsive header
|
||||
<header class="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur">
|
||||
<div class="container flex h-16 items-center px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex flex-1 items-center justify-between">
|
||||
<Logo class="h-8 w-auto" />
|
||||
|
||||
// Desktop navigation
|
||||
<nav class="hidden md:flex items-center space-x-6">
|
||||
<a href="/" class="text-sm font-medium">Home</a>
|
||||
<a href="/dashboard" class="text-sm font-medium">Dashboard</a>
|
||||
<a href="/settings" class="text-sm font-medium">Settings</a>
|
||||
</nav>
|
||||
|
||||
// Mobile menu trigger
|
||||
<button class="md:hidden p-2" aria-label="Open menu">
|
||||
<MenuIcon class="h-6 w-6" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
// Main content area
|
||||
<main class="container px-4 py-6 sm:px-6 lg:px-8">
|
||||
// Page header
|
||||
<div class="mb-8">
|
||||
<h1 class="text-2xl font-bold tracking-tight sm:text-3xl lg:text-4xl">
|
||||
Dashboard
|
||||
</h1>
|
||||
<p class="mt-2 text-sm text-muted-foreground sm:text-base">
|
||||
Welcome to your dashboard
|
||||
</p>
|
||||
</div>
|
||||
|
||||
// Stats grid - responsive columns
|
||||
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-4 mb-8">
|
||||
<Card class="p-6">
|
||||
<CardHeader class="pb-2">
|
||||
<CardTitle class="text-sm font-medium">Total Users</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div class="text-2xl font-bold">1,234</div>
|
||||
<p class="text-xs text-muted-foreground">+12% from last month</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
// More stat cards...
|
||||
</div>
|
||||
|
||||
// Main content - stacked on mobile, side-by-side on desktop
|
||||
<div class="grid gap-6 lg:grid-cols-7">
|
||||
// Primary content
|
||||
<div class="lg:col-span-5 space-y-6">
|
||||
<Card class="p-4 sm:p-6">
|
||||
<CardHeader>
|
||||
<CardTitle class="text-lg">Recent Activity</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div class="space-y-4">
|
||||
// Activity items
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
// Sidebar content
|
||||
<div class="lg:col-span-2 space-y-6">
|
||||
<Card class="p-4 sm:p-6">
|
||||
<CardHeader>
|
||||
<CardTitle class="text-lg">Quick Actions</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent class="space-y-2">
|
||||
<Button class="w-full justify-start" size="sm">
|
||||
Create New
|
||||
</Button>
|
||||
<Button class="w-full justify-start" variant="outline" size="sm">
|
||||
Import Data
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Responsive Form Example
|
||||
|
||||
```rust
|
||||
#[component]
|
||||
pub fn ResponsiveForm() -> impl IntoView {
|
||||
view! {
|
||||
<form class="space-y-6">
|
||||
<div>
|
||||
<h2 class="text-xl font-semibold mb-4 sm:text-2xl">User Information</h2>
|
||||
|
||||
// Name fields - side-by-side on tablet and up
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<FormField>
|
||||
<FormLabel>First Name</FormLabel>
|
||||
<Input class="w-full" placeholder="John" />
|
||||
</FormField>
|
||||
|
||||
<FormField>
|
||||
<FormLabel>Last Name</FormLabel>
|
||||
<Input class="w-full" placeholder="Doe" />
|
||||
</FormField>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
// Contact section - responsive grid
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold mb-4">Contact Information</h3>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<FormField>
|
||||
<FormLabel>Email</FormLabel>
|
||||
<Input type="email" class="w-full" placeholder="john@example.com" />
|
||||
</FormField>
|
||||
|
||||
<FormField>
|
||||
<FormLabel>Phone</FormLabel>
|
||||
<Input type="tel" class="w-full" placeholder="+1 234 567 8900" />
|
||||
</FormField>
|
||||
|
||||
<FormField class="md:col-span-2">
|
||||
<FormLabel>Address</FormLabel>
|
||||
<Input class="w-full" placeholder="123 Main St" />
|
||||
</FormField>
|
||||
|
||||
<FormField>
|
||||
<FormLabel>City</FormLabel>
|
||||
<Input class="w-full" placeholder="New York" />
|
||||
</FormField>
|
||||
|
||||
<FormField>
|
||||
<FormLabel>State</FormLabel>
|
||||
<Input class="w-full" placeholder="NY" />
|
||||
</FormField>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
// Form actions - stacked on mobile, side-by-side on desktop
|
||||
<div class="flex flex-col-reverse sm:flex-row gap-3 sm:justify-end">
|
||||
<Button variant="outline" class="w-full sm:w-auto">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button class="w-full sm:w-auto">
|
||||
Save Changes
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Checklist for Mobile-First Development
|
||||
|
||||
Use this checklist when creating or modifying components:
|
||||
|
||||
### Layout
|
||||
- [ ] Starts with single-column layout for mobile
|
||||
- [ ] Uses responsive breakpoints correctly (md: not md:)
|
||||
- [ ] Content doesn't overflow horizontally
|
||||
- [ ] Vertical scrolling works naturally
|
||||
|
||||
### Typography
|
||||
- [ ] Base font size is readable (16px minimum)
|
||||
- [ ] Headings scale appropriately
|
||||
- [ ] Line height is comfortable
|
||||
- [ ] Text truncation handles long content
|
||||
|
||||
### Spacing
|
||||
- [ ] Padding is appropriate for mobile (not too tight)
|
||||
- [ ] Touch targets meet minimum size (44x44px)
|
||||
- [ ] Spacing between interactive elements (8px minimum)
|
||||
|
||||
### Navigation
|
||||
- [ ] Menu is accessible on mobile
|
||||
- [ ] Breadcrumbs don't break layout
|
||||
- [ ] Back/buttons are easily accessible
|
||||
|
||||
### Forms
|
||||
- [ ] Input fields are full width on mobile
|
||||
- [ ] Labels are positioned correctly
|
||||
- [ ] Error messages are visible
|
||||
- [ ] Submit buttons are prominent
|
||||
|
||||
### Performance
|
||||
- [ ] Images are optimized
|
||||
- [ ] No unnecessary re-renders
|
||||
- [ ] Bundle size is minimized
|
||||
- [ ] Lazy loading used appropriately
|
||||
|
||||
### Accessibility
|
||||
- [ ] All interactive elements are keyboard accessible
|
||||
- [ ] Touch targets meet WCAG guidelines
|
||||
- [ ] Color contrast meets AA standards
|
||||
- [ ] Screen reader announces content correctly
|
||||
|
||||
## Resources
|
||||
|
||||
- [Tailwind CSS Responsive Design](https://tailwindcss.com/docs/responsive-design)
|
||||
- [MDN: Responsive Design](https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Responsive_Design)
|
||||
- [WCAG 2.1 Guidelines](https://www.w3.org/WAI/WCAG21/quickref/)
|
||||
- [Mobile Design Patterns](https://mobilepatterns.com/)
|
||||
|
||||
---
|
||||
|
||||
*Last updated: January 2026*
|
||||
92
examples/mobile-first-demo/src/lib.rs
Normal file
92
examples/mobile-first-demo/src/lib.rs
Normal file
@@ -0,0 +1,92 @@
|
||||
// This example demonstrates mobile-first design patterns
|
||||
// following the guidelines in docs/components/mobile-design-guidelines.md
|
||||
|
||||
use leptos::*;
|
||||
use leptos_shadcn_ui::*;
|
||||
|
||||
#[component]
|
||||
pub fn MobileFirstExample() -> impl IntoView {
|
||||
view! {
|
||||
// Example 1: Responsive Grid
|
||||
// Single column on mobile, 2 on tablet, 3-4 on desktop
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
|
||||
<Card class="p-4">
|
||||
<CardContent>
|
||||
<h3 class="font-semibold">Card 1</h3>
|
||||
<p class="text-sm text-muted-foreground">Content here</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card class="p-4">
|
||||
<CardContent>
|
||||
<h3 class="font-semibold">Card 2</h3>
|
||||
<p class="text-sm text-muted-foreground">Content here</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card class="p-4">
|
||||
<CardContent>
|
||||
<h3 class="font-semibold">Card 3</h3>
|
||||
<p class="text-sm text-muted-foreground">Content here</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card class="p-4">
|
||||
<CardContent>
|
||||
<h3 class="font-semibold">Card 4</h3>
|
||||
<p class="text-sm text-muted-foreground">Content here</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
// Example 2: Responsive Typography
|
||||
<div class="mt-8">
|
||||
<h1 class="text-2xl font-bold md:text-4xl lg:text-5xl">
|
||||
Responsive Heading
|
||||
</h1>
|
||||
<p class="mt-2 text-sm md:text-base lg:text-lg text-muted-foreground">
|
||||
This text scales appropriately across device sizes.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
// Example 3: Responsive Spacing
|
||||
<div class="px-4 py-6 sm:px-6 sm:py-8 lg:px-8 lg:py-12">
|
||||
<p>Container with responsive padding</p>
|
||||
</div>
|
||||
|
||||
// Example 4: Touch-Friendly Buttons
|
||||
// 48px height on mobile (meets minimum touch target)
|
||||
<div class="flex gap-4 mt-8">
|
||||
<Button class="h-12 px-6 md:h-10 md:px-4">
|
||||
Primary Action
|
||||
</Button>
|
||||
<Button variant="outline" class="h-12 px-6 md:h-10 md:px-4">
|
||||
Secondary
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
// Example 5: Responsive Form
|
||||
<form class="mt-8 space-y-4 md:space-y-6">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="text-sm font-medium">First Name</label>
|
||||
<Input class="mt-1 w-full" placeholder="John" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-sm font-medium">Last Name</label>
|
||||
<Input class="mt-1 w-full" placeholder="Doe" />
|
||||
</div>
|
||||
</div>
|
||||
<Button type="submit" class="w-full md:w-auto">
|
||||
Submit
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
// Example 6: Responsive Dialog
|
||||
// Full width on mobile, constrained on desktop
|
||||
<div class="mt-8">
|
||||
<Button on_click=move |_| {
|
||||
// Open dialog logic here
|
||||
}>
|
||||
Open Dialog
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user