Responsive Strategy
Purpose
This document defines how GreenGrass adapts to different screen sizes, devices, and platform capabilities. It covers breakpoints, device-specific behavior, the Capacitor native wrapper strategy, and how components transform across viewports.
Breakpoints
Definition
| Token | Width | Name | Target |
|---|---|---|---|
--bp-sm |
640px | Small | Large phones (landscape), small tablets |
--bp-md |
768px | Medium | Tablets (portrait) |
--bp-lg |
1024px | Large | Tablets (landscape), small laptops |
--bp-xl |
1280px | Extra large | Laptops, desktop monitors |
--bp-2xl |
1536px | 2x Extra large | Large desktop monitors |
Viewport Ranges
| Range | Grid | Nav | Shell | Primary Target |
|---|---|---|---|---|
| <640px | 4-col | Bottom tabs | Mobile shell | Phones |
| 640-767px | 4-col | Bottom tabs | Mobile shell | Large phones |
| 768-1023px | 8-col | Collapsed sidebar (overlay) | Tablet shell | Tablets |
| 1024-1279px | 12-col | Sidebar (collapsible) | Desktop shell | Small laptops |
| 1280px+ | 12-col | Sidebar (expanded default) | Desktop shell | Desktop |
Usage
Breakpoints are used via CSS media queries with a mobile-first approach:
/* Base styles apply to smallest viewport */
.component { /* mobile styles */ }
@media (min-width: 768px) { /* tablet adjustments */ }
@media (min-width: 1024px) { /* desktop adjustments */ }
Mobile-first means: the default (no media query) styles are for the smallest screen. Enhancements are added at wider breakpoints. This ensures that low-end phones with slow CSS processing get the simplest stylesheet.
Device Categories
Phone (< 768px)
The primary device for Volunteers, Team Leads, Supporters, and Candidates.
Layout:
- Full-screen content, no sidebar
- Bottom tab bar (4-5 role-adaptive tabs)
- Top bar: back/menu, title, contextual actions
- Lists are card-based (not table-based)
- Detail views are full-screen (tap to enter, back to exit)
Behavior:
- Touch interactions only (no hover states)
- Pull-to-refresh on lists
- Infinite scroll instead of pagination
- Swipe gestures on cards (dismiss, archive)
- Bottom sheet for filters and options (not dropdowns)
- Form inputs use native mobile keyboards (numeric, email, phone)
Performance:
- Smallest possible bundle (aggressive code splitting)
- Images lazy-loaded and size-optimized
- Skeleton loading states (no full-screen spinners)
- Service worker for offline shell caching
Tablet (768px - 1023px)
Used by some staff members, occasionally by Field Directors monitoring operations from a staging area.
Layout:
- 8-column grid
- Sidebar collapsed by default, expandable as overlay
- Detail panels can appear as right-side overlay (not inline split)
- Tables switch between card view and table view based on column count
Behavior:
- Touch + limited pointer interactions
- Dropdowns are bottom sheets when triggered by touch
- Split view available for messaging (conversation list + thread)
- Drag-and-drop works but is not the primary interaction model
Desktop (1024px+)
The primary device for Org Admins, Communications Directors, Finance Directors, Data Managers, and Volunteer Coordinators.
Layout:
- 12-column grid
- Sidebar always visible (collapsible to icon-only)
- Split views for list+detail patterns
- Detail panels slide out from the right
- Modals are centered dialogs (not full-screen)
- Tables are full data tables with column sorting and selection
Behavior:
- Mouse + keyboard interactions
- Hover states on interactive elements
- Keyboard shortcuts available
- Drag-and-drop for builders, kanban, calendar
- Right-click context menus where appropriate
- Tooltips on hover (information available without tapping)
Component Adaptation
How components transform across breakpoints:
Navigation
| Component | Phone | Tablet | Desktop |
|---|---|---|---|
| Primary nav | Bottom tab bar | Collapsed sidebar (overlay) | Sidebar (expanded/collapsed) |
| Secondary nav | "More" bottom sheet | "More" bottom sheet | Sidebar sections |
| Breadcrumbs | Hidden (back arrow instead) | Visible (abbreviated) | Visible (full path) |
| Search | Full-screen overlay | Full-screen overlay | Inline header bar |
| User menu | Full-screen profile | Bottom sheet | Dropdown |
Data Display
| Component | Phone | Tablet | Desktop |
|---|---|---|---|
| Data table | Card list | Card list or compact table | Full table with columns |
| List + detail | Stacked (full-screen each) | Optional split view | Split view (resizable) |
| Dashboard widgets | Single column, stacked | Two columns | Three or four columns |
| Charts | Simplified (fewer labels) | Standard | Full (legends, annotations) |
| Calendar | Agenda view (list) | Week view | Month/week/day views |
| Kanban/pipeline | Scrollable single column | Two visible columns | All columns visible |
Forms
| Component | Phone | Tablet | Desktop |
|---|---|---|---|
| Form layout | Single column, stacked | Single column | Two columns possible |
| Select inputs | Native mobile select | Bottom sheet | Dropdown |
| Date picker | Native mobile picker | Calendar popup | Calendar popup |
| File upload | Camera + file picker | Drag-and-drop + file picker | Drag-and-drop + file picker |
| Color picker | Full-screen | Popover | Popover |
| Multi-step forms | Full-screen per step | Full-screen per step | Inline with step indicator |
Overlays
| Component | Phone | Tablet | Desktop |
|---|---|---|---|
| Modals | Full-screen | Centered dialog (medium) | Centered dialog |
| Drawers | Full-screen or bottom sheet | Side drawer (full height) | Side drawer (full height) |
| Notifications | Full-screen list | Side drawer | Side drawer |
| Confirmation dialogs | Bottom sheet | Centered dialog | Centered dialog |
| Tooltips | Bottom sheet (on tap) | Popover (on tap or hover) | Popover (on hover) |
Field Mode
Field mode is phone-only by design. If accessed from a tablet or desktop viewport:
- Tablet: Renders in phone-sized viewport (centered, with padding). Field mode is designed for one-handed mobile use — scaling it to tablet dimensions would make touch targets inappropriately large and waste screen space.
- Desktop: Redirects to the management view (e.g.,
/field/canvassing/[campaignId]instead of/field-mode/canvass/[shiftId]). Staff don't canvass from a desktop.
Capacitor-Specific Considerations
GreenGrass uses Capacitor to wrap the SvelteKit web app as a native mobile app. This introduces specific considerations:
Native API Access
| Feature | Web-Only | With Capacitor |
|---|---|---|
| Passkey auth | WebAuthn API | Native biometric prompt |
| Camera (QR check-in) | getUserMedia API |
Native camera with QR scanner |
| Offline storage | IndexedDB (limited) | SQLite via SQLCipher (encrypted, larger) |
| Push notifications | Web Push API | Native push (APNs/FCM) |
| Map tiles caching | Service worker cache | File system cache (larger, persistent) |
| Background sync | Service worker (limited) | Native background tasks |
| Haptic feedback | Vibration API (limited) | Full haptic engine access |
| Deep links | URL handling | Universal links / App links |
Safe Areas
iOS and Android have safe areas (notch, status bar, home indicator, navigation bar) that must not overlap with interactive content:
/* Respect device safe areas */
.app-shell {
padding-top: env(safe-area-inset-top);
padding-bottom: env(safe-area-inset-bottom);
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
}
The bottom tab bar sits above the home indicator. The header bar sits below the status bar/notch. Field mode's panic button must be outside the safe area but still tappable.
Status Bar
- Standard app: Light status bar (dark text on light background)
- Dark mode: Dark status bar (light text on dark background)
- Field mode: Status bar hidden (full-screen immersive mode) to maximize content space
- Color matches header: Status bar background color matches the app header bar
App Icon and Splash Screen
- App icon: Tenant-customizable. Default GreenGrass icon for platform. Tenant logo (if square-compatible) used for tenant-branded builds.
- Splash screen: Brief (< 2 seconds). Shows tenant logo on brand-colored background. Transitions to the app when initial data loads.
Keyboard Handling
On mobile, the virtual keyboard overlapping form inputs is a common problem:
- Scroll to active input: When a form input gains focus, the viewport scrolls to keep it visible above the keyboard.
- Bottom-fixed elements reposition: The bottom tab bar and floating action buttons move above the keyboard when it's open.
- Field mode: Input fields are in the upper portion of the screen, so the keyboard doesn't overlap them. Response option buttons are large enough to remain tappable above the keyboard.
Performance Budgets
Performance is a core constraint — the target user is on a low-end Android phone with a slow connection.
Bundle Size Targets
| Resource | Target | Notes |
|---|---|---|
| Initial JS bundle | < 150KB gzipped | SvelteKit's compiled output helps here |
| Initial CSS | < 30KB gzipped | Design tokens + base styles |
| First contentful paint | < 2s on 3G | Server-side rendering critical |
| Time to interactive | < 4s on 3G | Hydration must be fast |
| Per-route chunk | < 50KB gzipped | Aggressive code splitting |
| Fonts | 0KB | System font stack — no network loading |
Image Strategy
- Logo/branding: SVG where possible, PNG fallback < 20KB
- Avatar images: 48x48px thumbnails, lazy-loaded
- Map tiles: 256x256px PNG, cached aggressively
- No decorative images. Every image serves a function.
- Responsive images:
srcsetwith size variants for different viewports - WebP with PNG fallback for raster images
Code Splitting Strategy
SvelteKit's route-based code splitting handles most cases. Additional splits:
| Split | Trigger |
|---|---|
| Map components | Only loaded on screens with maps |
| Chart library | Only loaded on dashboard/analytics screens |
| Builder components | Only loaded on builder screens (email, form, script) |
| Field mode | Only loaded when entering field mode |
| Admin/settings | Only loaded for Org Admin role |
This means a Volunteer's app loads significantly less code than an Org Admin's — they never download the builder, settings, or analytics chunks.
Progressive Enhancement
The SvelteKit app uses server-side rendering with progressive enhancement:
Without JavaScript
The app is not fully functional without JavaScript (SvelteKit hydration is required for interactivity). However, SSR ensures:
- Content is visible before JS loads (readable dashboards, contact lists)
- Links work before hydration (navigation via full page loads)
- Form inputs are visible (though submission requires JS)
- Search engines can index public pages
Without Service Worker
Without a service worker, the app works online-only. The service worker adds:
- Offline shell caching (navigation still works)
- Background sync for queued operations
- Push notification handling
- Map tile caching
The Capacitor native app always has service worker capabilities. Web-only users (accessing via browser) may not, but the core experience degrades gracefully.
Low-End Device Accommodations
For devices with limited RAM and CPU:
- Virtualized lists: Long lists render only visible items (virtual scrolling)
- Reduced animation:
prefers-reduced-motionrespected; additionally, devices with <4GB RAM get reduced transitions - Image quality reduction: Lower-resolution images served to slow connections
- Deferred loading: Non-critical UI (charts, secondary widgets) loads after initial render
- Memory-conscious caching: Cache limits are lower on low-memory devices
Testing Matrix
Required Testing Viewports
| Viewport | Device Representative | Priority |
|---|---|---|
| 360x640 | Budget Android phone (Samsung Galaxy A series) | Critical |
| 375x812 | iPhone SE / iPhone 13 Mini | Critical |
| 390x844 | iPhone 14 / 15 | High |
| 412x915 | Google Pixel / mid-range Android | High |
| 768x1024 | iPad (portrait) | Medium |
| 1024x768 | iPad (landscape) / small laptop | Medium |
| 1280x800 | Standard laptop | High |
| 1440x900 | Large laptop | Medium |
| 1920x1080 | Desktop monitor | Medium |
Priority Testing Scenarios
- Budget Android + slow 3G — the critical path. If it works here, it works everywhere.
- iPhone + LTE — the other major mobile scenario.
- Desktop + broadband — the staff experience.
- Tablet + Wi-Fi — occasional use case.
- RTL on all of the above — Arabic layout testing.
- Dark mode on all of the above — color contrast and readability.
- Field mode on budget Android — the highest-risk UX in the system.
Open Questions
-
Foldable devices. Samsung Galaxy Z Fold and similar foldable devices have two viewports (folded and unfolded). Should the app explicitly handle the fold crease and viewport change? Low priority but growing market.
-
PWA vs. native-only distribution. Should the app be installable as a Progressive Web App (via browser) in addition to the Capacitor-wrapped native app? PWA reduces friction (no app store needed) but has less access to native APIs.
-
Offline map provider. For pre-caching map tiles, which provider offers the best offline tile support for the target countries? OSM tiles are free but large. Vector tiles (Mapbox/MapLibre) are smaller but require more rendering power on low-end devices.