From 2088250bf250e2708b05e695cd41f54a2bf242ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oriol=20G=C3=B3mez?= Date: Sun, 8 Feb 2026 21:03:21 +0100 Subject: [PATCH 1/8] a11y ftw --- ScreenReader.html | 241 +++++++++++++++++++++++----------------------- main.js | 4 + 2 files changed, 125 insertions(+), 120 deletions(-) diff --git a/ScreenReader.html b/ScreenReader.html index a49acafe..4d324a2b 100644 --- a/ScreenReader.html +++ b/ScreenReader.html @@ -110,9 +110,9 @@
Screen Reader Information
-
+
+
@@ -134,7 +134,7 @@
Screen Reader Information
-
+
@@ -142,9 +142,9 @@
Screen Reader Information
Other Goodies
-
+
+

Automatically gain helium equal to the amount you earned on your best run @@ -154,9 +154,9 @@
Screen Reader Information
-
+
+
Get one Heirloom at the chances above, based on highest zone @@ -177,7 +177,7 @@
Screen Reader Information

Food

- 0 / +
@@ -189,7 +189,7 @@

Food

- +0/sec +
@@ -198,7 +198,7 @@

Food

@@ -221,7 +221,7 @@

Wood

@@ -248,7 +248,7 @@

Science

- +0/sec +
@@ -261,14 +261,14 @@

Science


0
- +
- +
- 0 / 10 +
@@ -305,7 +305,7 @@

???

- +
@@ -358,7 +358,7 @@

???

-
+
A green shimmer erupts then disappears, and you hit the ground. You look pretty hungry...
@@ -376,7 +376,7 @@

End of Log

- +

@@ -389,41 +389,41 @@

-
AutoStructure
-
+ +
-
AutoStorage
+
@@ -466,14 +466,14 @@

+
-
Fire
+
@@ -488,15 +488,15 @@

Upgrades
(Research first)
-
AutoGold
-
+ +
-
AutoPrestige
+
-
AutoUpgrade
+
@@ -513,8 +513,8 @@

Upgrades
(Research first)
 
-
AutoEquip
-
+ +
@@ -523,7 +523,7 @@

Upgrades
(Research first)
Your next mastery costs 0.
@@ -700,10 +700,10 @@

Upgrades
(Research first)

Trimp Battle Stats

-
+
DMG
-
@@ -1166,22 +1166,22 @@

-
+
-
Close
 
Trimps Info
+  
@@ -1205,7 +1205,7 @@

Any Heirlooms in the "Temporary" section will be recycled for Nullifium on portal.
@@ -1228,8 +1228,8 @@

Equipped Staff

@@ -1319,7 +1319,7 @@

Temporary ADVISOR



@@ -1442,34 +1442,35 @@
Game Overview
+
- - - - - - - - - @@ -1482,21 +1483,21 @@
Game Overview

