feat: add aria labels and state attributes to icon buttons

- Added `aria-label` to all icon-only buttons in the Info Panel and Magnify Glass toggle.
- Implemented dynamic `aria-pressed` state updates for toggle buttons (pin, lock, persist, etc.).
- Added `aria-expanded` state for the minimize/maximize button.
- Added `aria-label` to the copy-to-clipboard button.
- Ensures better screen reader compatibility and accessibility compliance.
This commit is contained in:
google-labs-jules[bot]
2026-01-14 04:37:02 +00:00
parent 9677722f43
commit fe8df50f48
6 changed files with 54 additions and 28 deletions
+22 -12
View File
@@ -69,7 +69,7 @@ export class UIManager {
<div class="header-subtitle">Real-time analysis</div>
</div>
<div class="header-controls">
<button class="control-btn minimize-btn" title="Minimize Panel" data-action="minimize">${Icons.minus}</button>
<button class="control-btn minimize-btn" title="Minimize Panel" aria-label="Minimize Panel" aria-expanded="true" data-action="minimize">${Icons.minus}</button>
</div>
`;
@@ -220,16 +220,16 @@ export class UIManager {
* - Pin icon = pin position (prevent drag)
*/
this.elements.controls.innerHTML = `
<button class="control-btn unlock-btn" title="Unlock/Lock Panel from Glass" data-action="pin">${Icons.unlock}</button>
<button class="control-btn pin-btn" title="Pin/Unpin Panel Position (Prevent Drag)" data-action="lock">${Icons.pin}</button>
<button class="control-btn persist-btn" title="Toggle Persist Mode - Sticky Info (S)" data-action="persist">${Icons.magnet}</button>
<button class="control-btn hold-btn" title="Hold Info - Pause/Play (P)" data-action="toggle-hold">${Icons.pause}</button>
<button class="control-btn visibility-btn" title="Toggle Panel Visibility (I)" data-action="toggle-panel">${Icons.eye}</button>
<button class="control-btn glass-btn" title="Toggle Glass Preview (G)" data-action="toggle-glass">${Icons.magnifyGlass}</button>
<button class="control-btn cursor-btn" title="Toggle Cursor Preview" data-action="toggle-cursor">${Icons.cursor}</button>
<button class="control-btn drag-glass-btn" title="Move Glass Position (H)" data-action="drag-glass">${Icons.move}</button>
<button class="control-btn reset-glass-btn" title="Reset Glass Position (O)" data-action="reset-glass">${Icons.reset}</button>
<button class="control-btn popout-btn" title="Open in New Tab (Shift+P)" data-action="popout">${Icons.externalLink}</button>
<button class="control-btn unlock-btn" title="Unlock/Lock Panel from Glass" aria-label="Unlock or Lock Panel from Glass" aria-pressed="false" data-action="pin">${Icons.unlock}</button>
<button class="control-btn pin-btn" title="Pin/Unpin Panel Position (Prevent Drag)" aria-label="Pin or Unpin Panel Position" aria-pressed="false" data-action="lock">${Icons.pin}</button>
<button class="control-btn persist-btn" title="Toggle Persist Mode - Sticky Info (S)" aria-label="Toggle Sticky Info" aria-pressed="false" data-action="persist">${Icons.magnet}</button>
<button class="control-btn hold-btn" title="Hold Info - Pause/Play (P)" aria-label="Hold Info" aria-pressed="false" data-action="toggle-hold">${Icons.pause}</button>
<button class="control-btn visibility-btn" title="Toggle Panel Visibility (I)" aria-label="Toggle Panel Visibility" aria-pressed="true" data-action="toggle-panel">${Icons.eye}</button>
<button class="control-btn glass-btn" title="Toggle Glass Preview (G)" aria-label="Toggle Glass Preview" aria-pressed="true" data-action="toggle-glass">${Icons.magnifyGlass}</button>
<button class="control-btn cursor-btn" title="Toggle Cursor Preview" aria-label="Toggle Cursor Preview" aria-pressed="false" data-action="toggle-cursor">${Icons.cursor}</button>
<button class="control-btn drag-glass-btn" title="Move Glass Position (H)" aria-label="Move Glass Position" aria-pressed="false" data-action="drag-glass">${Icons.move}</button>
<button class="control-btn reset-glass-btn" title="Reset Glass Position (O)" aria-label="Reset Glass Position" data-action="reset-glass">${Icons.reset}</button>
<button class="control-btn popout-btn" title="Open in New Tab (Shift+P)" aria-label="Open in New Tab" aria-pressed="false" data-action="popout">${Icons.externalLink}</button>
`;
// Insert before the panel in the document body, not as a child
@@ -402,6 +402,7 @@ export class UIManager {
if (pinBtn) {
pinBtn.classList.toggle('active', this.stateManager.state.isPanelPinned);
pinBtn.setAttribute('aria-pressed', String(this.stateManager.state.isPanelPinned));
pinBtn.title = this.stateManager.state.isPanelPinned ? "Lock Panel" : "Unlock Panel";
pinBtn.innerHTML = this.stateManager.state.isPanelPinned ? Icons.lock : Icons.unlock;
// Hide unlock button when panel is hidden
@@ -424,6 +425,7 @@ export class UIManager {
const showLockBtn = isPanelVisible && this.stateManager.state.isPanelPinned;
lockBtn.style.display = showLockBtn ? 'flex' : 'none';
lockBtn.classList.toggle('active', this.stateManager.state.isPanelLocked);
lockBtn.setAttribute('aria-pressed', String(this.stateManager.state.isPanelLocked));
lockBtn.title = this.stateManager.state.isPanelLocked ? "Unpin Panel Position" : "Pin Panel Position";
lockBtn.disabled = !this.stateManager.state.isPanelPinned;
}
@@ -432,6 +434,7 @@ export class UIManager {
if (persistBtn) {
const isPersistConfigured = !!this.stateManager.state.settings["🔍MagnifyGlass.InfoPanelPersist"];
persistBtn.classList.toggle('active', isPersistConfigured);
persistBtn.setAttribute('aria-pressed', String(isPersistConfigured));
persistBtn.title = isPersistConfigured ? "Disable Sticky Info (S)" : "Enable Sticky Info (S)";
persistBtn.style.display = isPanelVisible ? 'flex' : 'none';
}
@@ -445,6 +448,7 @@ export class UIManager {
holdBtn.style.display = (isPanelVisible && isPersistConfigured) ? 'flex' : 'none';
holdBtn.classList.toggle('active', isHeld);
holdBtn.setAttribute('aria-pressed', String(isHeld));
// Toggle icon: Pause (to hold) vs Play (to resume)
holdBtn.innerHTML = isHeld ? Icons.play : Icons.pause;
holdBtn.title = isHeld ? "Resume Info Update (P)" : "Pause Info Update (P)";
@@ -453,6 +457,7 @@ export class UIManager {
if (visibilityBtn) {
// Active means "Panel is Visible"
visibilityBtn.classList.toggle('active', isPanelVisible);
visibilityBtn.setAttribute('aria-pressed', String(isPanelVisible));
visibilityBtn.title = isPanelVisible ? "Hide Panel" : "Show Panel";
// Disable hide button if glass is hidden (prevent hiding everything)
@@ -468,6 +473,7 @@ export class UIManager {
if (glassBtn) {
glassBtn.classList.toggle('active', isGlassVisible);
glassBtn.setAttribute('aria-pressed', String(isGlassVisible));
glassBtn.title = isGlassVisible ? "Hide Glass Preview" : "Show Glass Preview";
// Only show glass toggle button if the inspector panel is visible
@@ -479,6 +485,7 @@ export class UIManager {
const cglass = (window as any).comfyUIMagnifyGlass;
const showCursor = cglass?.config?.showCursorPreview || false;
cursorBtn.classList.toggle('active', showCursor);
cursorBtn.setAttribute('aria-pressed', String(showCursor));
cursorBtn.title = showCursor ? "Hide Cursor Preview" : "Show Cursor Preview";
// Logic: Visible when glass is visible AND:
@@ -494,6 +501,7 @@ export class UIManager {
const mglass = (window as any).comfyUIMagnifyGlass;
const isDragMode = mglass?.state?.isDragModeEnabled || false;
dragGlassBtn.classList.toggle('active', isDragMode);
dragGlassBtn.setAttribute('aria-pressed', String(isDragMode));
dragGlassBtn.title = isDragMode ? "Cancel Move Mode (H)" : "Move Glass Position (H)";
// Only show drag button when glass is visible (even if panel is hidden)
dragGlassBtn.style.display = isGlassVisible ? 'flex' : 'none';
@@ -511,6 +519,7 @@ export class UIManager {
const isOpen = glass?.popOutManager?.isPopOutOpen() || false;
// Logger.debug(`[UIManager] Popout button update - Glass: ${!!glass}, IsOpen: ${isOpen}`);
popoutBtn.classList.toggle('active', isOpen);
popoutBtn.setAttribute('aria-pressed', String(isOpen));
popoutBtn.title = isOpen ? "Close Pop-out Viewer" : "Open Pop-out Viewer";
}
}
@@ -675,6 +684,7 @@ export class UIManager {
if (minimizeBtn) {
minimizeBtn.textContent = this.stateManager.state.isPanelMinimized ? '+' : '−';
(minimizeBtn as HTMLElement).title = this.stateManager.state.isPanelMinimized ? 'Expand Panel' : 'Minimize Panel';
minimizeBtn.setAttribute('aria-expanded', String(!this.stateManager.state.isPanelMinimized));
}
}
@@ -894,7 +904,7 @@ export class UIManager {
const rawValue = String(item.value || '');
const showCopyBtn = !item.clickable && rawValue.length > 3 && typeof item.value === 'string';
const copyBtnHtml = showCopyBtn ? `
<button class="copy-btn" data-copy-value="${rawValue.replace(/"/g, '&quot;')}" title="Copy to clipboard">
<button class="copy-btn" data-copy-value="${rawValue.replace(/"/g, '&quot;')}" title="Copy to clipboard" aria-label="Copy to clipboard">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
+4 -1
View File
@@ -441,6 +441,8 @@ export class UiManager {
btn.style.width = computed.width; // Match width for consistency
btn.title = "Toggle Magnify Glass (X)";
btn.setAttribute('aria-label', "Toggle Magnify Glass");
btn.setAttribute('aria-pressed', 'false');
btn.setAttribute('data-testid', 'toggle-magnify-glass-button');
btn.innerHTML = Icons.magnifyGlass;
@@ -463,9 +465,10 @@ export class UiManager {
if (this.onToggle) {
this.onToggle();
// Toggle active states to match ComfyUI's button styling
btn.classList.toggle('active');
const isActive = btn.classList.toggle('active');
btn.classList.toggle('p-highlight'); // PrimeVue
btn.classList.toggle('selected');
btn.setAttribute('aria-pressed', String(isActive));
}
});
+22 -12
View File
@@ -40,7 +40,7 @@ class UIManager {
<div class="header-subtitle">Real-time analysis</div>
</div>
<div class="header-controls">
<button class="control-btn minimize-btn" title="Minimize Panel" data-action="minimize">${Icons.minus}</button>
<button class="control-btn minimize-btn" title="Minimize Panel" aria-label="Minimize Panel" aria-expanded="true" data-action="minimize">${Icons.minus}</button>
</div>
`;
this.elements.content = document.createElement("div");
@@ -132,16 +132,16 @@ class UIManager {
this.elements.controls = document.createElement("div");
this.elements.controls.className = `floating-controls vertical-layout theme-${this.stateManager.state.currentTheme}`;
this.elements.controls.innerHTML = `
<button class="control-btn unlock-btn" title="Unlock/Lock Panel from Glass" data-action="pin">${Icons.unlock}</button>
<button class="control-btn pin-btn" title="Pin/Unpin Panel Position (Prevent Drag)" data-action="lock">${Icons.pin}</button>
<button class="control-btn persist-btn" title="Toggle Persist Mode - Sticky Info (S)" data-action="persist">${Icons.magnet}</button>
<button class="control-btn hold-btn" title="Hold Info - Pause/Play (P)" data-action="toggle-hold">${Icons.pause}</button>
<button class="control-btn visibility-btn" title="Toggle Panel Visibility (I)" data-action="toggle-panel">${Icons.eye}</button>
<button class="control-btn glass-btn" title="Toggle Glass Preview (G)" data-action="toggle-glass">${Icons.magnifyGlass}</button>
<button class="control-btn cursor-btn" title="Toggle Cursor Preview" data-action="toggle-cursor">${Icons.cursor}</button>
<button class="control-btn drag-glass-btn" title="Move Glass Position (H)" data-action="drag-glass">${Icons.move}</button>
<button class="control-btn reset-glass-btn" title="Reset Glass Position (O)" data-action="reset-glass">${Icons.reset}</button>
<button class="control-btn popout-btn" title="Open in New Tab (Shift+P)" data-action="popout">${Icons.externalLink}</button>
<button class="control-btn unlock-btn" title="Unlock/Lock Panel from Glass" aria-label="Unlock or Lock Panel from Glass" aria-pressed="false" data-action="pin">${Icons.unlock}</button>
<button class="control-btn pin-btn" title="Pin/Unpin Panel Position (Prevent Drag)" aria-label="Pin or Unpin Panel Position" aria-pressed="false" data-action="lock">${Icons.pin}</button>
<button class="control-btn persist-btn" title="Toggle Persist Mode - Sticky Info (S)" aria-label="Toggle Sticky Info" aria-pressed="false" data-action="persist">${Icons.magnet}</button>
<button class="control-btn hold-btn" title="Hold Info - Pause/Play (P)" aria-label="Hold Info" aria-pressed="false" data-action="toggle-hold">${Icons.pause}</button>
<button class="control-btn visibility-btn" title="Toggle Panel Visibility (I)" aria-label="Toggle Panel Visibility" aria-pressed="true" data-action="toggle-panel">${Icons.eye}</button>
<button class="control-btn glass-btn" title="Toggle Glass Preview (G)" aria-label="Toggle Glass Preview" aria-pressed="true" data-action="toggle-glass">${Icons.magnifyGlass}</button>
<button class="control-btn cursor-btn" title="Toggle Cursor Preview" aria-label="Toggle Cursor Preview" aria-pressed="false" data-action="toggle-cursor">${Icons.cursor}</button>
<button class="control-btn drag-glass-btn" title="Move Glass Position (H)" aria-label="Move Glass Position" aria-pressed="false" data-action="drag-glass">${Icons.move}</button>
<button class="control-btn reset-glass-btn" title="Reset Glass Position (O)" aria-label="Reset Glass Position" data-action="reset-glass">${Icons.reset}</button>
<button class="control-btn popout-btn" title="Open in New Tab (Shift+P)" aria-label="Open in New Tab" aria-pressed="false" data-action="popout">${Icons.externalLink}</button>
`;
document.body.appendChild(this.elements.controls);
this.elements.controls.style.display = "none";
@@ -277,6 +277,7 @@ class UIManager {
const isGlassVisible = this.stateManager.state.isGlassPreviewVisible;
if (pinBtn) {
pinBtn.classList.toggle("active", this.stateManager.state.isPanelPinned);
pinBtn.setAttribute("aria-pressed", String(this.stateManager.state.isPanelPinned));
pinBtn.title = this.stateManager.state.isPanelPinned ? "Lock Panel" : "Unlock Panel";
pinBtn.innerHTML = this.stateManager.state.isPanelPinned ? Icons.lock : Icons.unlock;
pinBtn.style.display = isPanelVisible ? "flex" : "none";
@@ -294,6 +295,7 @@ class UIManager {
const showLockBtn = isPanelVisible && this.stateManager.state.isPanelPinned;
lockBtn.style.display = showLockBtn ? "flex" : "none";
lockBtn.classList.toggle("active", this.stateManager.state.isPanelLocked);
lockBtn.setAttribute("aria-pressed", String(this.stateManager.state.isPanelLocked));
lockBtn.title = this.stateManager.state.isPanelLocked ? "Unpin Panel Position" : "Pin Panel Position";
lockBtn.disabled = !this.stateManager.state.isPanelPinned;
}
@@ -301,6 +303,7 @@ class UIManager {
if (persistBtn) {
const isPersistConfigured = !!this.stateManager.state.settings["🔍MagnifyGlass.InfoPanelPersist"];
persistBtn.classList.toggle("active", isPersistConfigured);
persistBtn.setAttribute("aria-pressed", String(isPersistConfigured));
persistBtn.title = isPersistConfigured ? "Disable Sticky Info (S)" : "Enable Sticky Info (S)";
persistBtn.style.display = isPanelVisible ? "flex" : "none";
}
@@ -310,11 +313,13 @@ class UIManager {
const isHeld = this.stateManager.state.isInfoHeld;
holdBtn.style.display = isPanelVisible && isPersistConfigured ? "flex" : "none";
holdBtn.classList.toggle("active", isHeld);
holdBtn.setAttribute("aria-pressed", String(isHeld));
holdBtn.innerHTML = isHeld ? Icons.play : Icons.pause;
holdBtn.title = isHeld ? "Resume Info Update (P)" : "Pause Info Update (P)";
}
if (visibilityBtn) {
visibilityBtn.classList.toggle("active", isPanelVisible);
visibilityBtn.setAttribute("aria-pressed", String(isPanelVisible));
visibilityBtn.title = isPanelVisible ? "Hide Panel" : "Show Panel";
if (!isGlassVisible) {
visibilityBtn.disabled = true;
@@ -327,6 +332,7 @@ class UIManager {
}
if (glassBtn) {
glassBtn.classList.toggle("active", isGlassVisible);
glassBtn.setAttribute("aria-pressed", String(isGlassVisible));
glassBtn.title = isGlassVisible ? "Hide Glass Preview" : "Show Glass Preview";
glassBtn.style.display = isPanelVisible ? "flex" : "none";
}
@@ -335,6 +341,7 @@ class UIManager {
const cglass = window.comfyUIMagnifyGlass;
const showCursor = ((_a = cglass == null ? void 0 : cglass.config) == null ? void 0 : _a.showCursorPreview) || false;
cursorBtn.classList.toggle("active", showCursor);
cursorBtn.setAttribute("aria-pressed", String(showCursor));
cursorBtn.title = showCursor ? "Hide Cursor Preview" : "Show Cursor Preview";
const isPersistConfigured = !!this.stateManager.state.settings["🔍MagnifyGlass.InfoPanelPersist"];
cursorBtn.style.display = isGlassVisible && !this.stateManager.state.isPanelPinned && !isPersistConfigured ? "flex" : "none";
@@ -344,6 +351,7 @@ class UIManager {
const mglass = window.comfyUIMagnifyGlass;
const isDragMode = ((_b = mglass == null ? void 0 : mglass.state) == null ? void 0 : _b.isDragModeEnabled) || false;
dragGlassBtn.classList.toggle("active", isDragMode);
dragGlassBtn.setAttribute("aria-pressed", String(isDragMode));
dragGlassBtn.title = isDragMode ? "Cancel Move Mode (H)" : "Move Glass Position (H)";
dragGlassBtn.style.display = isGlassVisible ? "flex" : "none";
}
@@ -356,6 +364,7 @@ class UIManager {
const glass = window.comfyUIMagnifyGlass;
const isOpen = ((_c = glass == null ? void 0 : glass.popOutManager) == null ? void 0 : _c.isPopOutOpen()) || false;
popoutBtn.classList.toggle("active", isOpen);
popoutBtn.setAttribute("aria-pressed", String(isOpen));
popoutBtn.title = isOpen ? "Close Pop-out Viewer" : "Open Pop-out Viewer";
}
}
@@ -475,6 +484,7 @@ class UIManager {
if (minimizeBtn) {
minimizeBtn.textContent = this.stateManager.state.isPanelMinimized ? "+" : "−";
minimizeBtn.title = this.stateManager.state.isPanelMinimized ? "Expand Panel" : "Minimize Panel";
minimizeBtn.setAttribute("aria-expanded", String(!this.stateManager.state.isPanelMinimized));
}
}
/**
@@ -641,7 +651,7 @@ class UIManager {
const rawValue = String(item.value || "");
const showCopyBtn = !item.clickable && rawValue.length > 3 && typeof item.value === "string";
const copyBtnHtml = showCopyBtn ? `
<button class="copy-btn" data-copy-value="${rawValue.replace(/"/g, "&quot;")}" title="Copy to clipboard">
<button class="copy-btn" data-copy-value="${rawValue.replace(/"/g, "&quot;")}" title="Copy to clipboard" aria-label="Copy to clipboard">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
File diff suppressed because one or more lines are too long
+4 -1
View File
@@ -326,6 +326,8 @@ class UiManager {
btn.style.minHeight = computed.minHeight;
btn.style.width = computed.width;
btn.title = "Toggle Magnify Glass (X)";
btn.setAttribute("aria-label", "Toggle Magnify Glass");
btn.setAttribute("aria-pressed", "false");
btn.setAttribute("data-testid", "toggle-magnify-glass-button");
btn.innerHTML = Icons.magnifyGlass;
btn.style.display = "inline-flex";
@@ -341,9 +343,10 @@ class UiManager {
btn.addEventListener("click", () => {
if (this.onToggle) {
this.onToggle();
btn.classList.toggle("active");
const isActive = btn.classList.toggle("active");
btn.classList.toggle("p-highlight");
btn.classList.toggle("selected");
btn.setAttribute("aria-pressed", String(isActive));
}
});
if (linkVisibilityBtn && minimapBtn.parentElement === linkVisibilityBtn.parentElement) {
File diff suppressed because one or more lines are too long