Saved Views: UI/UX Patterns
New .7.x
This page documents the user interface and user experience patterns for the Saved Views feature in Mapify, including dropdown design, keyboard shortcuts, accessibility requirements, loading states, error handling, and mobile behavior.
Back to Saved Views
Dropdown Menu: Location and Behavior
The Saved Views dropdown is the primary interface for accessing and managing saved views.
Desktop location:
[ Filters ] [ Saved Views ▾ ] [ Zoom In ] [ Zoom Out ]
Mobile/Tablet: Hamburger menu → "Saved Views" submenu
Badge indicator: Displays count: "Saved Views (12)"
Dropdown Behavior
When the user clicks Saved Views:
- Opens dropdown panel below button (desktop) or full-screen modal (mobile)
- Content order: search box → favorites → personal views → shared views → action footer
- Dropdown closes when: view is selected, click outside, or
Escpressed
Dropdown HTML Structure
<nav aria-label="Saved Views Menu">
<button
id="saved-views-trigger"
aria-haspopup="true"
aria-expanded="false"
aria-label="Open saved views menu. Currently 12 saved views available.">
<i class="fas fa-bookmark" aria-hidden="true"></i>
Saved Views (12)
</button>
<div id="saved-views-dropdown" role="menu" aria-labelledby="saved-views-trigger" hidden>
<div class="search-box">
<label for="view-search" class="visually-hidden">Search saved views</label>
<input type="text" id="view-search" placeholder="Search views..."
aria-describedby="search-help" autocomplete="off">
<span id="search-help" class="visually-hidden">Type to filter saved views by name or tag</span>
</div>
<section aria-labelledby="favorites-heading">
<h3 id="favorites-heading">
<i class="fas fa-star" aria-hidden="true"></i> Favorites
</h3>
<ul role="menu">
<li role="menuitem">
<button class="view-item" data-view-id="a3f8d7c2-..."
aria-label="Load favorite view: GDPR Compliance Audit Q1 2026">
<i class="fas fa-bookmark" aria-hidden="true"></i>
<span class="view-name">GDPR Compliance Audit Q1 2026</span>
<span class="view-meta">Created: 2026-01-15</span>
</button>
</li>
</ul>
</section>
<section aria-labelledby="personal-heading">
<h3 id="personal-heading">
<i class="fas fa-user" aria-hidden="true"></i> Personal Views
</h3>
<ul role="menu">
<li role="menuitem">
<button class="view-item" data-view-id="...">
<i class="fas fa-bookmark" aria-hidden="true"></i>
<span class="view-name">JD - SAP Analysis - 2026-01</span>
</button>
</li>
</ul>
</section>
<section aria-labelledby="shared-heading">
<h3 id="shared-heading">
<i class="fas fa-users" aria-hidden="true"></i> Shared Views
</h3>
<ul role="menu">
<li role="menuitem">
<button class="view-item" data-view-id="...">
<i class="fas fa-bookmark" aria-hidden="true"></i>
<span class="view-name">Executive - Monthly Dashboard - 2026-01</span>
<span class="view-owner">by admin@nodinite.com</span>
</button>
</li>
</ul>
</section>
<footer class="dropdown-footer">
<button class="btn-primary" aria-label="Create new saved view from current graph state">
<i class="fas fa-plus" aria-hidden="true"></i> Create New View
</button>
<button class="btn-secondary" aria-label="Open saved views management panel">
<i class="fas fa-cog" aria-hidden="true"></i> Manage All Views
</button>
</footer>
</div>
</nav>
Desktop Layout Mockup
┌─────────────────────────────────────────────────────────────────────┐
│ [ ≡ Menu ] [ 🔍 Search ] [ ⚙ Filters ] [ 📑 Saved Views ▾ ] │
│ └─────────────────┐ │
│ ┌──────────────────────────────────────────────────────┐ │ │
│ │ 📑 Saved Views (12) │ │ │
│ ├──────────────────────────────────────────────────────┤ │ │
│ │ 🔍 [Search views by name or tag...] │ │ │
│ ├──────────────────────────────────────────────────────┤ │ │
│ │ ⭐ FAVORITES │ │ │
│ │ 📌 GDPR Compliance Audit Q1 2026 │ │ │
│ │ 👤 admin@nodinite.com · Created: 2026-01-15 │ │ │
│ │ 📌 Executive - Monthly Dashboard - 2026-01 │ │ │
│ ├──────────────────────────────────────────────────────┤ │ │
│ │ 👤 PERSONAL VIEWS (5) │ │ │
│ │ 📄 JD - SAP Analysis - 2026-01 │ │ │
│ │ 📄 MK - Quick Debug - v2 │ │ │
│ │ ... [Show 3 more] │ │ │
│ ├──────────────────────────────────────────────────────┤ │ │
│ │ 👥 SHARED VIEWS (5) │ │ │
│ │ 📄 Compliance - SOX Audit - Q1 2026 │ │ │
│ │ 📄 Onboarding - Architecture Overview - v3 │ │ │
│ │ ... [Show 3 more] │ │ │
│ ├──────────────────────────────────────────────────────┤ │ │
│ │ [ ➕ Create New View ] [ ⚙ Manage All Views ] │ │ │
│ └──────────────────────────────────────────────────────┘ │ │
└─────────────────────────────────────────────────────────────────────┘
Keyboard Shortcuts
| Action | Windows/Linux | macOS | Description |
|---|---|---|---|
| Save Current View | Ctrl + S |
Cmd + S |
Opens "Save View" dialog |
| Open Saved Views | Ctrl + O |
Cmd + O |
Opens dropdown to load existing view |
| Quick Switch (Next) | Ctrl + Shift + . |
Cmd + Shift + . |
Cycles to next view in favorites (MRU order) |
| Quick Switch (Previous) | Ctrl + Shift + , |
Cmd + Shift + , |
Cycles to previous view in favorites |
| Search Views | Ctrl + K |
Cmd + K |
Opens dropdown with focus on search box |
| Close Dropdown | Esc |
Esc |
Closes dropdown without loading a view |
| Navigate List | Arrow Up/Down |
Arrow Up/Down |
Moves focus between items in dropdown |
| Load View | Enter |
Enter |
Loads the currently focused view |
| Mark as Favorite | Ctrl + F |
Cmd + F |
Toggles favorite status for focused view |
Implementation notes:
- Display shortcuts in button tooltips
- Prevent browser default behavior (e.g.,
Ctrl + Smust not trigger "Save Page") - Include shortcut in ARIA labels:
aria-label="Save current view. Keyboard shortcut: Control S"
document.addEventListener('keydown', (e) => {
if ((e.ctrlKey || e.metaKey) && e.key === 's') {
e.preventDefault();
openSaveViewDialog();
announceToScreenReader('Save view dialog opened');
}
if ((e.ctrlKey || e.metaKey) && e.key === 'o') {
e.preventDefault();
toggleSavedViewsDropdown();
announceToScreenReader('Saved views menu opened. Use arrow keys to navigate.');
}
if (e.key === 'Escape' && savedViewsDropdownIsOpen) {
closeSavedViewsDropdown();
announceToScreenReader('Saved views menu closed');
}
});
Accessibility (WCAG 2.1 AA)
Keyboard Navigation
- Tab key: Reaches all interactive elements — trigger → search → favorites → personal → shared → action buttons
- No keyboard traps: Users can tab in and out of dropdown without mouse
- Arrow keys: Navigate between view items
- Enter/Space: Load selected view
- Escape: Close dropdown and return focus to trigger button
function closeSavedViewsDropdown() {
dropdown.setAttribute('hidden', '');
triggerButton.setAttribute('aria-expanded', 'false');
triggerButton.focus(); // Return focus to trigger
}
Screen Reader Support
<!-- Status announcements -->
<div id="status-message" role="status" aria-live="polite" aria-atomic="true" class="visually-hidden"></div>
Announcement examples:
- Dropdown opens: "Saved views menu opened. 12 views available. Use arrow keys to navigate."
- View selected: "GDPR Compliance Audit Q1 2026. Shared view created by admin@nodinite.com."
- View loads: "View loaded successfully. Displaying 47 entities."
- No results: "No saved views found matching 'test'. Try different search terms."
Color Contrast
.view-name { color: #212529; } /* 16.1:1 on white ✓ */
.view-meta { color: #6c757d; } /* 4.6:1 on white ✓ */
.view-item:focus {
outline: 2px solid #0056b3; /* 4.5:1 ✓ */
outline-offset: 2px;
box-shadow: 0 0 0 3px rgba(0, 86, 179, 0.2);
}
Non-color indicators: Favorites use ⭐ icon + yellow border + section heading. Shared views use 👥 icon + "by [owner]" text. Active view uses checkmark icon + bold text.
Loading States and Error Handling
Loading States
<!-- Dropdown initial load -->
<div role="status" aria-live="polite">
<i class="fas fa-spinner fa-spin" aria-hidden="true"></i>
<span>Loading saved views...</span>
</div>
<!-- View loading after selection -->
<div class="graph-overlay" role="status" aria-live="assertive">
<i class="fas fa-circle-notch fa-spin" aria-hidden="true"></i>
<h2>Loading View: GDPR Compliance Audit Q1 2026</h2>
<progress value="60" max="100" aria-label="Loading progress: 60%">60%</progress>
</div>
<!-- Save button during operation -->
<button class="btn-primary" disabled aria-busy="true">
<i class="fas fa-spinner fa-spin" aria-hidden="true"></i>
Saving view...
</button>
Error Handling
Scenario 1: Network failure
<div class="alert alert-error" role="alert">
<i class="fas fa-exclamation-triangle" aria-hidden="true"></i>
<strong>Error:</strong> Failed to load "GDPR Audit Q1 2026" — network connection lost.
<button onclick="retryLoadView()">
<i class="fas fa-redo" aria-hidden="true"></i> Retry
</button>
</div>
Scenario 2: View deleted by another user
<div class="alert alert-warning" role="alert">
<i class="fas fa-info-circle" aria-hidden="true"></i>
<strong>View Not Found:</strong> "Executive Dashboard" has been deleted.
<button onclick="refreshViewsList()">Refresh Views List</button>
</div>
Scenario 3: Concurrent edit conflict
<div class="alert alert-error" role="alert">
<i class="fas fa-exclamation-triangle" aria-hidden="true"></i>
<strong>Update Conflict:</strong> This view was modified by admin@nodinite.com at 15:30.
<button onclick="reloadViewAndMerge()">
<i class="fas fa-sync" aria-hidden="true"></i> Reload and Retry
</button>
<button onclick="saveAsNewView()">
<i class="fas fa-save" aria-hidden="true"></i> Save as New View
</button>
</div>
Error message best practices:
- Specific description — never "Error occurred"
- User-actionable next steps (Retry, Refresh, Contact support)
- Multiple recovery options (Reload, Save as New, Discard)
- Use
role="alert"for immediate screen reader announcements
User Feedback: Toasts and Spinners
Toast Notifications
<!-- Success -->
<div class="toast toast-success" role="status" aria-live="polite">
<i class="fas fa-check-circle" aria-hidden="true"></i>
<span><strong>Success:</strong> View "GDPR Audit Q1 2026" saved successfully.</span>
<button class="toast-close" aria-label="Close notification">×</button>
</div>
<!-- Warning -->
<div class="toast toast-warning" role="status" aria-live="polite">
<i class="fas fa-exclamation-triangle" aria-hidden="true"></i>
<span>Large dataset: This view may take 5–10 seconds to render.</span>
</div>
Toast behavior:
- Auto-dismiss: 5 seconds (success/info), 10 seconds (warning/error)
- Manual dismiss: × button or Escape
- Position: bottom-right (desktop), top-center (mobile)
Progress Indicator (Long Operations)
<div class="progress-dialog" role="dialog" aria-labelledby="progress-title">
<h2 id="progress-title">Loading View: GDPR Audit Q1 2026</h2>
<p>Rendering 10,247 entities...</p>
<div class="progress-bar" role="progressbar"
aria-valuenow="60" aria-valuemin="0" aria-valuemax="100">
<div class="progress-fill" style="width: 60%;"></div>
</div>
<span aria-live="polite">60% complete</span>
<button onclick="cancelViewLoad()">
<i class="fas fa-times" aria-hidden="true"></i> Cancel
</button>
</div>
Mobile and Responsive Design
Mobile (<768px): Full-Screen Modal
<div class="saved-views-mobile-modal" role="dialog" aria-labelledby="mobile-modal-title">
<header class="modal-header">
<h2 id="mobile-modal-title">
<i class="fas fa-bookmark" aria-hidden="true"></i> Saved Views
</h2>
<button class="btn-close" aria-label="Close saved views">
<i class="fas fa-times" aria-hidden="true"></i>
</button>
</header>
<div class="modal-body">
<input type="search" placeholder="Search views..." class="search-mobile">
<!-- View list sections -->
</div>
<footer class="modal-footer">
<button class="btn-primary btn-block">
<i class="fas fa-plus" aria-hidden="true"></i> Create New View
</button>
</footer>
</div>
Mobile mockup:
┌─────────────────────────────┐
│ 📑 Saved Views [✖] │
├─────────────────────────────┤
│ 🔍 [Search views...] │ ← Sticky
├─────────────────────────────┤
│ ⭐ FAVORITES [▼] │
│ 📌 GDPR Audit Q1 2026 │
│ 📌 Executive Dashboard │
├─────────────────────────────┤
│ 👤 PERSONAL VIEWS (5) [▶] │ ← Collapsed
├─────────────────────────────┤
│ 👥 SHARED VIEWS (5) [▶] │ ← Collapsed
├─────────────────────────────┤
│ [ ➕ Create New View ] │
└─────────────────────────────┘
Touch requirements:
- Minimum tap target: 44×44px
- Minimum item spacing: 8px
- Swipe left on item reveals Delete and Edit actions
- Collapsible sections save screen space
Tablet (768–1024px): Side Panel
- Panel slides in from right (
position: fixed; width: 320px; height: 100vh) - Remains open while browsing (multi-column layout)
- Optional preview pane on tap before loading
Responsive CSS
@media (min-width: 1025px) {
.saved-views-dropdown { position: absolute; width: 400px; max-height: 600px; overflow-y: auto; }
}
@media (min-width: 768px) and (max-width: 1024px) {
.saved-views-panel { position: fixed; right: 0; top: 0; width: 320px; height: 100vh;
transform: translateX(100%); transition: transform 0.3s ease; }
.saved-views-panel.open { transform: translateX(0); }
}
@media (max-width: 767px) {
.saved-views-modal { position: fixed; inset: 0; z-index: 1000; background: white; }
.view-item { min-height: 48px; padding: 12px 16px; }
}
Performance Optimization for UI
- Lazy loading: Load 20 views initially; load more on scroll
- Virtual scrolling: For 100+ views, render only visible items (reduces DOM)
- Debounced search: Wait 300ms after typing stops before filtering
let searchTimeout;
searchInput.addEventListener('input', (e) => {
clearTimeout(searchTimeout);
searchTimeout = setTimeout(() => {
const query = e.target.value.toLowerCase();
filterViews(query);
announceToScreenReader(`${filteredCount} views found matching "${query}"`);
}, 300);
});
Related Topics
- Saved Views – Feature overview and workflow
- Technical Specifications – JSON schema, storage, versioning, conflict resolution
- Multi-User Collaboration
- Repository Model