Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/palette.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2024-05-23 - Accessible Toast Notifications
**Learning:** Toast notifications often disappear too quickly for some users. Implementing a 5000ms minimum duration AND a manual close button ensures compliance with accessibility standards (WCAG 2.2.1 Timing Adjustable) and improves usability for everyone.
**Action:** When implementing temporary feedback messages, always include a visual close button and ensure the timeout is sufficient (>= 5000ms), or allow user preference to extend it.

## 2024-04-19 - Keyboard Shortcut Hints & Preserving DOM Structure
**Learning:** When adding visual `<kbd>` hints inside interactive elements (like buttons), updating state using `textContent` destroys the nested HTML structure, removing the hints when state is restored. Furthermore, relying on `innerHTML` introduces XSS vulnerabilities (blocked by strict CSPs).
**Action:** When updating DOM elements that contain nested HTML tags, preserve structural integrity by caching child nodes via `Array.from(element.childNodes)` and resetting state using `element.replaceChildren(...cachedNodes)`. This safely maintains visual hints without violating security baselines.
20 changes: 14 additions & 6 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; style-src 'self' 'sha256-fgxmLOznNmVf4GAd24jy5Eiv/Rer+7sVLOBqnsVx0nY='; script-src 'self' 'sha256-cPRnZP+O4z5KsN+vdFstQwgKExGtoN98I3Gq+Tm1aSA='; object-src 'none'; base-uri 'self'; upgrade-insecure-requests;">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; style-src 'self' 'sha256-fgxmLOznNmVf4GAd24jy5Eiv/Rer+7sVLOBqnsVx0nY='; script-src 'self' 'sha256-XXdtzpeaptfGxeSgDjszwviQPcTdTPR0gagSTcyEbjA='; object-src 'none'; base-uri 'self'; upgrade-insecure-requests;">
<meta name="referrer" content="no-referrer">
<title>5ive - UX Sample</title>
<style>
Expand Down Expand Up @@ -109,8 +109,8 @@ <h1>Welcome to 5ive</h1>
<p>Sample accessible button component:</p>

<!-- ✅ GOOD UX: Semantic button with proper labeling and focus handling -->
<button type="button" class="btn" id="action-btn">
Click Me (Accessible)
<button type="button" class="btn" id="action-btn" aria-keyshortcuts="Control+Enter Meta+Enter">
Click Me (Accessible) <kbd>Ctrl+Enter</kbd>
</button>

<div id="feedback" class="feedback" aria-live="polite"></div>
Expand All @@ -133,10 +133,10 @@ <h1>Welcome to 5ive</h1>

// Set loading state
// ✅ Sentinel: Avoid innerHTML to prevent XSS
const originalText = actionBtn.textContent;
const originalNodes = Array.from(actionBtn.childNodes);
actionBtn.disabled = true;
actionBtn.setAttribute('aria-busy', 'true');
actionBtn.textContent = '';
actionBtn.replaceChildren();

const spinner = document.createElement('span');
spinner.className = 'spinner';
Expand All @@ -149,7 +149,7 @@ <h1>Welcome to 5ive</h1>
// Reset state
actionBtn.disabled = false;
actionBtn.removeAttribute('aria-busy');
actionBtn.textContent = originalText;
actionBtn.replaceChildren(...originalNodes);

// Show success with icon and transition
// ✅ Sentinel: Using textContent and DOM methods instead of innerHTML
Expand Down Expand Up @@ -198,6 +198,14 @@ <h1>Welcome to 5ive</h1>
}, 5000);
}, 1500);
});

document.addEventListener('keydown', (e) => {
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
e.preventDefault();
actionBtn.focus();
actionBtn.click();
}
});
</script>
</body>
</html>