From 4f3373660904f43bc3bbc9ad777d6f77209153bb Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Mon, 28 Sep 2026 18:54:53 +0000 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20[=EC=99=B8?= =?UTF-8?q?=EB=B6=80=20=EB=A7=81=ED=81=AC=20=EC=A0=91=EA=B7=BC=EC=84=B1=20?= =?UTF-8?q?=EB=B0=8F=20UX=20=EA=B0=9C=EC=84=A0]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 외부 링크에 대한 접근성과 UI를 개선합니다. - `target="_blank"` 링크에 `.sr-only` 클래스로 "새 탭에서 열림" 텍스트 추가 - 외부 링크임을 알리는 아이콘 삽입 (시각적 개선 및 `aria-hidden="true"`) - 긴 URL이 레이아웃을 깨는 것을 방지하기 위해 `word-break: break-all`이 전역으로 적용되지 않고 `.ext-link` 유틸리티 클래스로 제한되도록 스코핑 --- .jules/palette.md | 4 ++++ scanner/dashboard/index.html | 8 ++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.jules/palette.md b/.jules/palette.md index ea004e2d2..b20cb4c3b 100644 --- a/.jules/palette.md +++ b/.jules/palette.md @@ -81,3 +81,7 @@ ## 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. + +## 2026-10-24 - 외부 링크 접근성 강화 및 스타일 스코핑 +**Learning:** `target="_blank"` 속성을 가진 외부 링크에 시각적(아이콘) 표시는 제공하지만, 스크린 리더 등 보조 기기 사용자를 위한 텍스트 경고(`새 탭에서 열림` 등)를 누락하거나, 긴 URL 단어를 강제 줄바꿈하기 위한 `word-break: break-all`을 링크 전체에 전역으로 적용하면 UI 레이아웃이 무너지거나 접근성이 저하된다. +**Action:** 외부 링크에는 항상 화면에 보이지 않는 `.sr-only` 클래스의 텍스트 설명과, 보조 기기에서 무시되는 `aria-hidden="true"` 아이콘을 병행 제공하라. 또한 줄바꿈 등 특수 스타일은 전역 선택자(`a`) 대신 `.ext-link`와 같은 전용 유틸리티 클래스로 스코프를 제한하라. diff --git a/scanner/dashboard/index.html b/scanner/dashboard/index.html index 132bc31b2..1adc2c4cb 100644 --- a/scanner/dashboard/index.html +++ b/scanner/dashboard/index.html @@ -82,7 +82,11 @@ body.drag-active::after{content:"Drop findings.json here";position:fixed;inset:0;z-index:99;background:rgba(37,110,244,.08);border:4px dashed var(--primary);display:flex;align-items:center;justify-content:center;font-size:24px;font-weight:700;color:var(--primary);pointer-events:none;backdrop-filter:blur(2px)} .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0} a{color:var(--primary)} - .skip-link{position:absolute;top:-48px;left:0;background:var(--primary);color:#fff;padding:8px 16px;z-index:100;font-weight:700;border-radius:0 0 8px 0;transition:top .2s}\n .skip-link:focus{top:0;outline:3px solid #fff;outline-offset:2px;box-shadow:0 0 0 5px var(--primary)}\n + .skip-link{position:absolute;top:-48px;left:0;background:var(--primary);color:#fff;padding:8px 16px;z-index:100;font-weight:700;border-radius:0 0 8px 0;transition:top .2s} + .skip-link:focus{top:0;outline:3px solid #fff;outline-offset:2px;box-shadow:0 0 0 5px var(--primary)} + .ext-link{display:inline-flex;align-items:center;gap:4px;word-break:break-all} + .ext-icon{width:12px;height:12px;fill:currentColor;flex-shrink:0} + @@ -281,7 +285,7 @@

Dashboard

