Skip to content

Latest commit

 

History

History
803 lines (659 loc) · 17.1 KB

File metadata and controls

803 lines (659 loc) · 17.1 KB

Mobile Responsive Design - Detailed Documentation

Overview

Pulsefeed is fully responsive with comprehensive mobile support featuring a hamburger menu navigation system. The design adapts progressively from desktop (fixed sidebar) to mobile (bottom navigation bar with slide-up menu).

Implementation Files

  • static/styles.css - All responsive CSS with media queries
  • templates/base.html - Mobile navigation HTML + JavaScript

Navigation Patterns

Desktop (>992px)

  • Fixed left sidebar - 80px wide
  • Icon-only navigation with tooltips
  • Always visible, no hamburger menu
  • Settings gear icon at bottom

Mobile (≤992px)

  • Bottom navigation bar - Fixed at bottom of screen
  • Centered hamburger button (☰ icon)
  • Slide-up drawer menu on button tap
  • Full navigation with text labels

Breakpoints

Desktop/Large Tablet (>1024px)

  • Standard desktop layout
  • Full sidebar navigation
  • Normal spacing and font sizes
  • Mini calendar always visible

Tablet Landscape (≤1024px)

@media (max-width: 1024px) {
  /* Reduce spacing */
  .container { padding: 1rem; }

  /* Smaller mini calendar */
  #miniCalendar { max-width: 280px; }

  /* Compact cards */
  .card { margin-bottom: 0.75rem; }
}

Tablet/Phone (≤992px)

@media (max-width: 992px) {
  /* Hide desktop sidebar */
  .sidebar { display: none; }

  /* Show mobile bottom bar */
  .mobile-nav-bottom { display: flex; }

  /* Add padding for bottom bar */
  main { padding-bottom: 80px; }

  /* Mini calendar toggle button */
  #miniCalendarToggle { display: block; }

  /* Calendar hidden by default */
  #miniCalendar { display: none; }
}

Mobile Phone (≤480px)

@media (max-width: 480px) {
  /* Full-screen modals */
  .modal-dialog {
    margin: 0;
    max-width: 100%;
    height: 100vh;
  }

  /* Vertical radio buttons */
  .category-radio-btn { display: block; width: 100%; }

  /* Compact typography */
  body { font-size: 0.9rem; }
  h1 { font-size: 1.5rem; }
  h2 { font-size: 1.25rem; }

  /* Reduced card padding */
  .card-body { padding: 0.625rem; }

  /* Bottom bar height adjustment */
  main { padding-bottom: 70px; }
  .mobile-nav-bottom { height: 55px; }
}

Extra Small Phone (≤360px)

@media (max-width: 360px) {
  /* Minimize everything */
  body { font-size: 0.85rem; }
  .card-body { padding: 0.5rem; }
  .btn { padding: 0.25rem 0.5rem; font-size: 0.8rem; }

  /* Even more compact bottom bar */
  main { padding-bottom: 65px; }
  .mobile-nav-bottom { height: 50px; }
}

Landscape Mode (max-height 600px)

@media (max-height: 600px) {
  /* Reduce vertical spacing */
  .card { margin-bottom: 0.5rem; }
  .card-body { padding: 0.5rem; }

  /* Compact modals */
  .modal-body { padding: 0.75rem; }
}

Mobile Navigation System

Bottom Navigation Bar

HTML Structure:

<div class="mobile-nav-bottom">
  <button id="mobileMenuToggle" class="mobile-menu-toggle">
    <i class="bi bi-list"></i>
  </button>
</div>

CSS:

.mobile-nav-bottom {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  height: 60px;
  background: var(--sidebar-bg);
  border-top: 1px solid var(--border-color);
  display: none; /* Hidden on desktop */
  align-items: center;
  justify-content: center;
  z-index: 1000;
}

.mobile-menu-toggle {
  background: transparent;
  border: none;
  color: var(--text-primary);
  font-size: 1.5rem;
  padding: 0.5rem 1rem;
  cursor: pointer;
  transition: color 0.2s;
}

.mobile-menu-toggle:hover {
  color: var(--primary-color);
}

Height by Breakpoint:

  • Tablet (992px): 60px
  • Phone (480px): 55px
  • Tiny (360px): 50px

Slide-Up Menu

HTML Structure:

<div id="mobileMenuDrawer" class="mobile-menu-drawer">
  <div class="mobile-menu-header">
    <h5>Menu</h5>
    <button class="mobile-menu-close">
      <i class="bi bi-x-lg"></i>
    </button>
  </div>
  <nav class="mobile-menu-nav">
    <a href="/" class="mobile-menu-item">
      <i class="bi bi-house-door"></i>
      <span>Home</span>
    </a>
    <a href="/calendar" class="mobile-menu-item">
      <i class="bi bi-calendar3"></i>
      <span>Calendar</span>
    </a>
    <!-- ... more items ... -->
  </nav>
