Story 5.1: Filter Modal (Multi-Context)
Header Information
Story: Epic 5, Story 5.1 - Historical Content Search / Multi-Context Filtering Screen: Filter Modal (Responsive Mobile/Tablet Filter Interface) User Role: Attendee, Speaker, Organizer, Partner, Public (context-dependent) Related FR: FR13 (Content discovery with filtering), FR6 (Attendee access with filtering), FR11 (Archive access with search/filter)
Visual Wireframe
Desktop/Tablet View (Sidebar - Not Modal)
┌─── FILTERS ─────────────────────┐
│ │
│ Search within results │
│ ┌────────────────────────────┐ │
│ │ 🔍 Filter by keyword... │ │
│ └────────────────────────────┘ │
│ │
│ Topic Categories (142 total) │
│ ┌────────────────────────────┐ │
│ │ ☑ Cloud Native (142) │ │
│ │ ☐ Security (89) │ │
│ │ ☐ AI/ML (67) │ │
│ │ ☐ DevOps (134) │ │
│ │ ☐ Data Engineering (45) │ │
│ │ [Show More ▼] │ │
│ └────────────────────────────┘ │
│ │
│ Time Period │
│ ┌────────────────────────────┐ │
│ │ ○ Last Month │ │
│ │ ○ Last Year │ │
│ │ ● Last 5 Years │ │
│ │ ○ All Time (20+ years) │ │
│ │ │ │
│ │ Custom Range: │ │
│ │ From: [2020 ▼] To: [2024▼]│ │
│ └────────────────────────────┘ │
│ │
│ Content Type │
│ ┌────────────────────────────┐ │
│ │ ☑ Presentations (523) │ │
│ │ ☑ Videos (89) │ │
│ │ ☐ Code Examples (34) │ │
│ │ ☐ Workshop Materials (12) │ │
│ └────────────────────────────┘ │
│ │
│ Speaker │
│ ┌────────────────────────────┐ │
│ │ 🔍 Search speakers... │ │
│ │ │ │
│ │ ☐ Thomas Weber (12) │ │
│ │ ☐ Sara Kim (8) │ │
│ │ ☐ Alex Müller (15) │ │
│ │ [Show All Speakers ▼] │ │
│ └────────────────────────────┘ │
│ │
│ Company │
│ ┌────────────────────────────┐ │
│ │ ☐ UBS (45) │ │
│ │ ☐ Credit Suisse (38) │ │
│ │ ☐ Swisscom (42) │ │
│ │ ☐ Swiss Re (28) │ │
│ │ [Show All Companies ▼] │ │
│ └────────────────────────────┘ │
│ │
│ Rating │
│ ┌────────────────────────────┐ │
│ │ ○ All Ratings │ │
│ │ ● 4+ Stars (187) │ │
│ │ ○ 3+ Stars (245) │ │
│ └────────────────────────────┘ │
│ │
│ [Clear All] [Apply Filters]│
│ │
└──────────────────────────────────┘
Mobile View (Full-Screen Modal)
┌───────────────────────────────────────────────────────────────────────────┐
│ ✕ Close Filters [Clear All] │
├───────────────────────────────────────────────────────────────────────────┤
│ │
│ Active Filters (3) │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ Cloud Native ✕ Last 5 Years ✕ 4+ Stars ✕ │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
│ 247 results found │
│ │
│ ┌─── TOPIC CATEGORIES ───────────────────────────────────────────────▼─┐ │
│ │ │ │
│ │ Search topics... │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ 🔍 Filter by topic... │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ☑ Cloud Native 142 │ │
│ │ ☐ Security 89 │ │
│ │ ☐ AI/ML 67 │ │
│ │ ☐ DevOps 134 │ │
│ │ ☐ Data Engineering 45 │ │
│ │ ☐ Frontend Development 78 │ │
│ │ ☐ Backend Architecture 92 │ │
│ │ ☐ Database Systems 56 │ │
│ │ ☐ Testing & QA 34 │ │
│ │ ☐ Agile & Project Management 28 │ │
│ │ │ │
│ │ [Load More Topics...] │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─── TIME PERIOD ────────────────────────────────────────────────────▼─┐ │
│ │ │ │
│ │ ○ Last Month │ │
│ │ ○ Last Year │ │
│ │ ● Last 5 Years │ │
│ │ ○ All Time (20+ years) │ │
│ │ │ │
│ │ ☐ Custom Date Range │ │
│ │ ┌──────────────────────┬──────────────────────┐ │ │
│ │ │ From: [2020 ▼] │ To: [2024 ▼] │ │ │
│ │ └──────────────────────┴──────────────────────┘ │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─── CONTENT TYPE ───────────────────────────────────────────────────▲─┐ │
│ │ │ │
│ │ ☑ Presentations 523 │ │
│ │ ☑ Videos 89 │ │
│ │ ☐ Code Examples 34 │ │
│ │ ☐ Workshop Materials 12 │ │
│ │ ☐ Handouts & Documents 67 │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─── SPEAKER ────────────────────────────────────────────────────────▼─┐ │
│ │ │ │
│ │ Search speakers... │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ 🔍 Find speaker by name... │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ Recent & Popular Speakers: │ │
│ │ ☐ Thomas Weber (UBS) 12 │ │
│ │ ☐ Sara Kim (Swisscom) 8 │ │
│ │ ☐ Alex Müller (Credit Suisse) 15 │ │
│ │ ☐ Lisa Wang (Swiss Re) 6 │ │
│ │ │ │
│ │ [Browse All Speakers →] │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─── COMPANY ────────────────────────────────────────────────────────▼─┐ │
│ │ │ │
│ │ ☐ UBS 45 │ │
│ │ ☐ Credit Suisse 38 │ │
│ │ ☐ Swisscom 42 │ │
│ │ ☐ Swiss Re 28 │ │
│ │ ☐ Zurich Insurance 21 │ │
│ │ ☐ PostFinance 18 │ │
│ │ │ │
│ │ [Show All Companies...] │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─── RATING ─────────────────────────────────────────────────────────▼─┐ │
│ │ │ │
│ │ ○ All Ratings 247 │ │
│ │ ● 4+ Stars (⭐⭐⭐⭐) 187 │ │
│ │ ○ 3+ Stars (⭐⭐⭐) 245 │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─── FILTER PRESETS ─────────────────────────────────────────────────▼─┐ │
│ │ │ │
│ │ 💾 My Saved Filters: │ │
│ │ │ │
│ │ 📌 Recent Security Topics [Load] [Delete] │ │
│ │ Security (4+ stars, Last year) │ │
│ │ │ │
│ │ 📌 DevOps Resources [Load] [Delete] │ │
│ │ DevOps, Cloud Native (All time) │ │
│ │ │ │
│ │ [💾 Save Current Filters] │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
│ │
│ │
│ ┌─── ACTIONS ──────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ [Clear All Filters] [Apply Filters (247)] │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
│ │
└───────────────────────────────────────────────────────────────────────────┘
Key Interactive Elements
Collapsible Sections
- Accordion Headers: Each filter category (Topic, Time, Content Type, etc.) is collapsible
- Tap/click header to expand/collapse section
- Arrow indicator (▼/▲) shows expanded/collapsed state
- Default: First 3 sections expanded, rest collapsed
- State persists within session (remembers what user expanded)
Active Filters Bar
- Filter Pills: Selected filters displayed as dismissible pills at top
- Each pill shows filter name with ✕ close button
- Click ✕ to remove individual filter
- Pills wrap to multiple rows if needed
- Color-coded by category (blue for topics, green for time, etc.)
Search Within Filters
- Filter Search Boxes: Search within long lists (topics, speakers, companies)
- Real-time filtering as user types (debounced 300ms)
- Highlights matching text in results
- Shows “No results found” if no matches
- Clear button (✕) appears when text entered
Checkbox Lists
- Multi-Select Checkboxes: Allow multiple selections per category
- Result count shown next to each option (e.g., “Cloud Native (142)”)
- Count updates in real-time as filters applied
- Grayed out with count (0) if no results for that option
- “Select All” / “Deselect All” for categories with many options
Radio Button Groups
- Single-Select Options: Time period, rating level (only one choice)
- Visual indication of selected option (filled circle)
- Tap anywhere on row to select (not just radio button)
Date Range Picker
- Custom Range: Dropdown selectors for start/end year
- Start year must be before end year (validation)
- Defaults to current year for end date
- Goes back to 2000 (20+ years of content)
Result Counter
- Live Result Count: Shows number of results matching current filters
- Updates in real-time as filters changed
- Format: “247 results found” or “No results” if 0
- Helps users understand impact of each filter
Action Buttons
- [Apply Filters]: Sticky button at bottom (always visible)
- Shows result count on button: “Apply Filters (247)”
- Primary action color (blue)
- Disabled if no changes made
- [Clear All]: Remove all filters at once
- Secondary action (gray/outline)
- Confirmation if >5 filters active
- [Save Current Filters]: Save filter preset for future use
- Opens modal to name the preset
- Saves to user profile
Functional Requirements Met
- FR13 (Content Discovery): Advanced filtering for content search with faceted navigation, multi-dimensional filtering
- FR6 (Attendee Access): Filter event listings, session schedules, speaker directories
- FR11 (Archive Access): Filter historical content by year, topic, speaker across 20+ years
- NFR1 (Responsive Design): Mobile-first filter interface with touch-optimized controls, collapsible sections
User Interactions
Opening Filter Modal (Mobile)
- User clicks [Filters] button on Content Discovery or Event Listing screen
- Filter modal slides up from bottom (smooth animation)
- Modal takes full screen on mobile, semi-transparent backdrop
- Current active filters displayed at top
- First 3 filter sections expanded by default
- Result count shows current total
- Focus moves to first interactive element (search box or first checkbox)
Applying Filters
- User selects/deselects checkboxes or radio buttons
- Active filters bar updates immediately (adds/removes pills)
- Result count updates in real-time via debounced API call (500ms)
- [Apply Filters] button updates count: “Apply Filters (142)”
- User scrolls to bottom (or taps sticky button)
- User clicks [Apply Filters (142)]
- Modal closes with smooth animation
- Parent screen (Content Discovery) refreshes with filtered results
- Filter button badge shows active filter count (e.g., “Filters (3)”)
Real-Time Filter Updates
- As user checks/unchecks options:
- Immediate visual feedback (checkbox state changes)
- Active filter pill appears/disappears at top
- Result count API called after 500ms debounce
- Counts next to other options update (e.g., “DevOps (134)” → “DevOps (45)”)
- Options with 0 results become grayed out but remain selectable
- User can see impact of each filter before applying
Searching Within Filters
- User clicks search box in “Topic Categories” section
- Keyboard appears (mobile) or cursor active (desktop)
- User types “secur”
- After 300ms debounce, list filters to show only matching topics:
- “Security (89)”
- “Security Audit Logging (12)”
- “DevSecOps (23)”
- Non-matching topics hidden
- User can click [✕] in search box to clear, showing all topics again
Using Filter Presets
- User has previously saved filter combinations
- User expands “Filter Presets” section (collapsed by default)
- Sees list of saved presets with descriptions:
- “Recent Security Topics” (Security, 4+ stars, Last year)
- “DevOps Resources” (DevOps, Cloud Native, All time)
- User clicks [Load] on “Recent Security Topics”
- All filters reset to preset values:
- Topics: Security ☑
- Rating: 4+ Stars ●
- Time: Last Year ●
- Result count updates
- Active filter pills update
- User can modify loaded preset and save as new
Saving New Preset
- User configures desired filters (e.g., Cloud Native, AI/ML, 4+ stars)
- User clicks [💾 Save Current Filters]
- Modal overlay appears: “Save Filter Preset”
┌─────────────────────────────────────────┐ │ Save Filter Preset │ ├─────────────────────────────────────────┤ │ │ │ Preset Name │ │ ┌───────────────────────────────────┐ │ │ │ AI & Cloud Resources │ │ │ └───────────────────────────────────┘ │ │ │ │ Current Filters: │ │ • Cloud Native │ │ • AI/ML │ │ • 4+ Stars │ │ │ │ [Cancel] [Save Preset] │ │ │ └─────────────────────────────────────────┘ - User enters name, clicks [Save Preset]
- Success toast: “Filter preset saved”
- Preset appears in “Filter Presets” section
Clearing Filters
- User clicks [Clear All] button (top-right or bottom)
- If 5+ filters active: Confirmation dialog
Clear all filters? This will remove all 7 active filters. [Cancel] [Clear All Filters] - On confirm:
- All checkboxes unchecked
- Radio buttons reset to defaults
- Active filter pills disappear
- Result count returns to total (e.g., “247 results”)
- Success toast: “All filters cleared”
Removing Individual Filters
- User clicks ✕ on active filter pill (e.g., “Cloud Native ✕”)
- Filter immediately removed:
- Checkbox unchecks in filter list
- Pill disappears from active filters bar
- Result count updates
- Other filter counts recalculate
- No confirmation needed for single filter removal
Closing Without Applying
- User clicks [✕ Close] button (top-left)
- If changes made but not applied: Confirmation dialog
Discard filter changes? You have unsaved filter changes that will be lost. [Keep Editing] [Discard Changes] - On [Discard Changes]:
- Modal closes
- Filters revert to last applied state
- No changes to search results
- On [Keep Editing]:
- Dialog closes
- Modal remains open
Technical Notes
Frontend Implementation
- Framework: React 18.2 with TypeScript, Material-UI Drawer (mobile) / Sidebar (desktop)
- State Management: Zustand for filter state (persists across navigation), React Hook Form for form state
- Responsive Breakpoints:
- Mobile (<768px): Full-screen modal with collapsible sections
- Tablet (768-1024px): Slide-in drawer (60% width)
- Desktop (>1024px): Permanent sidebar (25% width)
- Animations: Framer Motion for smooth modal transitions, accordion expand/collapse
Real-Time Filter Counts
- Debounced API: 500ms debounce on filter changes to reduce API calls
- Optimistic Updates: UI updates immediately, API call in background
- Caching: Filter count results cached (5 min TTL) per unique filter combination
- Request Deduplication: Multiple rapid filter changes coalesced into single API call
Filter State Persistence
- Session Storage: Current filter state saved to sessionStorage (survives page refresh)
- URL Parameters: Filters encoded in URL query params for shareability
- Example:
?topics=cloud-native,security&rating=4&year=2020-2024 - Deep linking: Shared URL loads with filters pre-applied
- Example:
- User Presets: Saved to user profile (database), synced across devices
Performance Optimization
- Virtualized Lists: Long lists (topics, speakers, companies) use react-window for performance
- Only renders visible items (viewport + buffer)
- Smooth scrolling with thousands of items
- Lazy Loading: Filter options loaded on-demand (expand section → fetch options)
- Search Indexing: Client-side fuzzy search using Fuse.js for filter-within-filter
Accessibility Features
- Keyboard Navigation: Full keyboard support (Tab, Space, Enter, Arrow keys)
- Screen Reader: ARIA labels on all controls, live regions announce result count changes
- Focus Trapping: Modal traps focus (Tab cycles within modal)
- Escape Key: Closes modal (with confirmation if changes made)
API Requirements
Initial Page Load APIs
-
GET /api/v1/content?facets=topics,speakers,companies,contentTypes,years (Story 1.20)
- Consolidation: Uses faceted search pattern to get filter options with counts in single request
- Authorization: Optional (some filters require auth for personalization)
- Query Params:
facets=topics,speakers,companies,contentTypes,years(request filter facets)context(optional): “content” | “events” | “speakers” | “sessions”
- Returns: Available filter options with countsjson
{ "context": "content", "topics": [ { "id": "topic-cloud-native", "name": "Cloud Native", "slug": "cloud-native", "count": 142, "subcategories": [ { "id": "topic-kubernetes", "name": "Kubernetes", "count": 78 }, { "id": "topic-docker", "name": "Docker", "count": 56 } ] }, { "id": "topic-security", "name": "Security", "slug": "security", "count": 89 } // ... more topics ], "contentTypes": [ { "id": "presentation", "name": "Presentations", "count": 523 }, { "id": "video", "name": "Videos", "count": 89 } ], "speakers": [ { "id": "speaker-123", "name": "Thomas Weber", "company": "UBS", "count": 12, "avatar": "https://..." } // ... more speakers ], "companies": [ { "id": "company-456", "name": "UBS", "count": 45 } // ... more companies ], "yearRange": { "min": 2000, "max": 2024, "eventYears": [2024, 2023, 2022, ...] }, "presets": [ { "id": "preset-789", "name": "Recent Security Topics", "description": "Security content, 4+ stars, Last year", "filters": { "topics": ["topic-security"], "rating": 4, "timePeriod": "last-year" }, "createdAt": "2024-03-15T10:00:00Z" } ], "presets": [...] } - Used for: Populate filter options, show counts with facet data
- Performance: <500ms (P95) with facets
- Consolidation Benefit: Single request provides all filter options using faceted search (eliminates need for separate filter-options endpoint)
-
GET /api/v1/users/{userId}/preferences?include=filterPresets (Story 1.23)
- Consolidation: Uses unified preferences endpoint to fetch filter presets
- Authorization: Requires authenticated user
- Path Params:
userId(UUID) - Query Params:
include=filterPresets(expand filter presets from preferences)
- Returns: User’s saved filter presets in preferencesjson
{ "presets": [ { "id": "preset-789", "name": "Recent Security Topics", "description": "Security (4+ stars, Last year)", "context": "content", "filters": { "topics": ["topic-security"], "rating": 4, "timePeriod": "last-year" }, "useCount": 15, "lastUsed": "2024-04-01T14:30:00Z", "createdAt": "2024-03-15T10:00:00Z" }, { "id": "preset-790", "name": "DevOps Resources", "description": "DevOps, Cloud Native (All time)", "context": "content", "filters": { "topics": ["topic-devops", "topic-cloud-native"], "timePeriod": "all-time" }, "useCount": 8, "lastUsed": "2024-03-28T09:15:00Z", "createdAt": "2024-03-10T16:45:00Z" } ] } - Used for: Load user’s saved filter presets from preferences
- Performance: <150ms (P95)
- Consolidation Benefit: Filter presets integrated into user preferences
User Action APIs
-
GET /api/v1/content?filter={}&facets=topics,speakers,companies (Story 1.20)
- Consolidation: Real-time filter count uses same content endpoint with filter parameters
- Triggered by: Filter changes (debounced 500ms), real-time result count updates
- Authorization: Optional
- Query Params:
filter={}(current filter state as JSON)facets=topics,speakers,companies(get updated facet counts)
- Payload: N/A (GET request with query params)json
{ "context": "content", "filters": { "topics": ["topic-cloud-native"], "contentTypes": ["presentation", "video"], "timePeriod": "last-5-years", "rating": 4, "speakers": [], "companies": [] } } - Response: Result count and updated facet countsjson
{ "totalResults": 247, "facetCounts": { "topics": { "topic-cloud-native": 142, "topic-security": 45, "topic-devops": 67, "topic-aiml": 23 }, "contentTypes": { "presentation": 198, "video": 32, "code": 12, "workshop": 5 }, "speakers": { "speaker-123": 8, "speaker-456": 5 }, "companies": { "company-456": 25, "company-789": 18 }, "ratings": { "all": 247, "4plus": 187, "3plus": 245 } }, "executionTimeMs": 45 } - Used for: Update result count display, update counts next to each filter option
- Performance: <500ms (P95)
- Consolidation Benefit: Single content endpoint handles both search and facet counting (eliminates separate /filters/count endpoint)
-
PUT /api/v1/users/{userId}/preferences (Story 1.23)
- Consolidation: Save filter presets through unified preferences endpoint
- Triggered by: [Save Preset] button in save preset modal
- Authorization: Requires authenticated user
- Path Params:
userId(UUID) - Payload: Partial update with new filter presetjson
{ "name": "AI & Cloud Resources", "description": "Cloud Native, AI/ML, 4+ stars", "context": "content", "filters": { "topics": ["topic-cloud-native", "topic-aiml"], "rating": 4, "timePeriod": "all-time" }, "isPublic": false } - Validation:
- Name: Required, 3-50 characters, unique per user+context
- Filters: At least one filter must be set
- Response:json
{ "presetId": "preset-791", "name": "AI & Cloud Resources", "filters": { ... }, "createdAt": "2024-04-01T15:00:00Z", "success": true } - Used for: Save current filter state as reusable preset in user preferences
- Performance: <150ms (P95)
- Consolidation Benefit: Filter presets managed through user preferences (no separate preset endpoints)
-
GET /api/v1/topics?filter={“query”:“{searchTerm}”}&limit=10 (Story 1.21)
- Consolidation: Topic autocomplete uses consolidated Topics API
- Triggered by: Typing in topic filter search box
- Authorization: Optional
- Query Params:
filter={"query":"secur"}(search filter)limit=10(max autocomplete results)
- Returns: Topic autocomplete suggestionsjson
{ "topics": [ { "id": "topic-security", "name": "Security", "count": 89 }, { "id": "topic-security-audit", "name": "Security Audit Logging", "count": 12 } ], "totalMatches": 3 } - Performance: <200ms (P95)
-
GET /api/v1/speakers?filter={“query”:“{searchTerm}”}&limit=10 (Story 1.19)
- Consolidation: Speaker autocomplete uses consolidated Speakers API
- Triggered by: Typing in speaker filter search box
- Authorization: Optional
- Query Params:
filter={"query":"weber"}(search filter)limit=10(max autocomplete results)
- Returns: Speaker autocomplete suggestions
- Performance: <300ms (P95)
-
GET /api/v1/companies/search?query={searchTerm}&limit=10 (Story 1.22)
- Consolidation: Company autocomplete uses dedicated search endpoint with Caffeine in-memory caching
- Triggered by: Typing in company filter search box
- Authorization: Optional
- Query Params:
query={searchTerm}(search query)limit=10(max autocomplete results)
- Returns: Company autocomplete suggestionsjson
{ "query": "secur", "type": "topic", "suggestions": [ { "id": "topic-security", "name": "Security", "match": "Security", "count": 89, "highlightedName": "<mark>Secur</mark>ity" }, { "id": "topic-security-audit", "name": "Security Audit Logging", "match": "Security Audit Logging", "count": 12, "highlightedName": "<mark>Secur</mark>ity Audit Logging" }, { "id": "topic-devsecops", "name": "DevSecOps", "match": "DevSecOps", "count": 23, "highlightedName": "Dev<mark>Sec</mark>Ops" } ], "totalMatches": 3 } - Performance: <100ms (P95) with Caffeine in-memory caching
- Consolidation Benefit: Dedicated search endpoints replace generic autocomplete API, optimized per resource type
Navigation Map
Primary Navigation Actions
-
✕ Close button → Close modal, return to parent screen
- Behavior:
- If no changes: Close immediately
- If changes made: Show confirmation dialog “Discard filter changes?”
- On discard: Reset filters to last applied state, close modal
- On keep editing: Dialog closes, modal remains open
- Target: Parent screen (Content Discovery, Event Listing, Speaker Directory)
- No Changes to Results: Filters not applied, results unchanged
- Behavior:
-
[Apply Filters (247)] → Apply filters, close modal, refresh results
- Behavior:
- Modal closes with smooth transition
- Parent screen shows loading state
- Results refresh with filtered data
- URL updates with filter params
- Filter button badge shows count (e.g., “Filters (3)”)
- Target: Parent screen with filtered results
- Analytics: Track applied filters, popular filter combinations
- Behavior:
-
[Clear All] → Remove all filters
- Behavior:
- If 5+ filters: Confirmation dialog
- All checkboxes unchecked, radio buttons reset
- Active filter pills disappear
- Result count returns to total
- Success toast: “All filters cleared”
- No Navigation: Remains in filter modal
- Behavior:
Secondary Navigation (Data Interactions)
-
Active filter pill ✕ click → Remove individual filter
- Behavior:
- Pill disappears from active filters bar
- Corresponding checkbox/radio unchecks
- Result count updates (debounced API call)
- Other filter counts recalculate
- No Navigation: Remains in filter modal
- Behavior:
-
[Load] button on saved preset → Load filter preset
- Behavior:
- All filters reset to preset values
- Checkboxes/radios update to match preset
- Active filter pills update
- Result count updates
- Success toast: “Filter preset loaded: {preset name}”
- No Navigation: Remains in filter modal with loaded filters
- Behavior:
-
[Delete] button on saved preset → Delete filter preset
- Behavior:
- Confirmation dialog: “Delete filter preset ‘{name}’?”
- On confirm: API call to delete
- Preset removed from list
- Success toast: “Filter preset deleted”
- No Navigation: Remains in filter modal
- Behavior:
-
[💾 Save Current Filters] button → Open save preset modal
- Target: Modal overlay for preset naming
- Behavior:
- Save preset modal appears
- User enters name and description
- On save: Preset added to list
- On cancel: Modal closes, no changes
- No Navigation: Remains in filter modal after save
-
[Browse All Speakers →] link → Navigate to full speaker list
- Target: Speaker Directory page (if exists)
- Context: Pre-filters based on current topic/company filters
- Behavior: Opens speaker directory in new context, filter modal may close or remain
-
[Show More ▼] / [Show Less ▲] in filter lists → Expand/collapse list
- Behavior:
- Toggles between showing 5 items vs. all items
- Arrow indicator flips (▼ ↔ ▲)
- Smooth height transition animation
- No Navigation: Remains in filter modal
- Behavior:
-
Accordion section header click → Expand/collapse section
- Behavior:
- Section content slides down/up
- Arrow rotates (▼ → ▲)
- Other sections remain unchanged (multi-expand allowed)
- No Navigation: Remains in filter modal
- Behavior:
Event-Driven Navigation
-
Filter change → Update result count
- Trigger: Checkbox toggle, radio selection, date change
- Behavior:
- Immediate UI update (check/uncheck)
- Active filter pill appears/disappears
- After 500ms debounce: API call for new count
- Result count updates: “247 results” → “142 results”
- Filter option counts recalculate
- No Navigation: Remains in filter modal
-
Search within filters → Filter the filter list
- Trigger: Typing in filter search box (300ms debounce)
- Behavior:
- Non-matching items hidden
- Matching items highlighted
- “No results found” if no matches
- Clear button (✕) appears in search box
- No Navigation: Remains in filter modal
-
Zero results warning → Show warning message
- Trigger: Result count reaches 0
- Behavior:
- Warning banner appears: “⚠️ No results found with current filters. Try removing some filters.”
- [Clear All] button emphasized
- [Apply Filters] button disabled (grayed out)
- No Navigation: Remains in filter modal
-
Preset loaded → Update all filter controls
- Trigger: Click [Load] on saved preset
- Behavior:
- All filters reset to preset state
- Checkboxes/radios animated state change
- Active filter pills update
- Result count updates
- Success toast notification
- No Navigation: Remains in filter modal
Error States & Redirects
-
Filter options load failed (500) → Show error state
- Condition: API error loading filter options
- Display: Error message in modal
⚠️ Unable to load filters There was an error loading filter options. Please try again. [Retry] [Close] - Actions:
- [Retry]: Reload filter options API
- [Close]: Close modal, return to parent screen
-
Result count timeout → Show stale count warning
- Condition: Count API takes >5 seconds
- Display: Warning next to count: “Result count may be outdated. [Refresh]”
- Action: [Refresh] button retries count API
- No Navigation: Remains in filter modal
-
Save preset failed (400/500) → Show error message
- Condition: Preset save API error
- Display: Error toast with reason
- 400: “Preset name already exists. Please choose a different name.”
- 500: “Unable to save preset. Please try again.”
- No Navigation: Save modal remains open for retry
-
Session timeout during filtering → Save state, redirect to login
- Condition: JWT token expired while modal open
- Behavior:
- Filter state saved to sessionStorage
- Login modal appears: “Session expired. Please log in to continue.”
- After login: Return to same screen, restore filter state
- Toast: “Your filters have been restored.”
Responsive Design Considerations
Mobile Layout (< 768px)
- Full-Screen Modal: Takes entire viewport, slides up from bottom
- Sticky Header: “Filters” title with close and clear buttons always visible
- Collapsible Sections: All filter categories start collapsed except first 3
- Active Filters Bar: Sticky at top (below header), always visible while scrolling
- Accordion Animations: Smooth expand/collapse with spring physics
- Sticky Footer: [Apply Filters (247)] button always visible at bottom
- Touch Targets: Minimum 44x44px for all interactive elements
- Swipe Gestures:
- Swipe down from top → Close modal (with confirmation if changes)
- Swipe on checkbox rows → No action (prevent accidental swipes)
Tablet Layout (768px - 1024px)
- Slide-In Drawer: 60% width, slides in from right
- Semi-Transparent Backdrop: Click outside drawer to close (with confirmation)
- Scrollable Content: Drawer content scrolls independently
- Two-Column Checkboxes: Some sections show 2 columns of checkboxes to save vertical space
- Sticky Footer: Apply/Clear buttons sticky at drawer bottom
Desktop Layout (> 1024px)
- Permanent Sidebar: Not a modal, always visible on left (25% width)
- No Close Button: Sidebar always open, not dismissible
- Apply on Change: Option to auto-apply filters as they change (no Apply button needed)
- Setting in user preferences: “Apply filters automatically”
- If enabled: Results update in real-time (debounced)
- If disabled: [Apply Filters] button required
- Hover Interactions: Show tooltips on hover for truncated text
Mobile-Specific Interactions
- Tap Section Headers: Expand/collapse sections (large tap target)
- Pull-to-Refresh: Not applicable in modal (conflicting gesture)
- Virtual Keyboard: Modal resizes when keyboard appears, focused input remains visible
- Native Date Pickers: Use native iOS/Android date pickers for date range
Accessibility Notes
-
Keyboard Navigation:
- Tab: Move between interactive elements (checkboxes, radios, buttons)
- Space: Toggle checkbox, select radio button, expand/collapse section
- Enter: Activate button, expand/collapse section
- Escape: Close modal (with confirmation if changes)
- Arrow Up/Down: Navigate within checkbox/radio lists
-
Focus Management:
- On modal open: Focus moves to first checkbox or search box
- On modal close: Focus returns to “Filters” button that opened modal
- Focus trap: Tab cycles within modal (doesn’t escape to background)
- Focus visible: High-contrast outline on focused elements
-
Screen Reader Support:
- ARIA labels: “Filter modal”, “Topic category filters”, “Content type filters”
- ARIA live regions: Result count changes announced (“247 results found” → “142 results found”)
- ARIA expanded: Accordion sections announce expanded/collapsed state
- ARIA checked: Checkbox states announced
- ARIA selected: Radio button states announced
- Section headers: Proper heading hierarchy (h2 for main sections, h3 for subsections)
-
Visual Indicators:
- Color + Icon: Active filters use both color (blue) and checkmark icon
- High Contrast: All text meets WCAG 2.1 AA (4.5:1 for normal text)
- Focus Indicators: 3px solid outline, high contrast ratio
- Disabled State: Grayed out with lower opacity, not just color
-
Error Messaging:
- Errors announced via ARIA live region
- Error text visible, not just color indication
- Clear instructions on how to fix (e.g., “Please select at least one filter”)
State Management
Local Component State
expandedSections: Array of section IDs that are currently expandedfilterValues: Object containing current filter selectionsjavascript{ topics: ["topic-cloud-native", "topic-security"], contentTypes: ["presentation", "video"], timePeriod: "last-5-years", customRange: { from: 2020, to: 2024 }, speakers: [], companies: [], rating: 4 }appliedFilters: Last applied filter state (for reverting on cancel)resultCount: Current result count from APIisLoading: Boolean for loading state during count API callssearchTerms: Object mapping section ID to search term (filter-within-filter)
Global State (Zustand Store)
activeFilters: Currently active/applied filters (persisted)filterPresets: User’s saved filter presetsfilterHistory: Recent filter combinations (for suggestions)
Server State (React Query)
-
Query Keys:
['filters', 'options', context]: Filter options with counts (5 min cache)['filters', 'count', filterState]: Result count for filter combination (2 min cache)['user', userId, 'filter-presets', context]: User’s saved presets (10 min cache)
-
Mutations:
updateFilterCount: Invalidates count query, triggers recalculationsavePreset: Invalidates presets query, adds new preset to listdeletePreset: Invalidates presets query, removes preset from list
Persistence
- Session Storage: Current filter state (survives page refresh within session)
- URL Parameters: Applied filters encoded in URL for sharing
- Example:
?f=topics:cloud-native,security|rating:4|period:last-5-years
- Example:
- Local Storage: Expanded sections preference (remembers which sections user keeps open)
- User Profile: Saved filter presets (synced to database)
Form Validation Rules
Filter Preset Saving
-
Preset Name:
- Required: Cannot be empty
- Min Length: 3 characters
- Max Length: 50 characters
- Unique: Must be unique within user’s presets for same context
- Pattern: Alphanumeric, spaces, hyphens allowed
-
Preset Description:
- Optional: Can be empty
- Max Length: 200 characters
-
Filter Values:
- At least one filter must be selected to save preset
- Error: “Please select at least one filter before saving preset”
Date Range
-
From Year:
- Required if custom range selected
- Range: 2000 to current year
- Must be ≤ To Year
-
To Year:
- Required if custom range selected
- Range: From Year to current year
- Must be ≥ From Year
-
Validation:
- If From > To: Error “Start year must be before end year”
Edge Cases & Error Handling
Empty States
-
No Filter Options: If API returns empty arrays
- Display: “No filters available for this content type”
- Hide empty sections entirely
-
No Saved Presets: If user has no saved presets
- Display: “You haven’t saved any filter presets yet. Configure filters and click [Save Current Filters] to create one.”
-
No Search Results: If filter search returns no matches
- Display: “No matching {topics/speakers/companies} found. Try a different search term.”
Loading States
- Initial Load: Skeleton screens for filter sections while loading options
- Result Count Update: Spinner next to count, previous count shown until new arrives
- Preset Load: Loading indicator on [Load] button during API call
- Save Preset: Spinner on [Save Preset] button, disabled during save
Error States
-
API Timeout: If filter options API takes >10 seconds
- Error: “Loading filters is taking longer than expected. [Retry] [Continue Without Filters]”
-
Zero Results: If all filters result in 0 results
- Warning banner: “⚠️ No results match your filters. Try removing some filters or [Clear All]”
- [Apply Filters] button disabled
-
Preset Name Conflict: If user tries to save preset with existing name
- Error: “A preset with this name already exists. Please choose a different name or delete the existing preset.”
-
Network Error During Apply: If results API fails after applying filters
- Error toast: “Unable to load filtered results. Please try again.”
- [Retry] button in parent screen
Concurrent Actions
- Multiple Filter Changes: Debouncing ensures only last change triggers API
- Apply While Count Loading: If user clicks [Apply] before count loads
- Use last known count, proceed with apply
- Results screen shows loading state until actual results arrive
Performance Edge Cases
- 1000+ Filter Options: Virtualized lists prevent DOM bloat
- Slow Network: Optimistic UI updates, show immediate feedback
- Rapid Toggling: Debounced API calls (500ms) prevent request spam
Change Log
| Date | Version | Description | Author |
|---|---|---|---|
| 2025-04-01 | 1.0 | Initial wireframe creation | Sally (UX) |
Review Notes
Stakeholder Feedback
To be added after stakeholder review
Design Iterations
To be documented as design evolves
Open Questions
-
Auto-Apply vs. Manual Apply: Should filters auto-apply as users select them (real-time results), or require explicit [Apply] button?
- Current: Manual apply (better for mobile, prevents excessive API calls)
- Desktop Alternative: Auto-apply with debounce (500ms) for faster UX
- Recommendation: User preference setting, default to manual apply
-
Filter Combination Logic: How should multiple filters within same category combine?
- Current: OR logic within category (Cloud Native OR Security), AND across categories (Topics AND Rating)
- Alternative: Allow user to switch between AND/OR per category
- Recommendation: Keep simple OR/AND for MVP, add advanced logic post-launch
-
Shareable Filters: Should users be able to share filter presets with other users or make them public?
- Current: Private presets only
- Enhancement: “Share preset” generates link, “Public presets” gallery
- Use Case: Team collaboration, curated filter lists
- Recommendation: Add post-MVP if user demand exists
-
Filter Analytics: Should we track which filters are most popular to optimize UI?
- Data Collected: Filter usage frequency, popular combinations, abandoned filters
- Use Cases: Reorder filters by popularity, suggest common combinations
- Privacy: Aggregate data only, no individual tracking
- Recommendation: Implement analytics, use for continuous UI improvement
-
Advanced Filters: Should there be “advanced mode” with more granular controls?
- Examples: Exact date ranges, exclude certain topics, min/max view counts
- Current: Basic filters cover 95% of use cases
- Recommendation: Add “Advanced Filters” toggle post-MVP for power users