Add project housekeeping files and sync manifest version to 0.1.0. Also stages previously untracked chat summaries and score-ratio docs. Co-authored-by: Cursor <cursoragent@cursor.com>
15 KiB
Score Ratio Badge Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Add a temperature-coded badge to each post showing the score-per-comment ratio, using Font Awesome's fire icon and an HSL color scale from blue (cold) to red (hot).
Architecture: New ratio.js IIFE module on window.RedditTweaks.ratioTweak with the standard tweak interface. Score and comment count are parsed from the DOM per post. HSL hue is computed from the ratio and set as CSS custom properties on each badge element. CSS handles light/dark mode lightness via selectors. Font Awesome solid subset is bundled with scoped CSS to prevent global pollution.
Tech Stack: Vanilla JS (IIFE modules on window.RedditTweaks), plain CSS with custom properties, Font Awesome 6 Free (solid subset), Firefox WebExtension manifest v2.
Global Constraints
- old.reddit.com only
- No build tools, no frameworks, no npm runtime dependencies
- Modules use IIFE pattern and expose API on
window.RedditTweaks - Imports at top of file, no inline imports
- Score ratio requires
badgeLayoutorcardLayoutto be enabled (accepted dependency) - In
main.js, badges must always process before ratio (hard call-order requirement) - FA CSS must be scoped under
.rt-badge--ratio— no global FA selectors - Design spec:
docs/superpowers/specs/2026-08-27-vote-ratio-badge-design.md
Task 1: Settings infrastructure + popup toggle
Add the scoreRatio setting and wire a toggle in the popup's Card Layout section.
Files:
- Modify:
lib/settings.js - Modify:
popup/popup.html - Modify:
popup/popup.js
Interfaces:
-
Produces:
settings.scoreRatio(boolean, defaulttrue) -
Step 1: Add
scoreRatioto DEFAULTS inlib/settings.js
In lib/settings.js, add scoreRatio: true to the DEFAULTS object, after cardGap: 16:
cardGap: 16,
scoreRatio: true,
- Step 2: Add toggle to
popup/popup.html
In popup/popup.html, inside the Card Layout section, add the score ratio toggle after the "Enable card layout" toggle (after line 40: </label>):
<label class="toggle-row">
<span>Score ratio badge</span>
<input type="checkbox" id="scoreRatio">
</label>
- Step 3: Wire toggle in
popup/popup.js
In popup/popup.js, add to populateControls(settings), after the cardLayout checkbox line:
document.getElementById("scoreRatio").checked = settings.scoreRatio;
In saveFromControls(), add to the settings object, after the cardLayout line:
scoreRatio: document.getElementById("scoreRatio").checked,
In init(), add a change listener, after the cardLayout listener:
document.getElementById("scoreRatio").addEventListener("change", saveFromControls);
- Step 4: Verify toggle works
Load the extension in Firefox (about:debugging → Load Temporary Add-on). Open popup:
-
"Score ratio badge" checkbox appears in the Card Layout section, checked by default
-
Unchecking and reopening popup: stays unchecked (persisted)
-
Re-checking: persists
-
No console errors
-
Step 5: Commit
git add lib/settings.js popup/popup.html popup/popup.js
git commit -m "feat: add scoreRatio setting and popup toggle"
Task 2: Font Awesome solid subset + ratio badge CSS
Download the FA solid woff2 font, create scoped FA CSS and the ratio badge stylesheet, and register both in the manifest.
Files:
- Create:
content/fonts/fontawesome/fa-solid-900.woff2(downloaded) - Create:
content/styles/fontawesome-scoped.css - Create:
content/styles/ratio.css - Modify:
manifest.json
Interfaces:
-
Produces:
@font-facefor "Font Awesome 6 Free" (solid, weight 900) -
Produces:
.rt-badge--ratio .fa-solidand.rt-badge--ratio .fa-fire::beforeselectors -
Produces:
.rt-badge--ratioand.rt-badge--ratio-neutralCSS classes consuming--rt-ratio-hueand--rt-ratio-satcustom properties -
Step 1: Download FA 6.7.2 solid woff2 (pinned version)
mkdir -p content/fonts/fontawesome
curl -f -L -o content/fonts/fontawesome/fa-solid-900.woff2 \
"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/webfonts/fa-solid-900.woff2"
Verify integrity — must be 158KB with sha256 aa75998623a391e61c6901794ace832e3ecdd288b56d608f21bea0411acc0b8e:
ls -lh content/fonts/fontawesome/fa-solid-900.woff2
shasum -a 256 content/fonts/fontawesome/fa-solid-900.woff2
If the hash doesn't match or the download fails (curl -f flag fails on HTTP errors), stop and investigate — the CDN URL may have changed.
- Step 2: Create
content/styles/fontawesome-scoped.css
This defines the @font-face and scopes all FA selectors under .rt-badge--ratio to prevent global CSS pollution:
@font-face {
font-family: "Font Awesome 6 Free";
font-style: normal;
font-weight: 900;
font-display: block;
src: url("../fonts/fontawesome/fa-solid-900.woff2") format("woff2");
}
.rt-badge--ratio .fa-solid,
.rt-badge--ratio .fas {
font-family: "Font Awesome 6 Free";
font-weight: 900;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
display: inline-block;
font-style: normal;
font-variant: normal;
line-height: 1;
text-rendering: auto;
}
.rt-badge--ratio .fa-fire::before {
content: "\f06d";
}
- Step 3: Create
content/styles/ratio.css
This depends on .rt-badge base styles from badges.css (always loaded via manifest). It uses CSS custom properties --rt-ratio-hue and --rt-ratio-sat set inline by JS to derive all colors. Dark mode changes lightness via the .rt-dark selector — no JS DOM walk needed.
/* --- Score ratio badge: temperature-colored --- */
.rt-badge--ratio {
color: hsl(var(--rt-ratio-hue, 0), var(--rt-ratio-sat, 80%), 45%);
background: hsla(var(--rt-ratio-hue, 0), var(--rt-ratio-sat, 80%), 45%, 0.1);
border-color: hsla(var(--rt-ratio-hue, 0), var(--rt-ratio-sat, 80%), 45%, 0.25);
cursor: default;
gap: 3px;
display: inline-flex;
align-items: center;
}
.rt-badge--ratio .fa-solid {
font-size: 10px;
}
/* --- Dark mode: bump lightness from 45% to 55% --- */
html.rt-dark .rt-badge--ratio,
.rt-dark .rt-badge--ratio {
color: hsl(var(--rt-ratio-hue, 0), var(--rt-ratio-sat, 80%), 55%);
background: hsla(var(--rt-ratio-hue, 0), var(--rt-ratio-sat, 80%), 55%, 0.1);
border-color: hsla(var(--rt-ratio-hue, 0), var(--rt-ratio-sat, 80%), 55%, 0.25);
}
/* --- Neutral state for incalculable ratios (uses rt-badge base only, not rt-badge--ratio) --- */
.rt-badge--ratio-neutral {
color: #737373;
background: rgba(115, 115, 115, 0.1);
border-color: rgba(115, 115, 115, 0.25);
cursor: default;
}
html.rt-dark .rt-badge--ratio-neutral,
.rt-dark .rt-badge--ratio-neutral {
color: #a3a3a3;
background: rgba(163, 163, 163, 0.1);
border-color: rgba(163, 163, 163, 0.25);
}
/* --- Disable hover effect on non-interactive ratio badges --- */
.rt-badge--ratio:hover,
.rt-badge--ratio-neutral:hover {
opacity: 1;
}
- Step 4: Add CSS files to
manifest.json
In the content_scripts[0].css array, add both new CSS files after fonts.css:
"css": [
"content/styles/badges.css",
"content/styles/cards.css",
"content/styles/fonts.css",
"content/styles/fontawesome-scoped.css",
"content/styles/ratio.css"
],
- Step 5: Verify CSS loads without errors
Reload extension. Navigate to old.reddit.com. Open the browser console:
-
No CSS parse errors
-
No 404 errors for the woff2 font file
-
No visible changes yet (no JS creating badge elements)
-
Step 6: Commit
git add content/fonts/fontawesome/fa-solid-900.woff2 content/styles/fontawesome-scoped.css content/styles/ratio.css manifest.json
git commit -m "feat: add FA solid subset and score ratio badge CSS"
Task 3: Core ratio module + main.js integration
Create ratio.js with score parsing, comment count parsing, ratio calculation, HSL hue computation, and badge DOM injection. Wire it into main.js with enforced call order (after badges) and add to the manifest.
Files:
- Create:
content/tweaks/ratio.js - Modify:
content/main.js - Modify:
manifest.json
Interfaces:
-
Consumes:
settings.scoreRatio(boolean, from Task 1) -
Consumes:
.rt-badgebase styles frombadges.css -
Consumes:
.rt-badge--ratiostyles fromratio.css(Task 2) -
Consumes: FA
fa-solid fa-fireclasses fromfontawesome-scoped.css(Task 2) -
Produces:
window.RedditTweaks.ratioTweak.apply(settings) -
Produces:
window.RedditTweaks.ratioTweak.processNewPosts(settings) -
Produces:
window.RedditTweaks.ratioTweak.updateSettings(settings)(no-op) -
Step 1: Create
content/tweaks/ratio.js
(function () {
"use strict";
var SCALE_MAX = 100;
function parseScore(post) {
var scoreEl = post.querySelector(".midcol .score.unvoted");
if (!scoreEl) return null;
var raw = scoreEl.getAttribute("title");
if (raw && raw.indexOf("\u2022") === -1) {
var parsed = parseInt(raw, 10);
if (!isNaN(parsed)) return parsed;
}
var text = scoreEl.textContent.trim();
if (!text || text.indexOf("\u2022") !== -1) return null;
var num = parseInt(text, 10);
if (!isNaN(num)) return num;
var match = text.match(/^(-?[\d.]+)\s*([km])/i);
if (match) {
var val = parseFloat(match[1]);
var suffix = match[2].toLowerCase();
if (suffix === "k") return Math.round(val * 1000);
if (suffix === "m") return Math.round(val * 1000000);
}
return null;
}
function parseCommentCount(post) {
var commentsLink = post.querySelector(".flat-list .comments");
if (!commentsLink) return null;
var text = commentsLink.textContent.trim();
if (text === "comment") return 0;
var match = text.match(/^(\d+)/);
return match ? parseInt(match[1], 10) : null;
}
function computeRatio(score, comments) {
if (score === null || comments === null || comments === 0) return null;
return score / comments;
}
function ratioToHue(ratio) {
var clamped = Math.max(0, Math.min(ratio, SCALE_MAX));
return Math.round(240 - (clamped / SCALE_MAX) * 240);
}
function processPost(post) {
if (post.hasAttribute("data-rt-ratio-processed")) return;
post.setAttribute("data-rt-ratio-processed", "true");
var score = parseScore(post);
var comments = parseCommentCount(post);
var ratio = computeRatio(score, comments);
var badge = document.createElement("span");
if (ratio !== null) {
badge.className = "rt-badge rt-badge--ratio";
badge.style.setProperty("--rt-ratio-hue", String(ratioToHue(ratio)));
badge.style.setProperty("--rt-ratio-sat", "80%");
var text = document.createTextNode(ratio.toFixed(1) + " ");
var icon = document.createElement("i");
icon.className = "fa-solid fa-fire";
badge.appendChild(text);
badge.appendChild(icon);
} else {
badge.className = "rt-badge rt-badge--ratio-neutral";
badge.textContent = "\u2014";
}
var container = post.querySelector(".reddit-tweaks-badges");
if (container) {
container.appendChild(badge);
} else {
var entry = post.querySelector(".entry");
if (!entry) return;
var tagline = entry.querySelector(".tagline");
if (tagline && tagline.nextSibling) {
entry.insertBefore(badge, tagline.nextSibling);
} else if (tagline) {
tagline.parentNode.appendChild(badge);
} else {
entry.insertBefore(badge, entry.firstChild);
}
}
}
function processPosts() {
var posts = document.querySelectorAll(
"div.thing.link:not([data-rt-ratio-processed])"
);
for (var i = 0; i < posts.length; i++) {
processPost(posts[i]);
}
}
function apply() {
processPosts();
}
function updateSettings() {
// No-op: dark mode lightness is handled via CSS selectors on .rt-dark
}
window.RedditTweaks = window.RedditTweaks || {};
window.RedditTweaks.ratioTweak = {
apply: apply,
processNewPosts: processPosts,
updateSettings: updateSettings,
};
})();
- Step 2: Add
ratio.jstomanifest.json
In the content_scripts[0].js array, add content/tweaks/ratio.js after cards.js and before main.js:
"js": [
"lib/settings.js",
"content/tweaks/badges.js",
"content/tweaks/color-picker.js",
"content/tweaks/color-popover.js",
"content/tweaks/cards.js",
"content/tweaks/ratio.js",
"content/main.js"
],
- Step 3: Wire ratio into
main.jsinit
In content/main.js, inside the init() function, add the ratio block after the cardLayout block (after line 76: }) and before the observer start (line 78: if (settings.badgeLayout || settings.cardLayout)):
if (settings.scoreRatio) {
window.RedditTweaks.ratioTweak.apply(settings);
}
- Step 4: Wire ratio into post observer
In content/main.js, inside the startPostObserver function, in the if (hasNewPosts) callback, add ratio processing after the cardTweak.processNewPosts call (after line 52: }):
if (settings.scoreRatio) {
window.RedditTweaks.ratioTweak.processNewPosts(updated);
}
- Step 5: Wire ratio into dark mode observer
In content/main.js, inside the startDarkModeObserver function, in the if (updated.darkMode === "auto") callback, add ratio update after the cardTweak.updateSettings call (after line 13: }):
if (updated.scoreRatio) {
window.RedditTweaks.ratioTweak.updateSettings(updated);
}
- Step 6: Add
scoreRatioto storage change reload triggers
In content/main.js, in the browser.storage.onChanged.addListener callback, add scoreRatio to the reload condition (inside the if that checks for changes requiring reload, after the cardLayout check):
(changes.scoreRatio && changes.scoreRatio.newValue !== changes.scoreRatio.oldValue)
- Step 7: Wire ratio into settings update listener
In content/main.js, in the browser.storage.onChanged.addListener callback, in the .then(function (updated)) block that runs when no reload is needed, add ratio update after the cardTweak.updateSettings call:
if (updated.scoreRatio) {
window.RedditTweaks.ratioTweak.updateSettings(updated);
}
- Step 8: Verify end-to-end
Reload extension. Navigate to old.reddit.com. Verify:
-
Each post shows a colored ratio badge after the subreddit/comments badges
-
Badge text is a number with one decimal followed by a fire icon (e.g., "28.7 🔥")
-
Posts with high ratios (100+) show red badges
-
Posts with low ratios show blue badges
-
Posts with mid-range ratios show green/yellow badges
-
Posts with hidden scores (• in the vote column) show "—" in gray
-
Posts with 0 comments show "—" in gray
-
Dark mode (toggle RES night mode or force dark in popup): badge colors shift slightly brighter, fire icon follows
-
Infinite scroll (RES Never Ending Reddit): new posts get ratio badges
-
Disabling "Score ratio badge" in popup and reloading: no ratio badges appear
-
Re-enabling: badges return
-
No console errors
-
Step 9: Commit
git add content/tweaks/ratio.js content/main.js manifest.json
git commit -m "feat: add score ratio badge with temperature color scale"