Modern alternative to the Canvas Dashboard Grades extension.
// ==UserScript==
// @name Canvas Dashboard Grades
// @namespace https://xela.codes
// @version 2025-01-07
// @description Modern alternative to the Canvas Dashboard Grades extension.
// @author Alex
// @match https://*.instructure.com/
// @icon https://lh3.googleusercontent.com/kA8gaN8ouFmWN-A224OZoB7mR_YpqQuWqeMUcuATLT1DBVecjfD5arRhIvl0rQ17-LoeVump_yWWVmjKKmjc1kQDKbE=s60
// @grant none
// @run-at document-end
// ==/UserScript==
(async function () {
"use strict";
const enrolledCourses = await fetch(
location.protocol +
"//" +
location.host +
location.pathname +
"api/v1/courses?include[]=total_scores&per_page=100&enrollment_state=active",
{
method: "GET",
credentials: "include",
headers: {
Accept: "application/json",
},
}
).then((res) => res.json());
function doGrades() {
enrolledCourses.forEach((course) => {
/** course card header element */
const courseCard = document.querySelector(
`.ic-DashboardCard a[href="/courses/${course.id}"]`
)?.parentElement,
hero = courseCard?.querySelector(".ic-DashboardCard__header_hero"),
/** course enrollment information (contains grade) */
enrollment = course.enrollments?.slice(-1)[0];
if (!courseCard || !hero || !enrollment)
return console.error(`Failed to add grade for course ${course.name}.`);
// use the course color for text
const color = hero.style?.backgroundColor || "inherit";
const title = document.createElement("span");
title.classList.add("cdg-grade");
title.style.color = color;
title.style.background = "white";
title.style.borderRadius = "99999px";
title.style.opacity = "1";
title.style.padding = "0.2rem 0.5rem";
title.style.fontSize = title.style.lineHeight = "14px";
title.style.display = "inline-block";
title.style.position = "absolute";
title.style.top = title.style.left = "0.35rem";
title.style.fontWeight = "bold";
title.innerText =
// the enrollment contains the current grade
enrollment.computed_current_score == null ? "N/A" : `${enrollment.computed_current_score}%`;
courseCard.appendChild(title);
// update the color if changed
const observer = new MutationObserver((list) => {
if (!title.parentElement) return;
list.forEach((mut) => {
if (mut.type == "attributes" && mut.attributeName == "style")
title.style.color = hero.style.backgroundColor || "inherit";
});
});
observer.observe(hero, { attributes: true });
});
}
doGrades();
// sometimes the page gets re-rendered, removing the grades
// so we check every so often to make sure they're still there
if (enrolledCourses?.length)
setInterval(() => {
if (!document.querySelector(".cdg-grade")) doGrades();
}, 66);
})();