</div>
<div id="mobileMenuBackdrop" class="mobile-menu-backdrop"></div>

CSS:

.mobile-menu-drawer {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  background: var(--sidebar-bg);
  transform: translateY(100%); /* Hidden below screen */
  transition: transform 0.3s ease;
  z-index: 1050;
  max-height: 80vh;
  overflow-y: auto;
  border-top-left-radius: 1rem;
  border-top-right-radius: 1rem;
}

.mobile-menu-drawer.active {
  transform: translateY(0); /* Slide up */
}

.mobile-menu-backdrop {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.5);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s ease, visibility 0.3s ease;
  z-index: 1040;
}

.mobile-menu-backdrop.active {
  opacity: 1;
  visibility: visible;
}

.mobile-menu-item {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1rem 1.5rem;
  color: var(--text-primary);
  text-decoration: none;
  transition: background 0.2s;
}

.mobile-menu-item:hover {
  background: var(--hover-bg);
}

.mobile-menu-item i {
  font-size: 1.25rem;
  width: 1.5rem;
}

JavaScript:

const mobileMenuToggle = document.getElementById('mobileMenuToggle');
const mobileMenuDrawer = document.getElementById('mobileMenuDrawer');
const mobileMenuBackdrop = document.getElementById('mobileMenuBackdrop');
const mobileMenuClose = document.querySelector('.mobile-menu-close');
const mobileMenuItems = document.querySelectorAll('.mobile-menu-item');

// Open menu
mobileMenuToggle.addEventListener('click', () => {
  mobileMenuDrawer.classList.add('active');
  mobileMenuBackdrop.classList.add('active');
});

// Close menu - backdrop click
mobileMenuBackdrop.addEventListener('click', () => {
  mobileMenuDrawer.classList.remove('active');
  mobileMenuBackdrop.classList.remove('active');
});

// Close menu - close button
mobileMenuClose.addEventListener('click', () => {
  mobileMenuDrawer.classList.remove('active');
  mobileMenuBackdrop.classList.remove('active');
});

// Close menu - any menu item click
mobileMenuItems.forEach(item => {
  item.addEventListener('click', () => {
    mobileMenuDrawer.classList.remove('active');
    mobileMenuBackdrop.classList.remove('active');
  });
});

// Close menu - Escape key
document.addEventListener('keydown', (e) => {
  if (e.key === 'Escape' && mobileMenuDrawer.classList.contains('active')) {
    mobileMenuDrawer.classList.remove('active');
    mobileMenuBackdrop.classList.remove('active');
  }
});

Menu Items:

  • Home (house icon)
  • Calendar (calendar icon)
  • All Entries (list icon)
  • Templates (file icon)
  • Series Library (collection icon)
  • Calendar Imports (cloud icon)
  • Settings (gear icon)

Close Triggers:

  • Tap backdrop (semi-transparent overlay)
  • Tap X button in header
  • Press Escape key
  • Click any menu item (navigates and closes)

Animations:

  • Slide-up: 0.3s ease transform
  • Backdrop fade-in: 0.3s ease opacity
  • Smooth 60fps animations using CSS transforms

Key Mobile Adaptations

Date Display

Desktop:

[Time] │ [Date] - [Label]
12:45 PM │ Wednesday, January 15, 2025 - 3 days from now

Mobile (≤768px):

[Time] [→] [Date] - [Label]
12:45 PM → Wednesday, January 15, 2025
3 days from now

Implementation:

@media (max-width: 768px) {
  .date-divider { display: none; }
  .date-navigation-info { display: none; } /* Second section hidden */

  .date-arrow-indicator {
    display: inline; /* Show arrow */
    margin: 0 0.25rem;
  }
}

Arrow indicators:

  • for future dates
  • for past dates
  • No arrow for today

Single-row compact layout:

  • No vertical divider
  • No second section
  • Arrow appears inline with date
  • Label below on separate line

Mini Calendar Toggle

Desktop: Always visible, no toggle button

Mobile: Hidden by default, toggle button reveals it

HTML:

<button id="miniCalendarToggle" class="btn btn-outline-secondary btn-sm">
  <i class="bi bi-calendar3"></i>
  <span id="miniCalendarToggleText">Show Calendar</span>
</button>

<div id="miniCalendar" style="display: none;">
  <!-- Calendar widget -->
</div>

JavaScript:

const miniCalendarToggle = document.getElementById('miniCalendarToggle');
const miniCalendar = document.getElementById('miniCalendar');
const toggleText = document.getElementById('miniCalendarToggleText');

