Skip to content

Commit 36a2bc4

Browse files
feat(scratchpad): improve footer UX and accessibility
- Add line count to the scratchpad footer ("X chars, Y lines"). - Remove `aria-live` from character count to prevent screen reader spam. - Fix focus management: keep focus on "Copy" and "Remove Empty Lines" buttons after click to allow status updates to be announced and prevent disorienting focus jumps. - Update `.Jules/palette.md` with focus management learning.
1 parent ba2d7d9 commit 36a2bc4

2 files changed

Lines changed: 7 additions & 6 deletions

File tree

.Jules/palette.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,7 @@
99
## 2024-05-27 - Clipboard Reliability in Webviews
1010
**Learning:** `navigator.clipboard.writeText` in VS Code Webviews is flaky because it requires the document to be focused, which isn't guaranteed if the user just clicked a button.
1111
**Action:** Always delegate clipboard operations to the extension host via `postMessage` and `vscode.env.clipboard.writeText` for robust behavior.
12+
13+
## 2025-10-26 - Scratchpad Focus Management
14+
**Learning:** In VS Code Webviews, automatically returning focus to the main input (e.g. textarea) after a button click (like "Copy" or "Remove Empty Lines") can be disorienting for screen reader users and keyboard navigators. It interrupts the natural tab order and prevents users from hearing status updates on the button they just clicked.
15+
**Action:** Avoid calling `.focus()` on the input element immediately after secondary actions unless the primary purpose of the action is to prepare for immediate typing. For status updates (like "Copied!"), keep focus on the trigger element.

src/features/scratchpad/scratchpad.html

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@
108108
spellcheck="false"
109109
></textarea>
110110
<div class="footer">
111-
<span id="char-count" class="char-count" aria-live="polite">0 chars</span>
111+
<span id="char-count" class="char-count">0 chars, 0 lines</span>
112112
<div class="actions">
113113
<button id="btn-copy" class="btn btn-primary" aria-label="Copy to Clipboard" title="Copy content to clipboard">
114114
Copy
@@ -143,7 +143,8 @@
143143

144144
function updateCharCount() {
145145
const length = textarea.value.length;
146-
charCount.textContent = `${length} char${length !== 1 ? 's' : ''}`;
146+
const lines = length === 0 ? 0 : textarea.value.split('\n').length;
147+
charCount.textContent = `${length} char${length !== 1 ? 's' : ''}, ${lines} line${lines !== 1 ? 's' : ''}`;
147148
}
148149

149150
function resetClearButton() {
@@ -203,8 +204,6 @@
203204

204205
if (copyTimeoutId) clearTimeout(copyTimeoutId);
205206
copyTimeoutId = setTimeout(resetCopyButton, 2000);
206-
207-
textarea.focus();
208207
});
209208

210209
// Clear Button Logic
@@ -256,8 +255,6 @@
256255

257256
if (removeEmptyTimeoutId) clearTimeout(removeEmptyTimeoutId);
258257
removeEmptyTimeoutId = setTimeout(resetRemoveEmptyButton, 2000);
259-
260-
textarea.focus();
261258
});
262259

263260
textarea.addEventListener('focus', resetClearButton);

0 commit comments

Comments
 (0)