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).
static/styles.css- All responsive CSS with media queriestemplates/base.html- Mobile navigation HTML + JavaScript
- Fixed left sidebar - 80px wide
- Icon-only navigation with tooltips
- Always visible, no hamburger menu
- Settings gear icon at bottom
- Bottom navigation bar - Fixed at bottom of screen
- Centered hamburger button (☰ icon)
- Slide-up drawer menu on button tap
- Full navigation with text labels
- Standard desktop layout
- Full sidebar navigation
- Normal spacing and font sizes
- Mini calendar always visible
@media (max-width: 1024px) {
/* Reduce spacing */
.container { padding: 1rem; }
/* Smaller mini calendar */
#miniCalendar { max-width: 280px; }
/* Compact cards */
.card { margin-bottom: 0.75rem; }
}@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; }
}@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; }
}@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; }
}@media (max-height: 600px) {
/* Reduce vertical spacing */
.card { margin-bottom: 0.5rem; }
.card-body { padding: 0.5rem; }
/* Compact modals */
.modal-body { padding: 0.75rem; }
}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
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
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
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
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;
}
}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:
→
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
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
/* 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; }
}/* 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; }
}@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;
}
}- Media queries handle all layout changes
- No JavaScript for breakpoint detection
- Progressive enhancement approach
- Maintains 60fps animations
- 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
- All media queries work with both dark and light themes
- CSS custom properties respect theme variables
- No hardcoded colors in responsive CSS
- Respects system font size settings
- Supports dynamic type scaling
- No fixed pixel heights where possible
- Flexible layouts with min/max constraints
- Open DevTools:
F12 - Enable Device Toolbar:
Ctrl+Shift+M(Chrome/Brave) - Test responsive views
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
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
- Chrome/Brave mobile
- Safari mobile (iOS)
- Firefox mobile
- Edge mobile
- Samsung Internet
- Touch targets meet 44x44px guideline (iOS)
- Text scales with system font size
- Contrast ratios maintained (WCAG AA)
- Screen reader navigation works
- Keyboard navigation functional
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; }
}Solution: Ensure event listeners attached:
mobileMenuItems.forEach(item => {
item.addEventListener('click', closeMenu);
});Solution: Hide with media query:
@media (min-width: 993px) {
.mobile-menu-backdrop { display: none !important; }
}Solution: Increase base font size:
@media (max-width: 480px) {
body { font-size: 0.9rem; } /* Was 0.85rem */
}Solution: Add min-height and padding:
.mobile-menu-item {
min-height: 44px;
padding: 0.75rem 1rem;
}- 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)