miniCalendarToggle.addEventListener('click', () => {
  const isHidden = miniCalendar.style.display === 'none';

  if (isHidden) {
    miniCalendar.style.display = 'block';
    toggleText.textContent = 'Hide Calendar';
    miniCalendarToggle.querySelector('i').className = 'bi bi-x-lg';
  } else {
    miniCalendar.style.display = 'none';
    toggleText.textContent = 'Show Calendar';
    miniCalendarToggle.querySelector('i').className = 'bi bi-calendar3';
  }
});

Why hidden by default:

  • Saves vertical space on mobile
  • Date navigation still works without calendar
  • Optional enhancement when needed
  • Smooth slide-down animation on reveal

Content Padding

Account for bottom navigation bar:

@media (max-width: 992px) {
  main {
    padding-bottom: 80px; /* Tablet */
  }
}

@media (max-width: 480px) {
  main {
    padding-bottom: 70px; /* Phone */
  }
}

@media (max-width: 360px) {
  main {
    padding-bottom: 65px; /* Tiny */
  }
}

Floating Add Button - Repositioned above bottom bar:

.floating-add-btn {
  position: fixed;
  bottom: 20px;
  right: 20px;
  z-index: 999;
}

@media (max-width: 992px) {
  .floating-add-btn {
    bottom: 80px; /* Above bottom bar */
  }
}

@media (max-width: 480px) {
  .floating-add-btn {
    bottom: 70px;
  }
}

@media (max-width: 360px) {
  .floating-add-btn {
    bottom: 65px;
  }
}

Date Navigation

Desktop:

  • Year/month/week/day navigation visible
  • All buttons accessible

Mobile (≤768px):

.nav-year, .nav-month, .nav-week {
  display: none; /* Hide for simplicity */
}

.nav-day {
  display: flex; /* Prominent */
}

Day navigation buttons:

  • Previous Day:
  • Today: Today
  • Next Day:

Radio Button Groups

Desktop: Inline horizontal layout

Mobile (≤480px): Vertical stacking

@media (max-width: 480px) {
  .category-radio-btn,
  .checklist-radio-btn {
    display: block;
    width: 100%;
    margin-bottom: 0.5rem;
    min-height: 44px; /* iOS touch target guideline */
  }

  input[type="radio"] + label {
    padding: 0.75rem 1rem; /* Larger touch target */
  }
}

Touch Target Guidelines:

  • Minimum 44x44px (iOS Human Interface Guidelines)
  • 48x48dp recommended (Material Design)
  • Adequate spacing between tappable elements

Modals

Desktop: Centered with margins

Mobile (≤480px): Full-screen

@media (max-width: 480px) {
  .modal-dialog {
    margin: 0;
    max-width: 100%;
    height: 100vh;
    border-radius: 0; /* Remove rounded corners */
  }

  .modal-content {
    height: 100%;
    border: none;
  }

  .modal-body {
    overflow-y: auto;
    -webkit-overflow-scrolling: touch; /* Smooth iOS scrolling */
  }
}

Benefits:

  • More usable space on small screens
  • Easier form interaction
  • Native app-like feel
  • Better keyboard handling

Typography Scaling

/* Desktop */
body { font-size: 1rem; }
h1 { font-size: 2rem; }
h2 { font-size: 1.5rem; }

/* Phone (≤480px) */
@media (max-width: 480px) {
  body { font-size: 0.9rem; }
  h1 { font-size: 1.5rem; }
  h2 { font-size: 1.25rem; }
  h3 { font-size: 1.1rem; }
}

/* Tiny (≤360px) */
@media (max-width: 360px) {
  body { font-size: 0.85rem; }
  h1 { font-size: 1.25rem; }
  h2 { font-size: 1.1rem; }
  h3 { font-size: 1rem; }
}

Card Padding

/* Desktop */
.card-body { padding: 1rem; }

/* Tablet (≤768px) */
@media (max-width: 768px) {
  .card-body { padding: 0.875rem; }
}

/* Phone (≤480px) */
@media (max-width: 480px) {
  .card-body { padding: 0.625rem; }
}

/* Tiny (≤360px) */
@media (max-width: 360px) {
  .card-body { padding: 0.5rem; }
}

Print Styles

@media print {
  /* Hide navigation */
  .sidebar,
  .mobile-nav-bottom,
  .mobile-menu-drawer,
  .mobile-menu-backdrop,
  .floating-add-btn {
    display: none !important;
  }

  /* Full-width content */
  main {
    margin-left: 0 !important;
    padding: 0 !important;
  }

  /* Clean cards */
  .card {
    border: 1px solid #ccc;
    page-break-inside: avoid;
  }

  /* Remove interactive elements */
  .btn, button {
    display: none !important;
  }
}