function openDetail(f){ lastFocus = document.activeElement; const s = String(f.severity||'INFO').toUpperCase(); - const refs = (f.references||[]).map(r=>`${esc(r)}`).join('
'); + const refs = (f.references||[]).map(r=>`${esc(r)}(새 탭에서 열림)`).join('
'); const owasp = (f.owasp||[]).join(', '); const cwe = (f.cwe||[]).join(', '); const d = document.getElementById('detail'); From 5fdc3723384d3dc12fe98e6317050be821ef7d25 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 3 Oct 2026 18:53:11 +0900 Subject: [PATCH 2/3] chore: retire duplicate .jules/palette.md delta in favor of #1361 --- .jules/palette.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.jules/palette.md b/.jules/palette.md index b20cb4c3b..ea004e2d2 100644 --- a/.jules/palette.md +++ b/.jules/palette.md @@ -81,7 +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. - -## 2026-10-24 - 외부 링크 접근성 강화 및 스타일 스코핑 -**Learning:** `target="_blank"` 속성을 가진 외부 링크에 시각적(아이콘) 표시는 제공하지만, 스크린 리더 등 보조 기기 사용자를 위한 텍스트 경고(`새 탭에서 열림` 등)를 누락하거나, 긴 URL 단어를 강제 줄바꿈하기 위한 `word-break: break-all`을 링크 전체에 전역으로 적용하면 UI 레이아웃이 무너지거나 접근성이 저하된다. -**Action:** 외부 링크에는 항상 화면에 보이지 않는 `.sr-only` 클래스의 텍스트 설명과, 보조 기기에서 무시되는 `aria-hidden="true"` 아이콘을 병행 제공하라. 또한 줄바꿈 등 특수 스타일은 전역 선택자(`a`) 대신 `.ext-link`와 같은 전용 유틸리티 클래스로 스코프를 제한하라. From 1687847dbef9d228b7587c779930cdff3e2f596d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 3 Oct 2026 18:53:13 +0900 Subject: [PATCH 3/3] chore: retire duplicate scanner/dashboard/index.html delta in favor of #1361 --- scanner/dashboard/index.html | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/scanner/dashboard/index.html b/scanner/dashboard/index.html index 1adc2c4cb..132bc31b2 100644 --- a/scanner/dashboard/index.html +++ b/scanner/dashboard/index.html @@ -82,11 +82,7 @@ body.drag-active::after{content:"Drop findings.json here";position:fixed;inset:0;z-index:99;background:rgba(37,110,244,.08);border:4px dashed var(--primary);display:flex;align-items:center;justify-content:center;font-size:24px;font-weight:700;color:var(--primary);pointer-events:none;backdrop-filter:blur(2px)} .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0} a{color:var(--primary)} - .skip-link{position:absolute;top:-48px;left:0;background:var(--primary);color:#fff;padding:8px 16px;z-index:100;font-weight:700;border-radius:0 0 8px 0;transition:top .2s} - .skip-link:focus{top:0;outline:3px solid #fff;outline-offset:2px;box-shadow:0 0 0 5px var(--primary)} - .ext-link{display:inline-flex;align-items:center;gap:4px;word-break:break-all} - .ext-icon{width:12px;height:12px;fill:currentColor;flex-shrink:0} - + .skip-link{position:absolute;top:-48px;left:0;background:var(--primary);color:#fff;padding:8px 16px;z-index:100;font-weight:700;border-radius:0 0 8px 0;transition:top .2s}\n .skip-link:focus{top:0;outline:3px solid #fff;outline-offset:2px;box-shadow:0 0 0 5px var(--primary)}\n @@ -285,7 +281,7 @@

Dashboard

function openDetail(f){ lastFocus = document.activeElement; const s = String(f.severity||'INFO').toUpperCase(); - const refs = (f.references||[]).map(r=>`${esc(r)}(새 탭에서 열림)`).join('
'); + const refs = (f.references||[]).map(r=>`${esc(r)}`).join('
'); const owasp = (f.owasp||[]).join(', '); const cwe = (f.cwe||[]).join(', '); const d = document.getElementById('detail');