From 2d37677338af81605b983f8c1d0d0af6c34020fc Mon Sep 17 00:00:00 2001 From: Anurag Singh Date: Sun, 7 Jun 2026 15:44:38 +0530 Subject: [PATCH] fix(#516): handle incomplete card grid rows gracefully on dashboard Closes #516 The dashboard layouts left the final card isolated on a new row when there were fewer cards than the available columns, causing excessive whitespace and an unfinished appearance. Three separate grids in student_dashboard.html had this problem; admin_dashboard.html had a latent version covered by the comment fix below. Root causes and fixes --------------------- 1. student_dashboard.html -- .stat-card (flex grid, 3 cards) Before: flex: 1 (no upper bound) Problem: a lone last card expanded to fill 100% of the row Fix: flex: 1 1 200px + max-width: calc(33.333% - 14px) The max-width mirrors the natural 1/3 column width so the card never grows wider than a full-row peer would be. 2. student_dashboard.html -- .dashboard-action (flex grid, 3 cards) Same root cause as above. Fix: flex: 1 1 250px + max-width: calc(33.333% - 14px) 3. student_dashboard.html -- .recent-achievements (CSS Grid) Before: grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)) Problem: auto-fill creates invisible placeholder columns; the last real card is forced to fill the remaining track width (potentially the whole row width). Fix: Changed to auto-fit, which collapses empty tracks so no phantom columns remain. Added max-width: 350px on each .recent-achievement card so a lone survivor can't stretch past a natural card width. 4. admin_dashboard.html -- .stat-card (Bootstrap col-md-3 grid) Bootstrap already enforces 25% column width at >=768 px, so a lone card cannot stretch beyond that boundary. Added an explanatory comment in the CSS and a max-width: 100% reset for mobile, where Bootstrap stacks cards to a single column and max-width should not constrain them. Mobile behaviour ---------------- @media (max-width: 768px) overrides reset max-width to 100% for all affected elements so narrow screens continue to render full-width cards as intended. --- templates/admin_dashboard.html | 10 ++++++++++ templates/student_dashboard.html | 31 ++++++++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/templates/admin_dashboard.html b/templates/admin_dashboard.html index eacf67e0..81b467b2 100644 --- a/templates/admin_dashboard.html +++ b/templates/admin_dashboard.html @@ -83,6 +83,11 @@ box-shadow: 0 2px 10px rgba(0,0,0,0.05); transition: transform 0.3s; border: none; + /* Prevent a lone card in an incomplete row from filling the + full Bootstrap column width and creating excessive whitespace. + Bootstrap col-md-3 gives each card 25% — we enforce that cap + so the card never grows beyond its natural column size. */ + max-width: 100%; /* Bootstrap already caps at col-md-3 = 25% */ } .stat-card:hover { @@ -191,6 +196,11 @@ .sidebar.active { margin-left: 0; } + /* On mobile the Bootstrap grid stacks to 1-column, + so each stat card naturally fills 100% — no cap needed. */ + .stat-card { + max-width: 100%; + } } diff --git a/templates/student_dashboard.html b/templates/student_dashboard.html index f8e5b496..08981ebe 100644 --- a/templates/student_dashboard.html +++ b/templates/student_dashboard.html @@ -21,7 +21,8 @@ } .stat-card { - flex: 1; + flex: 1 1 200px; /* grow/shrink but never below 200 px */ + max-width: calc(33.333% - 14px); /* max 1/3 width so lone card stays bounded */ min-width: 200px; background: rgba(255, 0, 0, 0.1); border-radius: 10px; @@ -79,7 +80,8 @@ } .dashboard-action { - flex: 1; + flex: 1 1 250px; /* grow/shrink but never below 250 px */ + max-width: calc(33.333% - 14px); /* prevent a lone card stretching to full width */ min-width: 250px; background: var(--glass-bg); border-radius: 15px; @@ -141,15 +143,27 @@ .recent-achievements { display: grid; - grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + /* auto-fit collapses empty phantom columns so the last card + does not stretch across the full row when the row is incomplete. + auto-fill would leave invisible tracks that force the card to + fill 100% width. */ + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + /* hard cap so a single surviving card never grows past one column width */ + max-width: 100%; gap: 20px; } + + /* When only 1 card survives on the last row, stop it from filling the grid */ + .recent-achievement { + max-width: 350px; + } .recent-achievement { background: var(--glass-bg); border-radius: 10px; padding: 15px; border: 1px solid var(--border-color); + max-width: 350px; /* duplicated from grid rule above for specificity safety */ } .achievement-tag { @@ -177,6 +191,17 @@ .dashboard-stats { flex-direction: column; } + /* On mobile remove the max-width caps so cards fill the full column */ + .stat-card, + .dashboard-action { + max-width: 100%; + } + .recent-achievements { + grid-template-columns: 1fr; + } + .recent-achievement { + max-width: 100%; + } } .toggle-container {