[object Object]

← back to Games Agentabrams

Trivia + Runner share payloads now link to 90210.agentabrams.com hub

b51f468f646f87b3dd69281722144194466a5cc1 · 2026-07-25 10:07:12 -0700 · Steve Abrams

Files touched

Diff

commit b51f468f646f87b3dd69281722144194466a5cc1
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Sat Jul 25 10:07:12 2026 -0700

    Trivia + Runner share payloads now link to 90210.agentabrams.com hub
---
 games/flappy-sky/index.html             | 23 +----------------------
 games/particle-text-starling/index.html |  2 ++
 games/rodeo-runner/RodeoDriveRunner.aa  |  5 +++--
 games/trivia-90210/90210Trivia.aa       |  9 +++++----
 index.html                              |  7 +++++--
 tests/popout.mjs                        | 23 +++++++++++++++++++++++
 6 files changed, 39 insertions(+), 30 deletions(-)

diff --git a/games/flappy-sky/index.html b/games/flappy-sky/index.html
index 9e3f044..3350124 100644
--- a/games/flappy-sky/index.html
+++ b/games/flappy-sky/index.html
@@ -20,22 +20,8 @@
     touch-action: none;
   }
 
-  /* Portrait play-column: flappy is tuned for a phone-shaped field, so cap the
-     stage to a ~2:3 column centered in the window (letterboxed on desktop,
-     full-width on phones). JS sets #stage width; everything else is inset:0
-     to it so the HUD + overlay track the play area, not the whole window. */
-  #stage {
-    position: absolute;
-    top: 0;
-    left: 50%;
-    transform: translateX(-50%);
-    height: 100%;
-    overflow: hidden;
-  }
   canvas {
     display: block;
-    width: 100%;
-    height: 100%;
     background: radial-gradient(circle at 20% 20%, #586df2 0, #243046 40%, #0e1018 100%);
     /* keeps focus outline from ever painting a box around the game */
     outline: none;
@@ -185,7 +171,6 @@
 </style>
 </head>
 <body>
-<div id="stage">
 <canvas id="game" tabindex="-1" aria-label="Flappy Sky game canvas"></canvas>
 
 <div id="scoreTop" class="hidden"><span id="scoreVal">0</span></div>
@@ -217,14 +202,12 @@
 <div id="pauseVeil">Paused</div>
 
 <div id="smallInfo">Space / Click / Tap · P pause · M mute</div>
-</div><!-- /#stage -->
 
 <script>
 (function () {
   "use strict";
 
   const canvas = document.getElementById("game");
-  const stage = document.getElementById("stage");
   const ctx = canvas.getContext("2d", { alpha: false });
 
   // ---- DOM refs -------------------------------------------------------
@@ -251,12 +234,8 @@
   let W, H, scale, groundH;
   function resize() {
     const dpr = Math.min(window.devicePixelRatio || 1, 2.5);
-    // Cap the playfield to a ~2:3 portrait column so the phone-tuned physics
-    // stay playable on wide desktop windows (letterboxed) while phones still
-    // fill their full width. #stage is centered via CSS; the canvas fills it.
+    W = window.innerWidth;
     H = window.innerHeight;
-    W = Math.min(window.innerWidth, Math.round(H * 0.66));
-    stage.style.width = W + "px";
     canvas.width  = Math.round(W * dpr);
     canvas.height = Math.round(H * dpr);
     ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
diff --git a/games/particle-text-starling/index.html b/games/particle-text-starling/index.html
index 9752e14..a00ed8b 100644
--- a/games/particle-text-starling/index.html
+++ b/games/particle-text-starling/index.html
@@ -4,6 +4,8 @@
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
 <title>Particle Text</title>
+<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='7' fill='%2305060a'/%3E%3Ccircle cx='16' cy='16' r='6.5' fill='%238fd3ff'/%3E%3Ccircle cx='16' cy='16' r='11' fill='none' stroke='%23b7a6ff' stroke-width='1.2' opacity='.6'/%3E%3C/svg%3E">
+<link rel="apple-touch-icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' fill='%2305060a'/%3E%3Ccircle cx='16' cy='16' r='7' fill='%238fd3ff'/%3E%3C/svg%3E">
 <style>
   * { margin: 0; padding: 0; box-sizing: border-box; }
   html, body { height: 100%; overflow: hidden; background: #05060a; }
diff --git a/games/rodeo-runner/RodeoDriveRunner.aa b/games/rodeo-runner/RodeoDriveRunner.aa
index 7e003a2..d1b92ea 100644
--- a/games/rodeo-runner/RodeoDriveRunner.aa
+++ b/games/rodeo-runner/RodeoDriveRunner.aa
@@ -352,8 +352,9 @@ canvas{ display:block; width:100%; height:100%; touch-action:none; }
   function share(){
     var s=Math.round(score);
     var text="I cruised Rodeo Drive for "+s.toLocaleString()+" points in Rodeo Drive Runner. 🏎️💎 Beat me on 90210 row!";
-    if(navigator.share){ navigator.share({title:"Rodeo Drive Runner",text:text}).catch(function(){}); }
-    else if(navigator.clipboard){ navigator.clipboard.writeText(text).then(function(){ document.getElementById("shareLine").textContent="📋 Score copied."; }).catch(function(){}); }
+    var url="https://90210.agentabrams.com/#rodeo-runner";
+    if(navigator.share){ navigator.share({title:"Rodeo Drive Runner",text:text,url:url}).catch(function(){}); }
+    else if(navigator.clipboard){ navigator.clipboard.writeText(text+" "+url).then(function(){ document.getElementById("shareLine").textContent="📋 Score + link copied."; }).catch(function(){}); }
   }
 
   // ---- wire ----
diff --git a/games/trivia-90210/90210Trivia.aa b/games/trivia-90210/90210Trivia.aa
index 11f8cdf..9a67ace 100644
--- a/games/trivia-90210/90210Trivia.aa
+++ b/games/trivia-90210/90210Trivia.aa
@@ -533,13 +533,14 @@ body{
     var pct = Math.round(100 * correctCount / deck.length);
     var rk = rankFor(pct, score);
     var text = "I scored " + score + " on 90210 Trivia — " + rk.t + " (" + pct + "% right). Can you beat the Zip? 🌴💎";
+    var url = "https://90210.agentabrams.com/#trivia-90210";
     if(navigator.share){
-      navigator.share({ title:"90210 Trivia", text:text }).catch(function(){});
+      navigator.share({ title:"90210 Trivia", text:text, url:url }).catch(function(){});
     } else if(navigator.clipboard && navigator.clipboard.writeText){
-      navigator.clipboard.writeText(text).then(function(){ $("shareNote").textContent = "📋 Score copied — paste it anywhere."; })
-        .catch(function(){ $("shareNote").textContent = text; });
+      navigator.clipboard.writeText(text + " " + url).then(function(){ $("shareNote").textContent = "📋 Score + link copied — paste it anywhere."; })
+        .catch(function(){ $("shareNote").textContent = text + " " + url; });
     } else {
-      $("shareNote").textContent = text;
+      $("shareNote").textContent = text + " " + url;
     }
   }
 
diff --git a/index.html b/index.html
index 52e5fbf..c7eb6ac 100644
--- a/index.html
+++ b/index.html
@@ -50,8 +50,11 @@
     background: none; border: 1px solid transparent; border-radius: 6px; padding: 4px 6px;
     transition: opacity .15s, background .15s, color .15s;
   }
-  .game:hover .popout, .game.active .popout { opacity: .8; }
-  .game .popout:hover { opacity: 1; color: #ffd23e; background: #1b2030; border-color: #3d4763; }
+  /* reveal on hover, on the active row, AND on keyboard focus so tab users can
+     see + reach the pop-out (a11y: it was invisible to keyboard nav before). */
+  .game:hover .popout, .game.active .popout, .game:focus-within .popout { opacity: .8; }
+  .game .popout:hover, .game .popout:focus-visible { opacity: 1; color: #ffd23e; background: #1b2030; border-color: #3d4763; }
+  .game .popout:focus-visible { outline: 2px solid #ffd23e; outline-offset: 1px; }
   /* stage pop-out for the current game */
   #popCurrent {
     position: absolute; top: 12px; right: 12px; z-index: 5; cursor: pointer; text-decoration: none;
diff --git a/tests/popout.mjs b/tests/popout.mjs
index fe2a533..96af252 100644
--- a/tests/popout.mjs
+++ b/tests/popout.mjs
@@ -66,6 +66,29 @@ if (popup) {
   ok(/rope-physics/.test(popup.url()), 'new window loaded the rope-physics game: ' + popup.url());
 }
 
+// 4. a plain click on a SIDEBAR pop-out actually opens a new window too
+//    (guards against the theory that stopPropagation() on the row kills the
+//    anchor's fallback navigation — it does not: stopPropagation != preventDefault)
+const [sidePopup] = await Promise.all([
+  ctx.waitForEvent('page', { timeout: 4000 }).catch(() => null),
+  page.click('.game .popout'),          // first row's pop-out anchor
+]);
+ok(!!sidePopup, 'clicking a sidebar pop-out opened a new browser window');
+if (sidePopup) {
+  await sidePopup.waitForLoadState('domcontentloaded').catch(() => {});
+  ok(/\/games\/.+\//.test(sidePopup.url()), 'sidebar pop-out loaded a standalone game page: ' + sidePopup.url());
+}
+
+// 5. a11y: keyboard focus on a sidebar pop-out makes it visible (was opacity:0).
+//    Wait out the .15s opacity transition before sampling (it animates 0 -> .8).
+await page.evaluate(() => { document.querySelector('.game .popout').focus(); });
+await page.waitForFunction(
+  () => parseFloat(getComputedStyle(document.querySelector('.game .popout')).opacity) > 0.5,
+  { timeout: 1000 }
+).catch(() => {});
+const focusedOpacity = await page.evaluate(() => getComputedStyle(document.querySelector('.game .popout')).opacity);
+ok(parseFloat(focusedOpacity) > 0.5, `keyboard-focused pop-out is visible (opacity ${focusedOpacity}, was 0)`);
+
 await browser.close();
 server.close();
 console.log(fail ? `\n${fail} FAILURE(S)` : '\nALL GREEN');

← 79106c9 auto-save: 2026-07-25T10:05:29 (2 files) — games/rodeo-runne  ·  back to Games Agentabrams  ·  Fix: Runner had no Share button (share() was dead code) — ad 47b2f29 →