Performance Notes

CSS-Only Responsive Behavior

  • Media queries handle all layout changes
  • No JavaScript for breakpoint detection
  • Progressive enhancement approach
  • Maintains 60fps animations

Minimal JavaScript

  • Only for menu interactions (open/close/backdrop/escape)
  • Event listeners attached once on page load
  • No DOM manipulation for responsive behavior
  • Smooth CSS transforms for animations

Theme System Compatibility

  • All media queries work with both dark and light themes
  • CSS custom properties respect theme variables
  • No hardcoded colors in responsive CSS

User Preferences

  • Respects system font size settings
  • Supports dynamic type scaling
  • No fixed pixel heights where possible
  • Flexible layouts with min/max constraints

Testing Checklist

Browser Dev Tools

  1. Open DevTools: F12
  2. Enable Device Toolbar: Ctrl+Shift+M (Chrome/Brave)
  3. Test responsive views

Common Device Sizes

iPhone SE (375px width):

  • Bottom nav bar visible and usable
  • Hamburger menu opens/closes smoothly
  • Text readable without zooming
  • Touch targets adequate (44x44px minimum)
  • Floating button accessible above bottom bar

iPhone 12 (390px width):

  • Same as iPhone SE
  • Slightly more breathing room

Pixel 5 (393px width):

  • Same as iPhone 12
  • Android-specific testing

iPad (768px width):

  • Mobile view (bottom nav bar)
  • Verify bottom nav shows correctly
  • Landscape orientation works

Desktop (1920px width):

  • Fixed sidebar visible
  • No mobile elements showing
  • Mini calendar always visible

Functional Testing

Navigation:

  • Hamburger button opens slide-up menu
  • Backdrop click closes menu
  • X button closes menu
  • Escape key closes menu
  • Menu item click navigates and closes menu
  • Smooth animations (no jank)

Date Display:

  • Single-row layout on mobile
  • No divider visible
  • Arrow indicators show for non-today dates
  • Label displays correctly

Mini Calendar:

  • Hidden by default on mobile
  • Toggle button shows/hides calendar
  • Button text updates ("Show"/"Hide")
  • Icon changes (calendar → X)
  • Smooth transition animation

Content:

  • Bottom nav doesn't overlap content
  • Floating button above bottom bar
  • Scrolling works smoothly
  • Cards render correctly
  • Forms usable on small screens

Forms:

  • Radio buttons stack vertically on phone
  • Touch targets adequate size
  • Modals full-screen on phone
  • Keyboard doesn't obscure inputs

Orientation:

  • Portrait mode works
  • Landscape mode adjusts spacing
  • No horizontal scrolling

Themes:

  • Dark theme responsive styles correct
  • Light theme responsive styles correct
  • Theme toggle works on mobile

Performance:

  • Animations smooth (60fps)
  • No layout shift on resize
  • Fast page loads on mobile network

Cross-Browser Testing

  • Chrome/Brave mobile
  • Safari mobile (iOS)
  • Firefox mobile
  • Edge mobile
  • Samsung Internet

Accessibility

  • Touch targets meet 44x44px guideline (iOS)
  • Text scales with system font size
  • Contrast ratios maintained (WCAG AA)
  • Screen reader navigation works
  • Keyboard navigation functional

Common Issues & Solutions

Issue: Bottom nav overlaps content

Solution: Add padding-bottom to main:

@media (max-width: 992px) {
  main { padding-bottom: 80px; }
}

Issue: Floating button hidden by bottom nav

Solution: Adjust bottom position:

@media (max-width: 992px) {
  .floating-add-btn { bottom: 80px; }
}

Issue: Menu doesn't close on item click

Solution: Ensure event listeners attached:

mobileMenuItems.forEach(item => {
  item.addEventListener('click', closeMenu);
});

Issue: Backdrop visible on desktop

Solution: Hide with media query:

@media (min-width: 993px) {
  .mobile-menu-backdrop { display: none !important; }
}

Issue: Text too small on mobile

Solution: Increase base font size:

@media (max-width: 480px) {
  body { font-size: 0.9rem; } /* Was 0.85rem */
}

Issue: Touch targets too small

Solution: Add min-height and padding:

.mobile-menu-item {
  min-height: 44px;
  padding: 0.75rem 1rem;
}

Future Enhancements

  • Swipe gestures (swipe up to open menu, down to close)
  • Pull-to-refresh on home page
  • Bottom sheet component library
  • Native app wrapper (Capacitor/Cordova)
  • Offline mode with service workers
  • Progressive Web App (PWA) support
  • Dark mode auto-detection from system
  • Haptic feedback on interactions (mobile)