Skip to main content

12. Responsive Design

Overview

Dashboard 2.0 implements a mobile-first responsive design strategy with three breakpoints: desktop (1024px+), tablet (768px-1023px), and mobile (320px-767px). This ensures usability across all devices while maintaining productivity for professional use.

Design Philosophy:

  • Desktop-first for productivity: Rich features, multiple panels, detailed views
  • Tablet for collaboration: Streamlined layouts, touch-friendly interactions
  • Mobile for monitoring: Essential information, quick actions, lightweight interface

12.1 Breakpoint Strategy

Three Breakpoints

BreakpointScreen WidthTarget DevicesLayout Strategy
Desktop≥1024pxDesktop, laptopFull three-panel layout with sidebar
Tablet768px-1023pxiPad, Android tabletsCollapsible sidebar, simplified views
Mobile320px-767pxPhonesHamburger menu, stacked layout, touch-first

CSS Media Queries

/* Base styles - Mobile first */
.container {
padding: 16px;
}

/* Tablet (768px+) */
@media (min-width: 768px) {
.container {
padding: 24px;
}
}

/* Desktop (1024px+) */
@media (min-width: 1024px) {
.container {
padding: 32px;
}
}

12.2 Desktop Layout (≥1024px)

Full Three-Panel Layout

Layout Structure:

  • Left Sidebar: 280px fixed width (always visible)
  • Top Bar: 60px fixed height
  • Main Content: calc(100% - 280px) fluid width

CSS Grid:

@media (min-width: 1024px) {
.dashboard-layout {
display: grid;
grid-template-areas:
"sidebar topbar"
"sidebar main";
grid-template-columns: 280px 1fr;
grid-template-rows: 60px 1fr;
height: 100vh;
}

.sidebar {
grid-area: sidebar;
background: #F9FAFB;
border-right: 1px solid #E5E7EB;
}

.topbar {
grid-area: topbar;
background: #FFFFFF;
border-bottom: 1px solid #E5E7EB;
}

.main-content {
grid-area: main;
overflow-y: auto;
}
}

Kanban Board

Desktop-optimized:

  • Swimlanes: Full width, all projects visible simultaneously
  • Drag-drop: Mouse-based with hover states
  • Columns: 3 columns (To Do, In Progress, Done) with equal flex
  • Cards: 250px min-width with full details
@media (min-width: 1024px) {
.kanban-board {
display: grid;
grid-template-columns: 250px 1fr 1fr 1fr;
gap: 16px;
}

.task-card {
min-width: 250px;
padding: 16px;
}

.task-card-details {
display: block; /* Show all details */
}
}

12.3 Tablet Layout (768px-1023px)

Collapsible Sidebar

Behavior:

  • Default: Sidebar hidden (collapsed)
  • Toggle: Hamburger menu button to open/close
  • Overlay: Sidebar slides in from left, overlays content
  • Tap outside: Close sidebar

CSS:

@media (min-width: 768px) and (max-width: 1023px) {
.dashboard-layout {
display: grid;
grid-template-areas:
"topbar"
"main";
grid-template-columns: 1fr;
grid-template-rows: 60px 1fr;
}

.sidebar {
position: fixed;
top: 0;
left: -280px; /* Hidden by default */
width: 280px;
height: 100vh;
background: #F9FAFB;
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
transition: left 0.3s ease;
z-index: 1000;
}

.sidebar.open {
left: 0; /* Slide in */
}

/* Overlay backdrop */
.sidebar-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 999;
}

.sidebar.open ~ .sidebar-overlay {
display: block;
}

.main-content {
margin-left: 0;
}
}

JavaScript Toggle:

const sidebarToggle = document.getElementById('sidebar-toggle');
const sidebar = document.getElementById('sidebar');
const overlay = document.getElementById('sidebar-overlay');

sidebarToggle.addEventListener('click', () => {
sidebar.classList.toggle('open');
});

overlay.addEventListener('click', () => {
sidebar.classList.remove('open');
});

Simplified Kanban

Tablet-optimized:

  • Swimlanes: Collapsible by default, expand one at a time
  • Columns: Side-scroll with touch gestures
  • Cards: 220px min-width, slightly smaller details
@media (min-width: 768px) and (max-width: 1023px) {
.kanban-board {
overflow-x: auto;
-webkit-overflow-scrolling: touch; /* Smooth iOS scrolling */
}

.swimlane-columns {
display: flex;
gap: 12px;
min-width: min-content;
}

.kanban-column {
min-width: 220px;
flex: 0 0 220px;
}

.task-card {
padding: 12px;
}

.task-card-meta {
font-size: 11px; /* Smaller text */
}
}

12.4 Mobile Layout (320px-767px)

Hamburger Menu Navigation

