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-05-24 - Preserving Nested HTML Tags in DOM State Transitions
**Learning:** When updating elements that contain nested HTML tags (like `<kbd>` hints inside a button), using `element.textContent` to save and restore state will strip the HTML tags, leading to a loss of visual hints and structure.
**Action:** Use `Array.from(element.childNodes)` to save the complete node structure and `element.replaceChildren(...nodes)` to restore it safely. This preserves elements like `<kbd>` without risking XSS vulnerabilities that come with using `innerHTML`.
20 changes: 15 additions & 5 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-uVzoCdfzyM08tb2ZA7I03BhunQZJ4IzTzVopz7VupaA='; 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,7 +133,7 @@ <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 = '';
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,16 @@ <h1>Welcome to 5ive</h1>
}, 5000);
}, 1500);
});

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