diff --git a/main.js b/main.js index 852eda92..8bf0248a 100644 --- a/main.js +++ b/main.js @@ -13176,6 +13176,10 @@ function nextWorld() { Fluffy.rewardExp(); game.global.world++; document.getElementById('worldNumber').innerHTML = game.global.world; + if (usingScreenReader) { + var zoneAnnounce = document.getElementById('srZoneAnnounce'); + if (zoneAnnounce) zoneAnnounce.textContent = 'Zone ' + game.global.world; + } game.global.mapBonus = 0; const mapBonusElem = document.getElementById('mapBonus'); if (mapBonusElem.innerHTML !== '') document.getElementById('mapBonus').innerHTML = ''; From b61a923e2d122ee6da185da3beb33f75243bf26e Mon Sep 17 00:00:00 2001 From: oriol Date: Sun, 8 Feb 2026 20:51:40 +0000 Subject: [PATCH 2/8] a11y --- ScreenReader.html | 171 +++++++++++++++++++++------------------------- config.js | 3 +- main.js | 55 ++++++++++++--- updates.js | 15 +++- 4 files changed, 140 insertions(+), 104 deletions(-) diff --git a/ScreenReader.html b/ScreenReader.html index 4d324a2b..e7fa218f 100644 --- a/ScreenReader.html +++ b/ScreenReader.html @@ -176,8 +176,7 @@
Screen Reader Information
-

Food

- +
@@ -197,8 +196,7 @@

Food

+ Save +
Export
+
Import
+
Stats
+
Achieves
+
Settings
+ + V | What's New +     
"; + if (usingScreenReader) { + var resName = what.charAt(0).toUpperCase() + what.slice(1); + screenReaderAssert(resName + " per second: " + prettify(currentCalc)); + return; + } game.global.lockTooltip = false; tooltip('confirm', null, 'update', textString, "getPsString('" + what + "')", what.charAt(0).toUpperCase() + what.substr(1, what.length) + " Per Second", "Refresh", true); } @@ -3559,6 +3564,10 @@ function getMaxResources(what) { textString += "Heirloom (Shield)+ " + hatAmt + "" + prettify(currentCalc) + ""; } textString += ""; + if (usingScreenReader) { + screenReaderAssert("Max " + what + ": " + prettify(currentCalc) + ". " + structure + ": " + structureObj.owned); + return; + } game.global.lockTooltip = false; tooltip('confirm', null, 'update', textString, "getMaxResources('" + what + "')", "Max " + what, "Refresh", true); } @@ -4806,6 +4815,7 @@ function resetGame(keepPortal, resetting) { if (game.global.autoUpgradesAvailable) document.getElementById("autoUpgradeBtn").style.display = "block"; if (game.global.autoStorageAvailable) { document.getElementById("autoStorageBtn").style.display = "block"; + ensureSRInfoButton("autoStorageBtn"); toggleAutoStorage(true); } game.portal.Coordinated.currentSend = 1; @@ -5180,7 +5190,7 @@ function postMessages(){ var log = document.getElementById("log"); var needsScroll = ((log.scrollTop + 10) > (log.scrollHeight - log.clientHeight)); var pendingMessages = pendingLogs.all.join(''); - log.innerHTML += pendingMessages; + log.insertAdjacentHTML('beforeend', pendingMessages); pendingLogs.all = []; for (var item in pendingLogs){ if (item == "all" || item == "RAF") continue; @@ -5358,6 +5368,9 @@ function numTab(what, p) { const thisTab = document.getElementById(tabType + x); if (what === x) thisTab.className = thisTab.className.replace('tabNotSelected', 'tabSelected'); else thisTab.className = thisTab.className.replace('tabSelected', 'tabNotSelected'); + // Sync radio button checked state for screen reader layout + var radio = thisTab.querySelector('input[type="radio"]'); + if (radio) radio.checked = (what === x); if (x === 5) continue; switch (x) { case 1: From 74403047106d00c358b6858d08443c05504173d7 Mon Sep 17 00:00:00 2001 From: oriol Date: Mon, 9 Feb 2026 06:29:59 +0000 Subject: [PATCH 3/8] lots of a11y fixes --- ScreenReader.html | 29 +++--- main.js | 231 ++++++++++++++++++++++++++++++++++++++-------- updates.js | 59 ++++++++++++ 3 files changed, 264 insertions(+), 55 deletions(-) diff --git a/ScreenReader.html b/ScreenReader.html index e7fa218f..e24ea9f3 100644 --- a/ScreenReader.html +++ b/ScreenReader.html @@ -134,7 +134,7 @@
Screen Reader Information
- +
@@ -142,7 +142,7 @@
Screen Reader Information
Other Goodies
-
@@ -154,7 +154,7 @@
Screen Reader Information
-
@@ -278,7 +278,7 @@
Screen Reader Information
-

???

+ ???
@@ -348,7 +348,8 @@

???

-
+
+
A green shimmer erupts then disappears, and you hit the ground. You look pretty hungry...
@@ -369,7 +370,7 @@

End of Log

-

+
@@ -1429,28 +1430,28 @@
Game Overview
- - - - - - - -
+ Save +
Export
+
Import
+
Stats
+
Achieves
+
Settings
+ + V | What's New diff --git a/main.js b/main.js index b466cc0f..70079bb2 100644 --- a/main.js +++ b/main.js @@ -5525,10 +5525,22 @@ function buyUpgrade(what, confirmed, noTip, heldCtrl) { } var upgradesHereElem = document.getElementById("upgradesHere") var removeElem = document.getElementById(what); - if (removeElem) upgradesHereElem.removeChild(removeElem); + if (removeElem) { + if (usingScreenReader && removeElem.parentElement && removeElem.parentElement.tagName === 'H1') { + removeElem.parentElement.remove(); + } else { + upgradesHereElem.removeChild(removeElem); + } + } if (usingScreenReader){ var tooltipElem = document.getElementById('srTooltip' + what); - if (tooltipElem) upgradesHereElem.removeChild(tooltipElem); + if (tooltipElem) { + if (tooltipElem.parentElement && tooltipElem.parentElement.tagName === 'H1') { + tooltipElem.parentElement.remove(); + } else { + upgradesHereElem.removeChild(tooltipElem); + } + } } if (!noTip) tooltip("hide"); return true; @@ -6806,6 +6818,7 @@ function setVoidBuffTooltip(){ var stackCount = ""; var elem = document.getElementById('voidBuff'); elem.innerHTML = makeIconEffectHTML(buff.title, buff.text, buff.icon, "badBadge voidBadge") + screenReaderAssert(buff.title + " activated"); } var heirloomsShown = false; @@ -10544,6 +10557,90 @@ function drawGrid(maps) { else if (!maps && game.global.spireActive) className = 'spire'; else if (maps && map.location === 'Darkness') className = 'blackMap'; + // Screenreader: render world grid as an accessible table + if (usingScreenReader && !maps) { + let tableHTML = ``; + let counter = 0; + for (let i = 0; i < rows; i++) { + const start = i * cols + 1; + const end = start + cols - 1; + tableHTML += ``; + for (let x = 0; x < cols; x++) { + const cell = game.global.gridArray[counter]; + const id = `cell${counter}`; + let cellClasses = ['battleCell', 'cellColorNotBeaten']; + let srExtra = ''; + + if (cell.u2Mutation && cell.u2Mutation.length) { + for (let y = 0; y < cell.u2Mutation.length; y++) cellClasses.push(cell.u2Mutation[y]); + cellClasses.push('mutatedCell'); + } else if (cell.mutation) cellClasses.push(cell.mutation); + if (cell.vm) cellClasses.push(cell.vm); + if (cell.empowerment) { + cellClasses.push(`empoweredCell${cell.empowerment}`); + srExtra += ` Token of ${cell.empowerment}`; + } else if (checkIfSpireWorld() && game.global.spireActive) cellClasses.push('spireCell'); + if (cell.special === 'easterEgg') { + game.global.eggLoc = counter; + cellClasses.push('eggCell'); + srExtra += ' Egg'; + } + // Extract loot/item label from cell.text HTML (e.g. title='Metal') + if (cell.text) { + let labelMatch = cell.text.match(/title=["']([^"']+)["']/); + if (labelMatch) srExtra += ' ' + labelMatch[1]; + } + + tableHTML += ``; + counter++; + } + tableHTML += ''; + } + tableHTML += '
${start}\u2013${end}${cell.level}${srExtra}
'; + grid.className = ''; + grid.innerHTML = tableHTML; + const eggCell = document.querySelector('.eggCell'); + if (eggCell) eggCell.addEventListener('click', easterEggClicked); + return; + } + + // Screenreader: render map grid as an accessible table + if (usingScreenReader && maps) { + const size = game.global.mapGridArray.length; + let tableHTML = ``; + let counter = 0; + for (let i = 0; i < rows; i++) { + if (counter >= size) break; + const start = i * cols + 1; + const end = Math.min(start + cols - 1, size); + tableHTML += ``; + for (let x = 0; x < cols; x++) { + if (counter >= size) break; + const cell = game.global.mapGridArray[counter]; + const id = `mapCell${counter}`; + let cellClasses = ['battleCell', 'cellColorNotBeaten']; + let srExtra = ''; + + if (cell.name === 'Pumpkimp') { cellClasses.push('mapPumpkimp'); srExtra += ' Pumpkimp'; } + if (map.location === 'Void') cellClasses.push('voidCell'); + if (cell.vm) cellClasses.push(cell.vm); + // Extract loot/item label from cell.text HTML (e.g. title='Metal') + if (cell.text) { + let labelMatch = cell.text.match(/title=["']([^"']+)["']/); + if (labelMatch) srExtra += ' ' + labelMatch[1]; + } + + tableHTML += ``; + counter++; + } + tableHTML += ''; + } + tableHTML += '
${start}\u2013${end}${cell.level}${srExtra}
'; + grid.className = ''; + grid.innerHTML = tableHTML; + return; + } + const idText = maps ? 'mapCell' : 'cell'; let size = maps ? game.global.mapGridArray.length : 0; let counter = 0; @@ -10593,10 +10690,10 @@ function drawGrid(maps) { } } - html += `
  • ${innerHTML} @@ -10713,6 +10810,7 @@ function recycleMap(map, fromMass, killVoid, noRefund) { } var loc = "mapsHere"; if (killVoid){ + if (game.global.voidBuff) screenReaderAssert(voidBuffConfig[game.global.voidBuff].title + " ended"); game.global.voidBuff = ""; document.getElementById("voidBuff").innerHTML = ""; } @@ -10844,6 +10942,7 @@ function mapsSwitch(updateOnly, fromRecycle) { game.global.fighting = false; game.global.switchToMaps = false; game.global.switchToWorld = false; + if (game.global.voidBuff) screenReaderAssert(voidBuffConfig[game.global.voidBuff].title + " ended"); game.global.voidBuff = ""; if (game.global.preMapsActive) { game.global.mapsActive = false; @@ -16645,7 +16744,11 @@ function updateAntiStacks() { const s = game.global.antiStacks === 1 ? '' : 's'; elemText = makeIconEffectHTML("Anticipation", `Your Trimps are dealing ${number}% extra damage for taking ${game.global.antiStacks} second${s} to ${verb}.`, "icon-target2", "antiBadge", false, game.global.antiStacks) } - if (elem && elem.innerHTML != elemText) elem.innerHTML = elemText; + if (elem && elem.innerHTML != elemText) { + if (elemText && !elem.innerHTML) screenReaderAssert("Anticipation building"); + else if (!elemText && elem.innerHTML) screenReaderAssert("Anticipation consumed"); + elem.innerHTML = elemText; + } } function updateTitimp() { @@ -16660,7 +16763,11 @@ function updateTitimp() { } const elem = document.getElementById('titimpBuff'); - if (elem && elem.innerHTML != message) elem.innerHTML = message; + if (elem && elem.innerHTML != message) { + if (message && !elem.innerHTML) screenReaderAssert("Titimp activated, double damage"); + else if (!message && elem.innerHTML) screenReaderAssert("Titimp expired"); + elem.innerHTML = message; + } } function updateNomStacks(number) { @@ -17078,21 +17185,32 @@ function updateImports(which) { var badGuy = game.badGuys[item]; var elem = (badGuy.location == "World") ? world : maps; count++; - var row = elem.insertRow(); - var toRun = (which == 1) ? 'addToBundle' : 'selectImp'; - toRun += '("' + item + '")'; - if (game.unlocks.imps[item]){ - row.className = 'importOwned'; + if (usingScreenReader && which == 0) { + var row = elem.insertRow(); + var cell = row.insertCell(); + if (game.unlocks.imps[item]) { + cell.innerHTML = item + " - " + badGuy.dropDesc + " (Owned)"; + } else { + var radioId = "importRadio_" + item; + cell.innerHTML = ""; + } + } else { + var row = elem.insertRow(); + var toRun = (which == 1) ? 'addToBundle' : 'selectImp'; + toRun += '("' + item + '")'; + if (game.unlocks.imps[item]){ + row.className = 'importOwned'; + } + else + row.setAttribute('onclick', toRun); + row.id = (which == 1) ? item + "1" : item; + var name = row.insertCell(); + name.className = "importPreviewName"; + name.innerHTML = item; + var loot = row.insertCell(); + loot.className = "importPreviewLoot"; + loot.innerHTML = badGuy.dropDesc; } - else - row.setAttribute('onclick', toRun); - row.id = (which == 1) ? item + "1" : item; - var name = row.insertCell(); - name.className = "importPreviewName"; - name.innerHTML = item; - var loot = row.insertCell(); - loot.className = "importPreviewLoot"; - loot.innerHTML = badGuy.dropDesc; } } @@ -17438,35 +17556,46 @@ function displaySingleRunBonuses(){ document.getElementById('singleRunBonuses').style.marginTop = (anyPortals) ? "0" : "-2.5%"; var btnClass; var btnText; + var disabled = false; if (bonus.owned){ btnClass = 'boneBtnStateOff'; btnText = 'Active!'; + disabled = true; } else { if (item == "heliumy" && game.global.runningChallengeSquared){ btnClass = 'boneBtnStateOff'; - btnText = "Disabled on C" + ((game.global.universe == 1) ? 2 : 3) + ""; + btnText = "Disabled on C" + ((game.global.universe == 1) ? 2 : 3); + disabled = true; } else if (item == "quickTrimps" && (game.global.challengeActive == "Trapper" || game.global.challengeActive == "Trappapalooza")){ btnClass = 'boneBtnStateOff'; btnText = "Disabled on " + game.global.challengeActive; + disabled = true; } else{ - if (game.global.b < bonus.cost) - btnClass = 'boneBtnStateOff' - else + if (game.global.b < bonus.cost){ + btnClass = 'boneBtnStateOff'; + disabled = true; + } + else btnClass = 'boneBtnStateOn'; btnText = bonus.name + " (" + bonus.cost + " bones)"; } } - html += "
    " + btnText + "
    "; + if (usingScreenReader) + html += " onclick='purchaseSingleRunBonus(\"" + item + "\")'>" + btnText + ", " + bonus.text + ""; + else + html += " onclick='tooltip(\"Confirm Purchase\", null, \"update\", \"" + confText + "\", \"purchaseSingleRunBonus('" + item + "')\", 20)'>" + btnText + ""; } else - html += ">" + btnText + ""; - html += bonus.text; + html += ">" + btnText + (usingScreenReader ? ", " + bonus.text : "") + ""; + if (!usingScreenReader) html += bonus.text; html += ""; } document.getElementById("singleRunBonuses").innerHTML = html; @@ -17480,34 +17609,44 @@ function displayPermaBoneBonuses(){ var btnClass; var btnText; var desc; + var disabled = false; if (item == "voidMaps" && game.global.totalPortals < 1){ btnClass = 'boneBtnStateOff'; - btnText = ''; - desc = "Locked until your first Portal!" + btnText = usingScreenReader ? 'Locked' : ''; + desc = "Locked until your first Portal!"; + disabled = true; } else if (bonus.owned == 10){ btnClass = 'boneBtnStateOff'; btnText = bonus.name + ' (10/10)'; desc = bonus.text; + disabled = true; } else { var cost = getNextPermaBonePrice(item); desc = bonus.text; - if (game.global.b < cost) - btnClass = 'boneBtnStateOff' - else + if (game.global.b < cost){ + btnClass = 'boneBtnStateOff'; + disabled = true; + } + else btnClass = 'boneBtnStateOn'; - btnText = bonus.name + " (" + bonus.owned + "/10, " + cost + " Bones)"; + btnText = bonus.name + " (" + bonus.owned + "/10, " + cost + " Bones)"; } - html += "
    " + btnText + "
    "; + if (usingScreenReader) + html += " onclick='purchasePermaBoneBonus(\"" + item + "\")'>" + btnText + ", " + desc + ""; + else + html += " onclick='tooltip(\"Confirm Purchase\", null, \"update\", \"" + confText + "\", \"purchasePermaBoneBonus('" + item + "')\"," + cost + ")'>" + btnText + ""; } else - html += ">" + btnText + ""; - html += desc; + html += ">" + btnText + (usingScreenReader ? ", " + desc : "") + ""; + if (!usingScreenReader) html += desc; html += ""; } document.getElementById("permaBoneBonuses").innerHTML = html; @@ -17703,9 +17842,11 @@ var sugarRush = { enableIcon: function () { var elem = this.getIconElement(); if (!elem){ - document.getElementById('goodGuyName').innerHTML += ' '; + document.getElementById('goodGuyName').innerHTML += ' '; + screenReaderAssert("Sugar Rush activated, " + this.getAttackStrength() + "X attack"); return; } + if (!this.iconEnabled) screenReaderAssert("Sugar Rush activated, " + this.getAttackStrength() + "X attack"); elem.style.display = 'inline-block'; this.iconEnabled = true; }, @@ -17714,6 +17855,7 @@ var sugarRush = { if (!elem) return; elem.style.display = 'none'; + if (this.iconEnabled) screenReaderAssert("Sugar Rush expired"); this.iconEnabled = false; }, tick: function () { @@ -17818,6 +17960,7 @@ function activateTurkimpPowers() { game.global.turkimpTimer = timeToExpire; if (game.talents.turkimp2.purchased) return; document.getElementById("turkimpBuff").style.display = "block"; + screenReaderAssert("Well Fed activated"); if (game.global.playerGathering) setGather(game.global.playerGathering); var possibilities = [ "Yum, Turkimp! You eat some and put some in your pockets for later.", @@ -17897,6 +18040,7 @@ function updateTurkimpTime(drawIcon = false) { if (timeRemaining <= 0) { game.global.turkimpTimer = 0; document.getElementById('turkimpBuff').style.display = 'none'; + screenReaderAssert("Well Fed expired"); if (game.global.playerGathering) setGather(game.global.playerGathering); elem.innerHTML = '00:00'; return; @@ -19934,6 +20078,11 @@ function gameLoop(makeUp, now) { if (loops % 10 == 0){ runEverySecond(makeUp); } + //every minute, grant a bone + if (loops % 600 == 0){ + game.global.b++; + updateSkeleBtn(); + } //every 2 seconds if (loops % 20 == 0){ if (mutations.Living.active()){ diff --git a/updates.js b/updates.js index ccea86ac..3360f74c 100644 --- a/updates.js +++ b/updates.js @@ -1402,6 +1402,26 @@ function tooltip(what, isItIn, event, textString, attachFunction, numCheck, rena } if (what == "Export"){ var saveText = save(true); + // Screenreader: auto-download file, skip the modal + if (usingScreenReader) { + var saveName = 'Trimps Save P' + game.global.totalPortals; + if (game.global.universe == 2 || game.global.totalRadPortals > 0){ + saveName += " " + game.global.totalRadPortals + " U" + game.global.universe; + } + saveName += " Z" + game.global.world; + var a = document.createElement('a'); + a.download = saveName + '.txt'; + if (Blob !== null) { + var blob = new Blob([saveText], {type: 'text/plain'}); + a.href = URL.createObjectURL(blob); + } else { + a.href = 'data:text/plain,' + encodeURIComponent(saveText); + } + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + return; + } if (textString){ tooltipText = textString + "

    "; what = "Thanks!"; @@ -5190,6 +5210,15 @@ function postMessages(){ var log = document.getElementById("log"); var needsScroll = ((log.scrollTop + 10) > (log.scrollHeight - log.clientHeight)); var pendingMessages = pendingLogs.all.join(''); + // Announce new messages via a separate live region so log DOM changes never trigger SR + if (usingScreenReader) { + var announceDiv = document.getElementById('srLogAnnounce'); + if (announceDiv) { + var temp = document.createElement('div'); + temp.innerHTML = pendingMessages; + announceDiv.textContent = temp.textContent; + } + } log.insertAdjacentHTML('beforeend', pendingMessages); pendingLogs.all = []; for (var item in pendingLogs){ @@ -6244,6 +6273,20 @@ function updateButtonColor(what, canAfford, isJob) { } else swapClass("thingColor", "thingColorCanNotAfford", elem); + // Screenreader: wrap affordable items in h1 for heading navigation + if (usingScreenReader) { + if (canAfford && elem.parentElement && elem.parentElement.tagName !== 'H1') { + var h1 = document.createElement('h1'); + h1.style.margin = '0'; + h1.style.fontSize = 'inherit'; + elem.parentElement.insertBefore(h1, elem); + h1.appendChild(elem); + } else if (!canAfford && elem.parentElement && elem.parentElement.tagName === 'H1') { + var h1 = elem.parentElement; + h1.parentElement.insertBefore(elem, h1); + h1.remove(); + } + } } function getWarpstationColor() { @@ -7591,6 +7634,22 @@ if (elem == null) { else className = className[0] + newClass; elem.className = className; + // Screenreader: mark/unmark the active cell with * and aria-current live region + if (usingScreenReader && prefix === 'cellColor') { + if (newClass === 'cellColorCurrent') { + elem.setAttribute('aria-current', 'true'); + if (!elem.querySelector('.srCurrentMarker')) { + var marker = document.createElement('span'); + marker.className = 'srCurrentMarker'; + marker.textContent = '* '; + elem.prepend(marker); + } + } else { + elem.removeAttribute('aria-current'); + var marker = elem.querySelector('.srCurrentMarker'); + if (marker) marker.remove(); + } + } } function goRadial(elem, currentSeconds, totalSeconds, frameTime) { From 43e7ff899dbd4e6435bd2b9963a4f1bec0fa9e68 Mon Sep 17 00:00:00 2001 From: oriol Date: Mon, 9 Feb 2026 06:53:08 +0000 Subject: [PATCH 4/8] make message log respect settings --- ScreenReader.html | 10 +++++----- updates.js | 34 ++++++++++++++++++++++++---------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/ScreenReader.html b/ScreenReader.html index e24ea9f3..d344fa0b 100644 --- a/ScreenReader.html +++ b/ScreenReader.html @@ -314,20 +314,20 @@
    Screen Reader Information
    - +
    - +
    - +
    - +
    -
    +
    diff --git a/updates.js b/updates.js index 3360f74c..39203c51 100644 --- a/updates.js +++ b/updates.js @@ -5129,8 +5129,8 @@ function message(messageString, type, lootIcon, extraClass, extraTag, htmlPrefix if (!extraStyle) extraStyle = ""; else extraStyle = "; " + extraStyle; if (usingScreenReader){ - if (type == "Story") document.getElementById('srSumLastStory').innerHTML = "Z " + game.global.world + ": " + messageString; - if (type == "Combat") document.getElementById('srSumLastCombat').innerHTML = messageString; + if (type == "Story" && game.global.messages.Story.enabled) document.getElementById('srSumLastStory').innerHTML = "Z " + game.global.world + ": " + messageString; + if (type == "Combat" && game.global.messages.Combat.enabled) document.getElementById('srSumLastCombat').innerHTML = messageString; } if (messageLock && type !== "Notices"){ return; @@ -5216,7 +5216,13 @@ function postMessages(){ if (announceDiv) { var temp = document.createElement('div'); temp.innerHTML = pendingMessages; - announceDiv.textContent = temp.textContent; + var visibleText = ''; + for (var i = 0; i < temp.children.length; i++) { + if (temp.children[i].style.display !== 'none') { + visibleText += temp.children[i].textContent + ' '; + } + } + announceDiv.textContent = visibleText.trim(); } } log.insertAdjacentHTML('beforeend', pendingMessages); @@ -5262,17 +5268,25 @@ function trimMessages(what){ function filterMessage(what, updateOnly){ //send true for updateOnly var log = document.getElementById("log"); var displayed = game.global.messages[what].enabled; + var btnElem = document.getElementById(what + "Filter"); + if (btnElem == null) return; + var isCheckbox = (btnElem.type === 'checkbox'); if (!updateOnly){ - displayed = (displayed) ? false : true; + if (isCheckbox) { + displayed = btnElem.checked; + } else { + displayed = !displayed; + } game.global.messages[what].enabled = displayed; } var toChange = document.getElementsByClassName(what + "Message"); - var btnText = (displayed) ? what : what + " off"; - var btnElem = document.getElementById(what + "Filter"); - if (btnElem == null) return; - btnElem.innerHTML = btnText; - btnElem.className = ""; - btnElem.className = getTabClass(displayed); + if (isCheckbox) { + btnElem.checked = displayed; + } else { + btnElem.innerHTML = (displayed) ? what : what + " off"; + btnElem.className = ""; + btnElem.className = getTabClass(displayed); + } displayed = (displayed) ? "block" : "none"; for (var x = 0; x < toChange.length; x++){ toChange[x].style.display = displayed; From 5797648e461aaba47e9a0b090dbdfc8dd783ef0f Mon Sep 17 00:00:00 2001 From: oriol Date: Mon, 9 Feb 2026 07:32:17 +0000 Subject: [PATCH 5/8] map chamber fixes --- ScreenReader.html | 6 +++--- index.html | 22 +++++++++++----------- indexKong.html | 2 +- main.js | 27 ++++++++++++++++++++++----- updates.js | 13 ++++++++++--- 5 files changed, 47 insertions(+), 23 deletions(-) diff --git a/ScreenReader.html b/ScreenReader.html index d344fa0b..f15d83d8 100644 --- a/ScreenReader.html +++ b/ScreenReader.html @@ -103,7 +103,7 @@
    Screen Reader Information
    You have 0
    - You Can Also Earn Bones In Game By Killing Skeletimps + You Can Also Earn Bones In Game By Killing Skeletimps. You also earn 1 bone per minute.
    The Bone Trader trades bones for... bonuses @@ -766,11 +766,11 @@

    Zone 1

    - - +
    -
    @@ -954,9 +954,9 @@
    -
    +
    -
    +
    diff --git a/indexKong.html b/indexKong.html index 18b7cdbc..835d4628 100644 --- a/indexKong.html +++ b/indexKong.html @@ -100,7 +100,7 @@ Get some more bones

    - You can earn bones as you progress through the world by killing Skeletimps and Megaskeletimps. + You can earn bones as you progress through the world by killing Skeletimps and Megaskeletimps. You also earn 1 bone per minute.
    Click to Bundle 4 Exotic Imp-orts and get 100 free bones! diff --git a/main.js b/main.js index 70079bb2..bdd29304 100644 --- a/main.js +++ b/main.js @@ -10994,6 +10994,10 @@ function mapsSwitch(updateOnly, fromRecycle) { recycleBtn.style.visibility = "visible"; if (currentMapObj.noRecycle) recycleBtn.innerHTML = "Abandon Map"; } + if (usingScreenReader){ + var focusTarget = document.getElementById("advMapsPreset1"); + if (focusTarget) setTimeout(function(){ focusTarget.focus(); }, 100); + } } else if (game.global.mapsActive) { //Switching to maps @@ -11115,17 +11119,29 @@ function selectMap(mapId, force) { map = game.global.mapsOwnedArray[map]; if (!map) return; document.getElementById("selectedMapName").innerHTML = map.name; - document.getElementById("mapStatsSize").innerHTML = (Math.floor(map.size)); - document.getElementById("mapStatsDifficulty").innerHTML = Math.floor(map.difficulty * 100) + "%"; - document.getElementById("mapStatsLoot").innerHTML = Math.floor(map.loot * 100) + "%"; - document.getElementById("mapStatsItems").innerHTML = (map.location == "Void") ? " " : addSpecials(true, true, map); - document.getElementById("mapStatsResource").innerHTML = game.mapConfig.locations[map.location].resourceType; + var sizeElem = document.getElementById("mapStatsSize"); + sizeElem.innerHTML = (Math.floor(map.size)); + sizeElem.setAttribute("aria-label", "Size " + Math.floor(map.size)); + var diffElem = document.getElementById("mapStatsDifficulty"); + diffElem.innerHTML = Math.floor(map.difficulty * 100) + "%"; + diffElem.setAttribute("aria-label", "Difficulty " + Math.floor(map.difficulty * 100) + "%"); + var lootElem = document.getElementById("mapStatsLoot"); + lootElem.innerHTML = Math.floor(map.loot * 100) + "%"; + lootElem.setAttribute("aria-label", "Loot " + Math.floor(map.loot * 100) + "%"); + var itemsElem = document.getElementById("mapStatsItems"); + itemsElem.innerHTML = (map.location == "Void") ? " " : addSpecials(true, true, map); + itemsElem.setAttribute("aria-label", "Items " + ((map.location == "Void") ? "none" : addSpecials(true, true, map))); + var resourceElem = document.getElementById("mapStatsResource"); + resourceElem.innerHTML = game.mapConfig.locations[map.location].resourceType; + resourceElem.setAttribute("aria-label", "Resource " + game.mapConfig.locations[map.location].resourceType); if (typeof game.global.mapsOwnedArray[getMapIndex(game.global.lookingAtMap)] !== 'undefined') { var prevSelected = document.getElementById(game.global.lookingAtMap); prevSelected.className = prevSelected.className.replace("mapElementSelected","mapElementNotSelected"); + prevSelected.setAttribute("aria-checked", "false"); } var currentSelected = document.getElementById(mapId); currentSelected.className = currentSelected.className.replace("mapElementNotSelected", "mapElementSelected"); + currentSelected.setAttribute("aria-checked", "true"); game.global.lookingAtMap = mapId; document.getElementById("selectMapBtn").innerHTML = "Run Map"; document.getElementById("selectMapBtn").style.visibility = "visible"; @@ -17187,6 +17203,7 @@ function updateImports(which) { count++; if (usingScreenReader && which == 0) { var row = elem.insertRow(); + row.id = item; var cell = row.insertCell(); if (game.unlocks.imps[item]) { cell.innerHTML = item + " - " + badGuy.dropDesc + " (Owned)"; diff --git a/updates.js b/updates.js index 39203c51..70d74dc3 100644 --- a/updates.js +++ b/updates.js @@ -6073,9 +6073,16 @@ function unlockMap(what) { //what here is the array index } } else abbrev = ((abbrev) ? getMapSpecTag(abbrev) : ""); - let tagName = (usingScreenReader) ? 'li' : 'div' - if (game.options.menu.extraStats.enabled) elem.innerHTML = '<' + tagName + tooltip + ' class="' + btnClass + '" id="' + item.id + '" onclick="selectMap(\'' + item.id + '\')">
    ' + item.name + '

    ' + ((item.stacked) ? "(x" + (item.stacked + 1) + ") " : "") + 'Level ' + level + abbrev + '
    ' + Math.floor(item.loot * 100) + '% ' + item.size + ' ' + Math.floor(item.difficulty * 100) + '%' + elem.innerHTML; - else elem.innerHTML = '<' + tagName + tooltip + ' class="' + btnClass + '" id="' + item.id + '" onclick="selectMap(\'' + item.id + '\')">' + item.name + '
    ' + ((item.stacked) ? "(x" + (item.stacked + 1) + ") " : "") + 'Level ' + level + abbrev + '' + elem.innerHTML; + let tagName = 'div' + var isSelected = (item.id == game.global.lookingAtMap || item.id == game.global.currentMapId); + var radioAttrs = ' role="radio" aria-checked="' + isSelected + '" tabindex="0" onkeydown="if(event.key===\'Enter\'||event.key===\' \'){event.preventDefault();selectMap(\'' + item.id + '\')}"'; + if (game.options.menu.extraStats.enabled){ + var lootLabel = (usingScreenReader) ? 'Loot ' : ''; + var sizeLabel = (usingScreenReader) ? ' Size ' : ''; + var diffLabel = (usingScreenReader) ? ' Difficulty ' : ''; + elem.innerHTML = '<' + tagName + tooltip + radioAttrs + ' class="' + btnClass + '" id="' + item.id + '" onclick="selectMap(\'' + item.id + '\')">
    ' + item.name + '

    ' + ((item.stacked) ? "(x" + (item.stacked + 1) + ") " : "") + 'Level ' + level + abbrev + '
    ' + lootLabel + Math.floor(item.loot * 100) + '% ' + sizeLabel + item.size + ' ' + diffLabel + Math.floor(item.difficulty * 100) + '%' + elem.innerHTML; + } + else elem.innerHTML = '<' + tagName + tooltip + radioAttrs + ' class="' + btnClass + '" id="' + item.id + '" onclick="selectMap(\'' + item.id + '\')">' + item.name + '
    ' + ((item.stacked) ? "(x" + (item.stacked + 1) + ") " : "") + 'Level ' + level + abbrev + '' + elem.innerHTML; if (item.id == game.global.currentMapId) swapClass("mapElement", "mapElementSelected", document.getElementById(item.id)); } From 85d68032f1c8b9167e095bed5913c90cf7028c7b Mon Sep 17 00:00:00 2001 From: oriol Date: Mon, 9 Feb 2026 08:52:54 +0000 Subject: [PATCH 6/8] address copilot review: fix duplicate IDs, Blob guard, lockTooltip, keyboard a11y, remove bone timer - Fix duplicate id="closeStatsBtn" (renamed to trimpsInfoBtn) - Use typeof Blob/URL guards + revokeObjectURL in SR export - Clear lockTooltip in getPsString/getMaxResources SR paths - Add keyboard handlers (Enter/Space) to settings table buttons - Change role="link" to role="button" on settings table cells - Remove superfluous second arg from presetTab() radio calls - Remove bone-per-5-minutes timer from gameLoop Co-Authored-By: Claude Opus 4.6 --- ScreenReader.html | 32 ++++++++++++++++---------------- index.html | 2 +- indexKong.html | 2 +- main.js | 5 ----- updates.js | 14 ++++++++++---- 5 files changed, 28 insertions(+), 27 deletions(-) diff --git a/ScreenReader.html b/ScreenReader.html index f15d83d8..b5574cf3 100644 --- a/ScreenReader.html +++ b/ScreenReader.html @@ -103,7 +103,7 @@
    Screen Reader Information
    You have 0
    - You Can Also Earn Bones In Game By Killing Skeletimps. You also earn 1 bone per minute. + You Can Also Earn Bones In Game By Killing Skeletimps.
    The Bone Trader trades bones for... bonuses @@ -1030,13 +1030,13 @@

    Perk Preset @@ -1176,7 +1176,7 @@

    -   +   @@ -1430,31 +1430,31 @@
    Game Overview
    - - - - - - - - - diff --git a/index.html b/index.html index 96326d49..d7885553 100644 --- a/index.html +++ b/index.html @@ -94,7 +94,7 @@
    You have 0
    - You can earn bones as you progress through the world by killing Skeletimps and Megaskeletimps. You also earn 1 bone per minute. + You can earn bones as you progress through the world by killing Skeletimps and Megaskeletimps.
    The Bone Trader trades bones for... bonuses diff --git a/indexKong.html b/indexKong.html index 835d4628..18b7cdbc 100644 --- a/indexKong.html +++ b/indexKong.html @@ -100,7 +100,7 @@ Get some more bones

    - You can earn bones as you progress through the world by killing Skeletimps and Megaskeletimps. You also earn 1 bone per minute. + You can earn bones as you progress through the world by killing Skeletimps and Megaskeletimps.
    Click to Bundle 4 Exotic Imp-orts and get 100 free bones! diff --git a/main.js b/main.js index bdd29304..0abac72c 100644 --- a/main.js +++ b/main.js @@ -20095,11 +20095,6 @@ function gameLoop(makeUp, now) { if (loops % 10 == 0){ runEverySecond(makeUp); } - //every minute, grant a bone - if (loops % 600 == 0){ - game.global.b++; - updateSkeleBtn(); - } //every 2 seconds if (loops % 20 == 0){ if (mutations.Living.active()){ diff --git a/updates.js b/updates.js index 70d74dc3..6fd0cf4f 100644 --- a/updates.js +++ b/updates.js @@ -1411,15 +1411,19 @@ function tooltip(what, isItIn, event, textString, attachFunction, numCheck, rena saveName += " Z" + game.global.world; var a = document.createElement('a'); a.download = saveName + '.txt'; - if (Blob !== null) { + if (typeof Blob !== 'undefined' && typeof URL !== 'undefined' && URL.createObjectURL) { var blob = new Blob([saveText], {type: 'text/plain'}); a.href = URL.createObjectURL(blob); + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(a.href); } else { a.href = 'data:text/plain,' + encodeURIComponent(saveText); + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); } - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); return; } if (textString){ @@ -2527,6 +2531,7 @@ function getPsString(what, rawNum) { if (usingScreenReader) { var resName = what.charAt(0).toUpperCase() + what.slice(1); screenReaderAssert(resName + " per second: " + prettify(currentCalc)); + game.global.lockTooltip = false; return; } game.global.lockTooltip = false; @@ -3586,6 +3591,7 @@ function getMaxResources(what) { textString += "
    + Save +
    Export
    +
    Import
    +
    Stats
    +
    Achieves
    +
    Settings
    - + + - V | What's New + + V | What's New +     
    "; if (usingScreenReader) { screenReaderAssert("Max " + what + ": " + prettify(currentCalc) + ". " + structure + ": " + structureObj.owned); + game.global.lockTooltip = false; return; } game.global.lockTooltip = false; From 9ebf2f991a6a82ed58270827fd400e487ba0a41b Mon Sep 17 00:00:00 2001 From: oriol Date: Mon, 9 Feb 2026 17:51:47 +0000 Subject: [PATCH 7/8] Improve screen reader keyboard navigation and focus Co-Authored-By: Claude Opus 4.6 --- ScreenReader.html | 30 +++++++++++++++--------------- main.js | 4 +++- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/ScreenReader.html b/ScreenReader.html index b5574cf3..c3126d5a 100644 --- a/ScreenReader.html +++ b/ScreenReader.html @@ -185,7 +185,7 @@
    Screen Reader Information
    - +
    @@ -206,7 +206,7 @@
    Screen Reader Information
    - +
    @@ -227,7 +227,7 @@
    Screen Reader Information
    - +
    @@ -241,7 +241,7 @@
    Screen Reader Information

    - +
    @@ -302,7 +302,7 @@
    Screen Reader Information
    0/0 employed
    - +
    @@ -370,7 +370,7 @@

    End of Log

    - +
    @@ -1430,31 +1430,31 @@
    Game Overview
    - - - - - - - - - diff --git a/main.js b/main.js index 0abac72c..000a23af 100644 --- a/main.js +++ b/main.js @@ -4957,7 +4957,8 @@ function buildBuilding(what, amt = 1) { if (building.owned === 0 && typeof building.first !== 'undefined') building.first(); building.owned += amt; if (usingScreenReader && game.global.playerGathering === 'buildings') { - screenReaderAssert("Built " + (amt > 1 ? amt + " " : "") + what + ", " + building.owned + " owned"); + if (!(what === 'Trap' && game.global.trapBuildToggled && building.owned % 100 !== 0)) + screenReaderAssert("Built " + (amt > 1 ? amt + " " : "") + what + ", " + building.owned + " owned"); } let toIncrease; checkAchieve('housing', what); @@ -6623,6 +6624,7 @@ function buyMap() { game.resources.fragments.owned -= cost; createMap(newLevel); if (!game.global.currentMapId) selectMap(game.global.mapsOwnedArray[game.global.mapsOwnedArray.length - 1].id); + document.getElementById("selectMapBtn").focus(); return 1; } else message("You can't afford this map! You need " + prettify(cost) + " fragments but only have " + prettify(game.resources.fragments.owned) + ".", "Notices"); From fc562d74dbbfdf609080ff8b8b8cc689ab6cd19c Mon Sep 17 00:00:00 2001 From: oriol Date: Thu, 12 Feb 2026 02:59:52 +0000 Subject: [PATCH 8/8] warn about full resouces and time left --- main.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/main.js b/main.js index 000a23af..ccfc1ec2 100644 --- a/main.js +++ b/main.js @@ -4620,6 +4620,17 @@ function gather() { const timeToFillElem = document.getElementById(increase + 'TimeToFill'); const timeToMax = calculateTimeToMax(game.resources[increase], perSec, null, true); if (timeToFillElem && timeToFillElem.innerHTML !== timeToMax) timeToFillElem.textContent = timeToMax; + if (increase === 'food' || increase === 'wood' || increase === 'metal') { + var collectBtn = document.getElementById(increase + 'CollectBtn'); + if (collectBtn) { + var btnName = setGatherTextAs(increase, game.global.playerGathering === increase); + var resource = game.resources[increase]; + var effectiveMax = calcHeirloomBonus("Shield", "storageSize", resource.max * (1 + game.portal.Packrat.modifier * getPerkLevel("Packrat"))); + var isFull = resource.owned >= effectiveMax; + var ariaLabel = btnName + (isFull ? ', Full' : (timeToMax ? ', ' + timeToMax : '')); + collectBtn.setAttribute('aria-label', ariaLabel); + } + } } }
    + Save +
    Export
    +
    Import
    +
    Stats
    +
    Achieves
    +
    Settings
    + + V | What's New +