Full mobile-first navigation:

  • Hamburger icon: Top-left corner of top bar
  • Full-screen menu: Slides in from left, covers entire screen
  • Close button: X icon in top-right of menu
  • Touch gestures: Swipe left to close menu

HTML:

<div class="topbar-mobile">
<button class="hamburger-menu" aria-label="Open navigation menu">
<span class="hamburger-icon"></span>
</button>
<h1 class="dashboard-title">Dashboard 2.0</h1>
<button class="search-toggle" aria-label="Open search">🔍</button>
</div>

<div class="mobile-menu" id="mobile-menu">
<div class="mobile-menu-header">
<h2>Navigation</h2>
<button class="close-menu" aria-label="Close menu"></button>
</div>
<nav class="mobile-nav">
<!-- Navigation items -->
</nav>
</div>

CSS:

@media (max-width: 767px) {
.dashboard-layout {
display: flex;
flex-direction: column;
}

.topbar-mobile {
display: flex;
align-items: center;
justify-content: space-between;
height: 56px;
padding: 0 16px;
background: #FFFFFF;
border-bottom: 1px solid #E5E7EB;
}

.hamburger-menu {
width: 40px;
height: 40px;
border: none;
background: transparent;
cursor: pointer;
}

.hamburger-icon {
display: block;
width: 24px;
height: 2px;
background: #111827;
position: relative;
}

.hamburger-icon::before,
.hamburger-icon::after {
content: '';
display: block;
width: 24px;
height: 2px;
background: #111827;
position: absolute;
}

.hamburger-icon::before {
top: -8px;
}

.hamburger-icon::after {
top: 8px;
}

/* Mobile Menu */
.mobile-menu {
position: fixed;
top: 0;
left: -100%;
width: 100%;
height: 100vh;
background: #FFFFFF;
transition: left 0.3s ease;
z-index: 2000;
overflow-y: auto;
}

.mobile-menu.open {
left: 0;
}

.mobile-menu-header {
display: flex;
align-items: center;
justify-content: space-between;
height: 56px;
padding: 0 16px;
border-bottom: 1px solid #E5E7EB;
}

.close-menu {
width: 40px;
height: 40px;
border: none;
background: transparent;
font-size: 24px;
cursor: pointer;
}
}

Stacked Content Layout

Mobile-optimized views:

  • Portfolio Overview: 1-column grid, cards stack vertically
  • Kanban: Horizontal scroll for columns, swipe between projects
  • Timeline: Vertical orientation, compressed time scale
  • Task Detail: Full-screen modal, bottom sheet slide-up

Portfolio Cards (Mobile):

@media (max-width: 767px) {
.portfolio-grid {
display: flex;
flex-direction: column;
gap: 16px;
}

.kpi-card {
width: 100%;
padding: 16px;
}

.kpi-value {
font-size: 32px; /* Larger for touch targets */
}

.kpi-label {
font-size: 14px;
}
}

Mobile Kanban

Horizontal scroll with snap points:

@media (max-width: 767px) {
.kanban-board {
overflow-x: auto;
scroll-snap-type: x mandatory;
-webkit-overflow-scrolling: touch;
}

.swimlane {
scroll-snap-align: start;
min-width: 100%;
}

.swimlane-columns {
display: flex;
gap: 12px;
padding: 16px;
}

.kanban-column {
min-width: 280px;
flex: 0 0 280px;
}

.task-card {
padding: 12px;
margin-bottom: 8px;
}

/* Simplified card - hide less important details */
.task-card-assignee,
.task-card-tags {
display: none;
}
}

Mobile Task Detail Modal

Bottom sheet pattern:

@media (max-width: 767px) {
.task-detail-modal {
position: fixed;
bottom: -100%; /* Hidden by default */
left: 0;
width: 100%;
height: 90vh;
background: #FFFFFF;
border-top-left-radius: 16px;
border-top-right-radius: 16px;
box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.1);
transition: bottom 0.3s ease;
z-index: 3000;
}

.task-detail-modal.open {
bottom: 0; /* Slide up */
}

/* Drag handle */
.modal-drag-handle {
width: 40px;
height: 4px;
background: #D1D5DB;
border-radius: 2px;
margin: 12px auto;
}
}

Touch gesture for closing:

let startY = 0;
let currentY = 0;

modal.addEventListener('touchstart', (e) => {
startY = e.touches[0].clientY;
});

modal.addEventListener('touchmove', (e) => {
currentY = e.touches[0].clientY;
const deltaY = currentY - startY;

// Only allow downward swipe
if (deltaY > 0) {
modal.style.transform = `translateY(${deltaY}px)`;
}
});

modal.addEventListener('touchend', () => {
const deltaY = currentY - startY;

// If swiped down >100px, close modal
if (deltaY > 100) {
modal.classList.remove('open');
}

// Reset transform
modal.style.transform = '';
});

12.5 Touch Interactions

