📱 Responsive Breakpoints
// Tailwind CSS 4 — Responsive Breakpoints
sm: 640px // Small tablet (portrait)
md: 768px // Tablet (landscape) / large phone
lg: 1024px // Desktop / laptop
xl: 1280px // Large desktop
2xl: 1536px // Extra large (wide monitors)
// Mobile-first: default styles = mobile, overrides go up
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
{/* Stacks on mobile, 2-col tablet, 3-col desktop */}
</div>
// Layout components
// apps/web/src/components/layout/MobileFirstLayout.tsx → mobile
// apps/web/src/components/layout/AppLayout.tsx → desktop
📲 Mobile Optimizations
👆 Touch Targets
Minimum 44x44px touch targets voor betere touch accuracy
// Minimum touch target
className="p-4" // Good
className="p-2" // Too small
↔️ Adequate Spacing
Voldoende ruimte tussen interactive elements
// Adequate spacing
className="gap-3" // Good
className="gap-1" // Too tight
👉 Touch Gestures
Swipe, pinch, long-press support
- • Swipe left/right: Navigate between views
- • Pull to refresh: Update content
- • Long press: Context menu
- • Pinch zoom: Images & maps
⚡ Active States
Visual feedback on touch
className="active:scale-95 active:bg-purple-700"
📐 Sidebar: No Horizontal Scroll
Critical: Prevent Horizontal Overflow
Sidebars moeten NOOIT een horizontale scrollbar hebben. Dit zorgt voor slechte UX.
// Sidebar CSS - Prevent horizontal scroll
.sidebar {
width: 320px;
max-width: 100%; /* Never exceed viewport */
overflow-x: hidden; /* Hide horizontal scroll */
overflow-y: auto; /* Allow vertical scroll */
}
// Content truncation
.sidebar-item {
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
// OR use word-wrap
.sidebar-text {
width: 100%;
word-wrap: break-word;
overflow-wrap: break-word;
}
<div className="w-full max-w-full overflow-x-hidden overflow-y-auto">
<div className="truncate">Long text here...</div>
<div className="break-words">LongURLorTextHere</div>
</div>
🧭 Mobile Navigation Patterns
Bottom Navigation Bar
Role-based menu items — verschillende tabs per rol (Viewer / Member / Creator / Provider).
Slide-out Drawer
🏗️ Layout Components
📱 MobileFirstLayout.tsx
apps/web/src/components/layout/MobileFirstLayout.tsx
- • Fixed bottom navigation bar (role-based)
- • Swipe-drawer sidebar (geen overlay scroll)
- • Pull-to-refresh op feed-pagina's
- • Safe-area insets (iPhone notch / Android)
- • Min 44px touch targets op alle interacties
🖥️ AppLayout.tsx
apps/web/src/components/layout/AppLayout.tsx
- • Persistent sidebar (links, collapsible)
- • 3-kolom grid: sidebar / feed / context panel
- • Sticky header met zoekbalk
- • Right panel voor notificaties / chat context
- • Keyboard shortcuts voor desktop power-users
// Router wiring (react-router-dom v7)
// MobileFirstLayout wraps mobile routes
// AppLayout wraps desktop routes
// Device detection via useMediaQuery hook
function RootLayout() {
const isMobile = useMediaQuery('(max-width: 768px)');
return isMobile
? <MobileFirstLayout><Outlet /></MobileFirstLayout>
: <AppLayout><Outlet /></AppLayout>;
}
📲 PWA Production Setup
• Service Worker auto-registratie
• StaleWhileRevalidate (assets)
• NetworkFirst (API calls)
• display: "standalone"
• orientation: "portrait"
• theme_color + icons (192/512px)
• iOS: Safari "Share → Add to Home"
• SW update prompt in app
• Offline fallback pagina
// vite.config.ts — VitePWA configuratie
VitePWA({
registerType: 'autoUpdate',
workbox: {
runtimeCaching: [
{ urlPattern: /\/api\//, handler: 'NetworkFirst' },
{ urlPattern: /\.(js|css|png|woff2)$/, handler: 'StaleWhileRevalidate' }
]
}
})
⚡ Mobile Performance
• Lazy loading (IntersectionObserver)
• Responsive srcset sizes
• Dynamic imports + Suspense
• Tree shaking + manualChunks
• Suspense boundaries per route
• Server Components (waar van toepassing)
• Windowing lange lijsten
• TanStack Query v5 infinite scroll