Skip to content
Open
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
3 changes: 3 additions & 0 deletions .Jules/palette.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,6 @@
## 2026-06-09 - Empty State Feedback
**Learning:** When a list or process finishes with zero results, an empty summary container gives no feedback, leaving the user wondering if it actually ran or failed silently.
**Action:** Always provide a helpful empty state message explaining why no results might have occurred, using styles consistent with other hints to offer guidance without showing as an error.
## 2025-02-18 - Semantic Form Group Accessibility
**Learning:** Generic `<div>` containers combined with `role="group"` or `role="radiogroup"` and `aria-labelledby` attributes are often flagged by linters (like Biome) and can be unnecessarily complex. Native semantic HTML `<fieldset>` and `<legend>` elements implicitly provide this accessibility structure without requiring explicit ARIA roles.
**Action:** Default to using `<fieldset>` and `<legend>` when grouping related form controls (like radio buttons or segmented controls). Remember to apply a basic CSS reset (`border: none; padding: 0; margin: 0;`) to `<fieldset>` and style `<legend>` to match `<label>` to maintain the intended visual design.
9 changes: 8 additions & 1 deletion popup.css
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ section {
border-radius: 6px;
}

label {
label,
legend {
display: block;
font-size: 12px;
color: #94a3b8;
Expand Down Expand Up @@ -260,3 +261,9 @@ button.secondary:hover {
.summary .skipped {
color: #fbbf24;
}

fieldset {
border: none;
padding: 0;
margin: 0;
}
24 changes: 12 additions & 12 deletions popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,34 +13,34 @@ <h1>Jules Task Archiver</h1>

<section class="options">
<h2>Options</h2>
<div class="setting-row">
<label id="opModeLabel">Operation</label>
<div class="segmented" id="opMode" role="group" aria-labelledby="opModeLabel">
<fieldset class="setting-row">
<legend id="opModeLabel">Operation</legend>
<div class="segmented" id="opMode">
<button type="button" data-value="archive" class="active" aria-pressed="true">Archive Tasks</button>
<button type="button" data-value="suggestions" aria-pressed="false">Start Suggestions</button>
</div>
</div>
<div class="setting-row">
<label id="execModeLabel">Execution Mode</label>
<div class="radio-group" role="radiogroup" aria-labelledby="execModeLabel">
</fieldset>
<fieldset class="setting-row">
<legend id="execModeLabel">Execution Mode</legend>
<div class="radio-group">
<label><input type="radio" name="mode" value="dry" checked /> Dry Run</label>
<label><input type="radio" name="mode" value="run" /> Run</label>
</div>
</div>
</fieldset>
<div class="setting-row">
<label class="checkbox">
<input type="checkbox" id="force" aria-describedby="forceHint" />
Force
</label>
<span class="hint" id="forceHint" style="display: block; margin-left: 20px; margin-top: 2px;">Archive every task, ignoring state and matching open Pull Requests</span>
</div>
<div class="setting-row">
<label id="scopeLabel">Scope</label>
<div class="radio-group" role="radiogroup" aria-labelledby="scopeLabel">
<fieldset class="setting-row">
<legend id="scopeLabel">Scope</legend>
<div class="radio-group">
<label><input type="radio" name="scope" value="current" /> Current tab</label>
<label><input type="radio" name="scope" value="all" checked /> All Jules tabs</label>
</div>
</div>
</fieldset>
</section>

<section class="settings">
Expand Down
4 changes: 2 additions & 2 deletions tests/popup.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -437,9 +437,9 @@ describe('popup.html accessibility', () => {

it('should use explicit visible labels for radio groups via aria-labelledby', () => {
assert.ok(popupHtml.includes('id="execModeLabel"'), 'execModeLabel should exist')
assert.ok(popupHtml.includes('aria-labelledby="execModeLabel"'), 'mode radiogroup should use aria-labelledby')
// // assert.ok(popupHtml.includes('aria-labelledby="execModeLabel"'), 'mode radiogroup should use aria-labelledby') // Removed as fieldset provides native grouping // Removed as fieldset provides native grouping
assert.ok(popupHtml.includes('id="scopeLabel"'), 'scopeLabel should exist')
assert.ok(popupHtml.includes('aria-labelledby="scopeLabel"'), 'scope radiogroup should use aria-labelledby')
// // assert.ok(popupHtml.includes('aria-labelledby="scopeLabel"'), 'scope radiogroup should use aria-labelledby') // Removed as fieldset provides native grouping // Removed as fieldset provides native grouping
})
})

Expand Down