Touch-Friendly Targets

Minimum touch target size: 44px × 44px (Apple HIG, Material Design)

/* All interactive elements on touch devices */
@media (hover: none) and (pointer: coarse) {
button,
a,
.clickable {
min-width: 44px;
min-height: 44px;
padding: 12px;
}

.task-card {
padding: 16px; /* More generous padding */
}

.filter-chip {
padding: 10px 16px; /* Larger for touch */
}
}

Swipe Gestures

Kanban column navigation:

let startX = 0;
let currentX = 0;
const kanbanBoard = document.getElementById('kanban-board');

kanbanBoard.addEventListener('touchstart', (e) => {
startX = e.touches[0].clientX;
});

kanbanBoard.addEventListener('touchmove', (e) => {
currentX = e.touches[0].clientX;
});

kanbanBoard.addEventListener('touchend', () => {
const deltaX = startX - currentX;

// Swipe left (next column)
if (deltaX > 50) {
scrollToNextColumn();
}

// Swipe right (previous column)
if (deltaX < -50) {
scrollToPreviousColumn();
}
});

Pull-to-Refresh

Native-like refresh behavior:

let pullDistance = 0;
const threshold = 80;

mainContent.addEventListener('touchstart', (e) => {
if (mainContent.scrollTop === 0) {
startY = e.touches[0].clientY;
}
});

mainContent.addEventListener('touchmove', (e) => {
if (mainContent.scrollTop === 0) {
currentY = e.touches[0].clientY;
pullDistance = currentY - startY;

if (pullDistance > 0 && pullDistance < threshold) {
// Show refresh indicator
showRefreshIndicator(pullDistance / threshold);
}
}
});

mainContent.addEventListener('touchend', () => {
if (pullDistance >= threshold) {
// Trigger refresh
refreshDashboard();
}

// Hide indicator
hideRefreshIndicator();
pullDistance = 0;
});

12.6 Performance Optimization

Lazy Loading

Load content on scroll:

const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src; // Load image
observer.unobserve(img);
}
});
});

// Observe all lazy images
document.querySelectorAll('img[data-src]').forEach(img => {
observer.observe(img);
});

Reduce JavaScript on Mobile

Conditional feature loading:

if (window.innerWidth >= 1024) {
// Load desktop-only features
loadAdvancedFiltering();
loadDragDropLibrary();
} else {
// Mobile-optimized features only
loadTouchGestures();
}

CSS Containment

Improve rendering performance:

.task-card {
contain: layout style paint;
}

.kanban-column {
contain: layout style;
}

12.7 Orientation Changes

Portrait vs. Landscape

Tablet landscape: Show more columns in Kanban Phone landscape: Compress navigation, maximize content

@media (max-width: 767px) and (orientation: landscape) {
.topbar-mobile {
height: 48px; /* Shorter to maximize vertical space */
}

.kanban-column {
min-width: 240px; /* Narrower columns to fit more */
}
}

JavaScript orientation handler:

window.addEventListener('orientationchange', () => {
setTimeout(() => {
// Recalculate layout after orientation change
recalculateLayout();
}, 100);
});

12.8 Implementation Prompt - Responsive Design

Create a responsive dashboard with mobile-first approach.

HTML Structure

<!-- Mobile-first layout -->
<div class="dashboard-layout">
<!-- Mobile Top Bar -->
<div class="topbar-mobile">
<button class="hamburger-menu"></button>
<h1 class="dashboard-title">Dashboard</h1>
<button class="search-toggle">🔍</button>
</div>

<!-- Mobile Menu (hidden by default) -->
<div class="mobile-menu" id="mobile-menu">
<!-- Navigation -->
</div>

<!-- Sidebar (tablet/desktop) -->
<aside class="sidebar" id="sidebar">
<!-- Sidebar content -->
</aside>

<!-- Main Content -->
<main class="main-content">
<!-- Dashboard content -->
</main>
</div>

CSS Media Queries

  1. Base (Mobile): 320px+ with stacked layout
  2. Tablet: 768px+ with collapsible sidebar
  3. Desktop: 1024px+ with persistent sidebar

JavaScript Requirements

  • Sidebar toggle (tablet)
  • Hamburger menu (mobile)
  • Touch gestures (swipe, pull-to-refresh)
  • Orientation change handler
  • Lazy loading for performance

Testing Checklist

Devices to test:

  • iPhone SE (320px width)
  • iPhone 12/13 (390px)
  • iPad (768px)
  • iPad Pro (1024px)
  • Desktop (1920px+)

Orientations:

  • Portrait
  • Landscape

Touch interactions:

  • Tap targets ≥44px
  • Swipe gestures work
  • Pull-to-refresh functional

Next: 13. Accessibility (WCAG 2.1 AA) Previous: 11. Global Search Index: Master Index