Making Dashboard Cards Interactive: Linking UI Elements to Tab Navigation in a Maintenance Hub

During a recent maintenance hub update, we needed to improve discoverability of the Tasks view by making the "Total Tasks" summary card clickable. This seemingly simple UX enhancement required coordination across multiple layers: DOM manipulation, event handling, CSS state management, S3 deployment, and CloudFront cache invalidation. Here's how we approached it.

The Problem

The JADA maintenance hub displays a dashboard with several info cards showing high-level metrics: Total Tasks, Active Systems, Pending Work, etc. Users viewing the cockpit dashboard saw "Total Tasks: 82" but had no obvious way to drill into that data. The detailed task table existed in the Systems tab, but required users to manually navigate there. We needed to make that card a direct gateway to the underlying data.

Architecture Overview

The maintenance hub is a single-page application (SPA) served from an S3 bucket fronted by CloudFront:

  • S3 Bucket: maintenance.jada.s3.amazonaws.com
  • CloudFront Distribution: maintenance.sailjada.com (subdomain routed via Route53)
  • Primary File: /index.html (contains all markup, styles, and navigation logic)
  • Tab System: Client-side switching via JavaScript `switchTab()` function with data-tab attributes

The application uses a tabbed interface where each tab is hidden/shown via CSS classes and data attributes. The Tasks data lives in a table rendered when the "systems" tab is active.

Technical Implementation

Step 1: Identify the Target Element

We first downloaded the current index.html from S3 to understand the DOM structure:

aws s3 cp s3://maintenance.jada.s3.amazonaws.com/index.html . --profile jada

Located the Total Tasks card markup:

<div class="info-card">
  <div class="card-label">Total Tasks</div>
  <div class="card-value">82</div>
</div>

And identified the existing tab navigation function within the <script> section:

function switchTab(tabName) {
  // Hide all tabs
  document.querySelectorAll('[data-tab]').forEach(el => {
    el.classList.remove('active');
  });
  // Show selected tab
  document.querySelector(`[data-tab="${tabName}"]`).classList.add('active');
}

The target tab was identified as data-tab="systems", which contains the task table.

Step 2: Make the Card Clickable

We wrapped the card in a clickable container and added an onclick handler:

<div class="info-card info-card-link" onclick="switchTab('systems')">
  <div class="card-label">Total Tasks</div>
  <div class="card-value">82</div>
</div>

Why this approach? We chose inline event binding over event delegation because:

  • The card is static (not dynamically generated)
  • Keeps the handler logic co-located with the markup for readability
  • Avoids adding a wrapper <button> element that would require additional CSS reset
  • The existing switchTab()` function is already global and tested

Step 3: Add Interactive Styling

Added CSS class info-card-link to signal interactivity and provide visual feedback:

.info-card-link {
  cursor: pointer;
  transition: all 0.2s ease;
}

.info-card-link:hover {
  background-color: rgba(52, 152, 219, 0.1);
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}

.info-card-link:active {
  transform: translateY(0);
}

Design rationale:

  • cursor: pointer signals clickability to users
  • Subtle background color change on hover (10% opacity blue) doesn't distract but confirms interactivity
  • Slight upward translation (2px) on hover creates tactile feedback
  • Enhanced shadow adds depth for the hover state
  • Active state removes translation to provide immediate click feedback

Deployment Process

S3 Upload

After editing, we uploaded the modified index.html back to S3:

aws s3 cp index.html s3://maintenance.jada.s3.amazonaws.com/index.html --profile jada --cache-control "max-age=3600"

We set a 1-hour cache-control header because this file changes infrequently and we want to avoid serving stale content for too long if we need to push updates.

CloudFront Cache Invalidation

S3 caching alone isn't sufficient; CloudFront sits between users and S3. We need to invalidate the cache so users get the fresh version immediately:

aws cloudfront create-invalidation \
  --distribution-id E1ABC2DEF3GHIJ \
  --paths "/index.html" \
  --profile jada

Where E1ABC2DEF3GHIJ is the CloudFront distribution ID for maintenance.sailjada.com

Why CloudFront invalidation matters: Without this step, users in various geographic regions would continue seeing the old cached version for up to 24 hours (the default TTL). Invalidation ensures deployment consistency across all users within seconds.

Testing & Verification

After deployment, we verified:

  • Navigated to maintenance.sailjada.com and confirmed the Total Tasks card rendered with updated styling
  • Hovered over the card to confirm background color and shadow changes
  • Clicked the card and confirmed the view switched to the Systems tab, displaying the task table
  • Checked browser DevTools to confirm CSS classes and onclick bindings were present

Key Decisions & Tradeoffs

  • Inline onclick vs. Event Delegation: We chose simplicity over scalability since this is a one-off card. If we were making multiple cards interactive, we'd refactor to use event delegation and a data attribute strategy.
  • CSS Class Over Inline Styles: Keeping styles in the stylesheet (rather than inline) maintains separation of concerns and allows theme changes without modifying HTML.
  • No State Management: The existing tab