From 4f690600fc4595c1501e31bf834b5e7b2cee7f7b Mon Sep 17 00:00:00 2001 From: Shivam Gupta <231542@kit.ac.in> Date: Mon, 15 Jun 2026 11:23:40 +0530 Subject: [PATCH 1/2] fix: add ARIA roles to leaderboard for screen reader accessibility --- frontend/js/leaderboard/render.js | 16 ++++++++++++++-- frontend/leaderboard.html | 29 +---------------------------- 2 files changed, 15 insertions(+), 30 deletions(-) diff --git a/frontend/js/leaderboard/render.js b/frontend/js/leaderboard/render.js index b601959c..55614f7f 100644 --- a/frontend/js/leaderboard/render.js +++ b/frontend/js/leaderboard/render.js @@ -22,6 +22,7 @@ document.addEventListener("click", (e) => { .querySelectorAll(".mobile-score.active") .forEach((el) => el.classList.remove("active")); }); + // Creates a rank change DOM element (safe — values are internally generated) function createRankChangeElement(rankChange) { if (!rankChange) return null; @@ -99,16 +100,19 @@ function renderLeaderboardRow(user, rank) { const row = document.createElement("div"); row.className = "leaderboard-row"; + row.setAttribute("role", "row"); // Rank (numeric — safe) const rankDiv = document.createElement("div"); rankDiv.className = "rank"; rankDiv.textContent = rank; + rankDiv.setAttribute("role", "cell"); row.appendChild(rankDiv); // Name cell — tag is safe DOM element, name is user-controlled (textContent) const nameDiv = document.createElement("div"); nameDiv.className = "name-cell"; + nameDiv.setAttribute("role", "cell"); if (rankTagEl) { nameDiv.appendChild(rankTagEl); } @@ -127,6 +131,7 @@ function renderLeaderboardRow(user, rank) { // Username with link and external icon — id is user-controlled (textContent) const usernameDiv = document.createElement("div"); usernameDiv.className = "username"; + usernameDiv.setAttribute("role", "cell"); const link = document.createElement("a"); link.href = `https://leetcode.com/u/${user.id}`; link.target = "_blank"; @@ -141,29 +146,34 @@ function renderLeaderboardRow(user, rank) { const easyDiv = document.createElement("div"); easyDiv.className = "easy"; easyDiv.textContent = user.data.easySolved; + easyDiv.setAttribute("role", "cell"); row.appendChild(easyDiv); // Medium (numeric — safe) const mediumDiv = document.createElement("div"); mediumDiv.className = "medium"; mediumDiv.textContent = user.data.mediumSolved; + mediumDiv.setAttribute("role", "cell"); row.appendChild(mediumDiv); // Hard (numeric — safe) const hardDiv = document.createElement("div"); hardDiv.className = "hard"; hardDiv.textContent = user.data.hardSolved; + hardDiv.setAttribute("role", "cell"); row.appendChild(hardDiv); // Total (numeric — safe) const totalDiv = document.createElement("div"); totalDiv.className = "total"; totalDiv.textContent = user.data.totalSolved; + totalDiv.setAttribute("role", "cell"); row.appendChild(totalDiv); // Score with tooltip — all content is numeric or hardcoded labels const scoreDiv = document.createElement("div"); scoreDiv.className = "mobile-score tooltip-score"; + scoreDiv.setAttribute("role", "cell"); const scoreSpan = document.createElement("span"); scoreSpan.textContent = user.score; @@ -281,11 +291,13 @@ function renderMobileCard(user, rank) { }; mobileStats.appendChild(makeStat(user.data.easySolved, "easy", "Easy")); - mobileStats.appendChild(makeStat(user.data.mediumSolved, "medium", "Medium")); + mobileStats.appendChild( + makeStat(user.data.mediumSolved, "medium", "Medium"), + ); mobileStats.appendChild(makeStat(user.data.hardSolved, "hard", "Hard")); mobileStats.appendChild(makeStat(user.data.totalSolved, "total", "Total")); card.appendChild(mobileStats); return card; -} +} \ No newline at end of file diff --git a/frontend/leaderboard.html b/frontend/leaderboard.html index 932e40f6..ba014ff1 100644 --- a/frontend/leaderboard.html +++ b/frontend/leaderboard.html @@ -121,34 +121,7 @@