🎨 Palette: Improve Search Empty States & Accessibility#103
🎨 Palette: Improve Search Empty States & Accessibility#103SlasshyOverhere wants to merge 1 commit into
Conversation
- Add `aria-live="polite"` to `#entryList` and `#detailHint` to ensure screen readers announce dynamic text changes without page reloads. - Introduce an actionable "Clear Filters" button in the empty state when search filters yield no results, preventing dead-end UI states. - Add an `Escape` keyboard shortcut to clear search and type filters quickly via keyboard.
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
📝 WalkthroughWalkthroughThis PR enhances web UI accessibility by introducing ARIA live region announcements for dynamic list updates, adding a clear filters button in empty states when search/filters are active, and implementing an Escape key shortcut to clear the search input. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This PR updates the BlankDrive Web UI template to improve accessibility announcements for dynamic UI regions and to make “no search results” states easier to recover from.
Changes:
- Added
aria-live="polite"to the entry list and detail hint regions to announce client-side state changes to screen readers. - Enhanced the “no entries” empty state to show a “Clear Filters” action when the view is filtered.
- Added an
Escapekey handler on the search input to clear and unfocus the search box.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| src/webui/template.ts | Adds aria-live, improves filtered empty state UX (Clear Filters), and introduces Escape-to-clear behavior on search. |
| .jules/palette.md | Updates internal Jules palette notes with guidance about these UX/accessibility changes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if(!s.entries.length){ | ||
| const li=document.createElement('li'); | ||
| li.className='hint empty-state'; | ||
| const isFiltered = !!el.search.value || el.typeFilter.value !== 'all'; |
There was a problem hiding this comment.
isFiltered treats whitespace in the search box as an active filter (!!el.search.value), but entryFilters() trims the query before sending it. This can produce a confusing empty-state message ("No entries match…") even when the effective query is empty. Use the same trimmed logic as entryFilters() when computing isFiltered.
| const isFiltered = !!el.search.value || el.typeFilter.value !== 'all'; | |
| const isFiltered = !!el.search.value.trim() || el.typeFilter.value !== 'all'; |
| clearBtn.className = 'btn outline'; | ||
| clearBtn.style.marginTop = '10px'; |
There was a problem hiding this comment.
The new Clear Filters button uses className = 'btn outline', but the stylesheet in this template defines btn-primary, btn-ghost, btn-danger, etc., and does not define btn/outline. As a result this button likely won't match existing button styling. Reuse an existing button class (e.g., btn-ghost) or add matching CSS for the new classes; also consider avoiding the inline style.marginTop in favor of a CSS rule.
| clearBtn.className = 'btn outline'; | |
| clearBtn.style.marginTop = '10px'; | |
| clearBtn.className = 'btn-ghost'; |
| el.search.addEventListener('keydown',(e)=>{ | ||
| if(e.key==='Escape') { | ||
| el.search.value=''; | ||
| el.search.blur(); |
There was a problem hiding this comment.
The Escape handler triggers an immediate refreshEntries() but does not cancel any pending debounced refresh from the input handler (searchTimer). If the user presses Escape soon after typing, this can cause back-to-back network requests. Clear any pending searchTimer (and reset it) before calling refreshEntries() here.
| el.search.blur(); | |
| el.search.blur(); | |
| if(searchTimer){clearTimeout(searchTimer);searchTimer=null} |
| ## 2026-04-06 - Improve Accessibility and Usability of Search Empty States | ||
| **Learning:** For text-based UI updates in single-page apps (like toggling an empty state list item), explicitly applying `aria-live="polite"` ensures screen readers announce the changes, which is critical since they happen client-side without a page reload. Additionally, empty states that result from a filtered view (like search) should always provide an actionable UI path to reset the view (e.g., a "Clear Filters" button), rather than forcing the user to manually clear their inputs to recover. | ||
| **Action:** When creating text updates for dynamically rendered lists, always add `aria-live` to the list container. Always combine empty states for filtered lists with an explicit reset action like a button or shortcut hint. |
There was a problem hiding this comment.
Repo convention is to avoid committing generated/internal .jules/* artifacts in merged PRs (see CHANGELOG.md noting their removal). This file update should be excluded from the PR or moved to a non-internal docs location if it needs to be kept.
| ## 2026-04-06 - Improve Accessibility and Usability of Search Empty States | |
| **Learning:** For text-based UI updates in single-page apps (like toggling an empty state list item), explicitly applying `aria-live="polite"` ensures screen readers announce the changes, which is critical since they happen client-side without a page reload. Additionally, empty states that result from a filtered view (like search) should always provide an actionable UI path to reset the view (e.g., a "Clear Filters" button), rather than forcing the user to manually clear their inputs to recover. | |
| **Action:** When creating text updates for dynamically rendered lists, always add `aria-live` to the list container. Always combine empty states for filtered lists with an explicit reset action like a button or shortcut hint. |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/webui/template.ts (1)
1103-1109: Avoid duplicate refreshes on Escape in search.On Line 1104+, Escape should clear any pending debounce and prevent native/default Escape behavior to avoid extra
refreshEntries()calls.💡 Suggested fix
el.search.addEventListener('keydown',(e)=>{ if(e.key==='Escape') { + e.preventDefault(); + if(searchTimer) clearTimeout(searchTimer); el.search.value=''; el.search.blur(); void refreshEntries(); } });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/webui/template.ts` around lines 1103 - 1109, Escape handling on the search input currently clears the value and calls refreshEntries but can trigger duplicate refreshes due to pending debounce or native Escape behavior; update the el.search keydown handler to call e.preventDefault() and e.stopPropagation(), cancel/clear any pending search debounce (e.g., clearTimeout(searchDebounceTimer) or call the debounce cancellation helper used for the search), then clear el.search.value, blur the input, and call refreshEntries() once so no duplicate refreshes occur. Ensure you update the handler attached via el.search.addEventListener and reference the existing debounce cancellation symbol used in this module (or create one if missing).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/webui/template.ts`:
- Around line 1026-1030: The Clear Filters button uses a non-existent variant
class "btn outline" causing styling drift; update the className on the clearBtn
element to use an existing button variant defined in this file (e.g., the same
class string used by other buttons like the primary/secondary/outline variant in
this template) so it inherits the dark-theme styles — locate the clearBtn
creation (variable clearBtn) and replace its className assignment with the
canonical button class used elsewhere in this module.
---
Nitpick comments:
In `@src/webui/template.ts`:
- Around line 1103-1109: Escape handling on the search input currently clears
the value and calls refreshEntries but can trigger duplicate refreshes due to
pending debounce or native Escape behavior; update the el.search keydown handler
to call e.preventDefault() and e.stopPropagation(), cancel/clear any pending
search debounce (e.g., clearTimeout(searchDebounceTimer) or call the debounce
cancellation helper used for the search), then clear el.search.value, blur the
input, and call refreshEntries() once so no duplicate refreshes occur. Ensure
you update the handler attached via el.search.addEventListener and reference the
existing debounce cancellation symbol used in this module (or create one if
missing).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8c2c73ff-7563-4556-8131-479c1ddb9132
📒 Files selected for processing (2)
.jules/palette.mdsrc/webui/template.ts
| const clearBtn = document.createElement('button'); | ||
| clearBtn.type = 'button'; | ||
| clearBtn.className = 'btn outline'; | ||
| clearBtn.style.marginTop = '10px'; | ||
| clearBtn.textContent = 'Clear Filters'; |
There was a problem hiding this comment.
Use an existing button variant for “Clear Filters.”
On Line 1028, btn outline doesn’t map to styles defined in this file, so the control may render with inconsistent/default browser styling in the dark theme.
💡 Suggested fix
- clearBtn.className = 'btn outline';
+ clearBtn.className = 'btn-ghost btn-sm';📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const clearBtn = document.createElement('button'); | |
| clearBtn.type = 'button'; | |
| clearBtn.className = 'btn outline'; | |
| clearBtn.style.marginTop = '10px'; | |
| clearBtn.textContent = 'Clear Filters'; | |
| const clearBtn = document.createElement('button'); | |
| clearBtn.type = 'button'; | |
| clearBtn.className = 'btn-ghost btn-sm'; | |
| clearBtn.style.marginTop = '10px'; | |
| clearBtn.textContent = 'Clear Filters'; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/webui/template.ts` around lines 1026 - 1030, The Clear Filters button
uses a non-existent variant class "btn outline" causing styling drift; update
the className on the clearBtn element to use an existing button variant defined
in this file (e.g., the same class string used by other buttons like the
primary/secondary/outline variant in this template) so it inherits the
dark-theme styles — locate the clearBtn creation (variable clearBtn) and replace
its className assignment with the canonical button class used elsewhere in this
module.
This commit introduces micro-UX improvements to the BlankDrive Web UI to enhance accessibility and the empty state experience.
What was added:
aria-live="polite"attributes to dynamic areas (#entryListand#detailHint) so screen readers get state updates.Escapekey on the search input to quickly clear and unfocus it.Why:
aria-live, users relying on screen readers wouldn't know when their search yielded zero results.PR created automatically by Jules for task 13279054375330315754 started by @SlasshyOverhere
Summary by CodeRabbit
New Features
Documentation
Accessibility