From 762489b8073dec24d03bed16a0d4acdf8671b810 Mon Sep 17 00:00:00 2001
From: seonghobae <8172694+seonghobae@users.noreply.github.com>
Date: Thu, 3 Sep 2026 14:29:00 +0000
Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20=EA=B0=9C?=
=?UTF-8?q?=EC=84=A0=EB=90=9C=20=EB=B9=84=EB=8F=99=EA=B8=B0=20=EB=A1=9C?=
=?UTF-8?q?=EB=94=A9=20=EC=A0=91=EA=B7=BC=EC=84=B1=20=EB=B0=8F=20UX=20(#co?=
=?UTF-8?q?nsole)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* `scanner/dashboard/console.html` 내 `aria-busy="true"` 상태에 대한 명시적 CSS 시각 장애(opacity, pointer-events) 추가
* `tr.scan`에 대한 `onclick` 및 `keydown` 이벤트에 JavaScript 가드(`aria-busy` 체크) 추가하여 중복 클릭/이벤트 방지
* 관련 UX/접근성 학습 내용을 `.jules/palette.md` 저널에 기록
---
.jules/palette.md | 3 +++
scanner/dashboard/console.html | 5 +++--
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/.jules/palette.md b/.jules/palette.md
index ea004e2d..ae0705f8 100644
--- a/.jules/palette.md
+++ b/.jules/palette.md
@@ -81,3 +81,6 @@
## 2026-08-12 - Skip to Content Accessibility
**Learning:** Screen reader and keyboard-only users experience significant friction when forced to navigate through repetitive header controls on every page load.
**Action:** Keep a visible-on-focus skip link as the first interactive element, target a programmatically focusable main container, and give the focused link a high-contrast outline.
+## 2024-10-10 - Blocking Interactions on Non-Native Loading Elements
+**Learning:** Adding `aria-busy="true"` to non-native elements (like `
`) provides screen reader feedback but fails to natively block interactions. Keyboard and mouse users can still re-trigger async actions while they are loading.
+**Action:** Always pair `aria-busy="true"` with CSS visual disabling (e.g. `pointer-events: none; opacity: 0.7;`) and explicitly add JavaScript event guards (`if (el.getAttribute('aria-busy') === 'true') return;`) in `click` and `keydown` handlers to fully block duplicate interactions.
diff --git a/scanner/dashboard/console.html b/scanner/dashboard/console.html
index 7ec262af..bf9c0823 100644
--- a/scanner/dashboard/console.html
+++ b/scanner/dashboard/console.html
@@ -46,6 +46,7 @@
.err{color:var(--crit);font-weight:600}
code{background:var(--bg);padding:1px 5px;border-radius:4px}
.hidden{display:none}
+ [aria-busy="true"]{pointer-events:none;opacity:0.7}
@@ -135,8 +136,8 @@ AppGuardrail Console
${esc(s.created_at)} | ${esc(s.repo||"—")} | ${esc((s.commit||"—").slice(0,10))} |
${s.total} | ${pill(s.deploy_blocking,"var(--crit)")} | ${pill(s.new_blocking,"var(--high)")} |
`).join("")||'| No scans. POST to /api/v1/scans from CI. |
';
document.querySelectorAll("tr.scan").forEach(tr=>{
- tr.onclick=()=>detail(tr.dataset.id,tr);
- tr.addEventListener('keydown', e => { if(e.key === 'Enter' || e.key === ' ') { e.preventDefault(); detail(tr.dataset.id,tr); } });
+ tr.onclick=()=>{if(tr.getAttribute("aria-busy")==="true")return;detail(tr.dataset.id,tr);};
+ tr.addEventListener('keydown', e => { if(e.key === 'Enter' || e.key === ' ') { e.preventDefault(); if(tr.getAttribute("aria-busy")==="true")return; detail(tr.dataset.id,tr); } });
});
}catch(e){ $("#msg").classList.remove("hidden");$("#app").classList.add("hidden");
$("#msg").innerHTML=`${esc(e.message)}`; }
From 2599c24c4262abe0303d553df2faa24987dfce40 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Fri, 4 Sep 2026 00:06:42 +0900
Subject: [PATCH 2/3] test(console): pin busy-row duplicate request guards
---
tests/test_console_detail_loading_contract.py | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/tests/test_console_detail_loading_contract.py b/tests/test_console_detail_loading_contract.py
index 38eebed0..88ee8327 100644
--- a/tests/test_console_detail_loading_contract.py
+++ b/tests/test_console_detail_loading_contract.py
@@ -29,6 +29,18 @@ def test_console_exposes_loading_busy_and_error_states():
assert 'tr.removeAttribute("aria-busy");' in html
+def test_console_busy_scan_row_blocks_duplicate_pointer_and_keyboard_requests():
+ """Busy scan rows must not start a second detail request from pointer or keyboard input."""
+ html = _console_html()
+
+ assert '[aria-busy="true"]{pointer-events:none;opacity:0.7}' in html
+ assert 'tr.onclick=()=>{if(tr.getAttribute("aria-busy")==="true")return;detail(tr.dataset.id,tr);};' in html
+ assert (
+ "if(e.key === 'Enter' || e.key === ' ') { e.preventDefault(); "
+ 'if(tr.getAttribute("aria-busy")==="true")return; detail(tr.dataset.id,tr); }'
+ ) in html
+
+
def test_console_detail_scrolling_respects_reduced_motion():
"""Successful and failed detail requests must honor reduced-motion preferences."""
html = _console_html()
From 27d7729c437955fee774d957eee9c884f9016341 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Fri, 4 Sep 2026 00:06:58 +0900
Subject: [PATCH 3/3] docs(palette): keep loading guidance scoped to tested
console behavior
---
.jules/palette.md | 3 ---
1 file changed, 3 deletions(-)
diff --git a/.jules/palette.md b/.jules/palette.md
index ae0705f8..ea004e2d 100644
--- a/.jules/palette.md
+++ b/.jules/palette.md
@@ -81,6 +81,3 @@
## 2026-08-12 - Skip to Content Accessibility
**Learning:** Screen reader and keyboard-only users experience significant friction when forced to navigate through repetitive header controls on every page load.
**Action:** Keep a visible-on-focus skip link as the first interactive element, target a programmatically focusable main container, and give the focused link a high-contrast outline.
-## 2024-10-10 - Blocking Interactions on Non-Native Loading Elements
-**Learning:** Adding `aria-busy="true"` to non-native elements (like ``) provides screen reader feedback but fails to natively block interactions. Keyboard and mouse users can still re-trigger async actions while they are loading.
-**Action:** Always pair `aria-busy="true"` with CSS visual disabling (e.g. `pointer-events: none; opacity: 0.7;`) and explicitly add JavaScript event guards (`if (el.getAttribute('aria-busy') === 'true') return;`) in `click` and `keydown` handlers to fully block duplicate interactions.