Making Dashboard Stat Cards Actionable: Implementing Tab Navigation from the Maintenance Hub UI
One of the fundamental UX patterns in dashboard design is discoverability—users should be able to navigate to detailed views from summary statistics with a single click. During this session, we implemented exactly that pattern for the JADA Maintenance Hub: making the "Total Tasks" stat card clickable to navigate directly to the Systems tab where the full task table lives.
The Problem
The maintenance dashboard's home view displays key metrics in stat cards, including a "Total Tasks: 82" card. However, this card was purely informational—clicking it did nothing. Users interested in viewing the full task list had to manually navigate via the tab bar, creating an extra step in a workflow that should be streamlined.
Technical Implementation
Understanding the Existing Architecture
The maintenance hub is served from an S3 bucket and distributed via CloudFront. The file structure is:
- S3 Location:
s3://[maintenance-bucket-name]/index.html - CloudFront Distribution: Queried to identify the maintenance subdomain distribution ID
- Tab Navigation: Implemented via JavaScript function
switchTab(tabName)
The existing tab system uses HTML data attributes (data-tab) and a switch statement in the JavaScript to show/hide content based on the active tab. The Systems tab contains the task table rendering logic.
Locating the Target Element
First, we downloaded the current index.html from S3 to understand the DOM structure and find the Total Tasks card:
aws s3 cp s3://[maintenance-bucket]/index.html ./maintenance_index.html
Searching the file revealed the stat card was rendered as a simple <div class="info-card"> element within the dashboard header. This card needed two modifications:
- Visual feedback: Add CSS styling to indicate it's clickable (cursor change, hover state)
- Functional behavior: Attach an onclick handler that calls
switchTab('systems')
CSS Changes
We added a new CSS class info-card-link to provide visual affordance that the card is interactive:
.info-card-link {
cursor: pointer;
transition: all 0.2s ease;
}
.info-card-link:hover {
background-color: rgba(0, 0, 0, 0.05);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
This follows established patterns from the JavaScript ecosystem: the cursor changes to a pointer, a subtle background color shift occurs on hover, and a slight upward transform creates a "lifting" effect. The transition property ensures smooth animation rather than abrupt changes.
HTML/JavaScript Changes
The Total Tasks card element was modified from:
<div class="info-card">
<div class="stat-value">82</div>
<div class="stat-label">Total Tasks</div>
</div>
To:
<div class="info-card info-card-link" onclick="switchTab('systems')">
<div class="stat-value">82</div>
<div class="stat-label">Total Tasks</div>
</div>
We chose inline onclick over event listeners because the card is static HTML—there's no dynamic element creation happening at runtime, so the performance and readability trade-off favors the simpler approach.
Infrastructure and Deployment
S3 Upload
After modifying the HTML locally, we uploaded the updated file back to S3:
aws s3 cp ./maintenance_index.html s3://[maintenance-bucket]/index.html --content-type "text/html"
CloudFront Cache Invalidation
S3 alone doesn't invalidate cached content—CloudFront continues serving the old version until its TTL expires or we explicitly invalidate it. We queried the CloudFront distributions to find the one serving the maintenance subdomain:
aws cloudfront list-distributions --query 'DistributionList.Items[?Aliases.Items[0]==`maintenance.[domain]`]' --output json
Once we had the distribution ID, we invalidated the root path:
aws cloudfront create-invalidation --distribution-id [DISTRIBUTION_ID] --paths "/*"
Invalidating /* rather than just /index.html is safer—it clears all cached assets, preventing any edge case where related resources (CSS, JavaScript bundles) might be out of sync. The invalidation propagates to all CloudFront edge locations globally within seconds.
Why This Approach
- No backend changes required: Tab switching is purely client-side via existing JavaScript functions. This reduces deployment complexity and risk.
- Semantic HTML: We didn't convert the div to a button element because the card isn't a form control—it's a navigational element triggering a state change in the UI. A div with onclick is semantically appropriate here.
- CSS class separation: By creating
info-card-linkas a separate class, we preserve the ability to have non-clickable info cards elsewhere on the page. This follows the Open/Closed Principle—the code is open for extension but closed for modification. - Hover state design: The combination of color, shadow, and transform provides multi-modal feedback—users with motor disabilities see the color change, users with visual impairments benefit from the shadow depth, and fully-sighted users get the complete "lifting" effect.
Testing and Verification
After deployment, we verified:
- CloudFront invalidation completed (checked status via AWS console)
- Navigated to the maintenance dashboard in a browser (with cache cleared)
- Clicked the Total Tasks card and confirmed navigation to the Systems tab
- Verified the hover state appears on mouseover
What's Next
This pattern can be extended to other stat cards on the dashboard. If, for example, a "Pending Approvals" card exists, it could be linked to an Approvals tab using the same approach. The CSS class and onclick pattern is now established and reusable across the maintenance hub and potentially other dashboards in the JADA system.
For accessibility, consider adding ARIA attributes in future iterations: role="button" and tabindex="0" on the card would allow keyboard navigation and screen reader identification as an interactive element, which is a standard practice for divs that act as buttons.