diff --git a/.Jules/palette.md b/.Jules/palette.md new file mode 100644 index 0000000..7b8ac48 --- /dev/null +++ b/.Jules/palette.md @@ -0,0 +1,3 @@ +## 2024-05-22 - Custom UI Component Focus States +**Learning:** Custom UI components (like divs acting as buttons/toggles) lack native focus indicators, making keyboard navigation invisible and confusing. +**Action:** Always add explicit `:focus-visible` styles to custom interactive elements in CSS to ensure keyboard users can see where they are. diff --git a/css/sidebar.css b/css/sidebar.css index 6f458a3..9be093d 100644 --- a/css/sidebar.css +++ b/css/sidebar.css @@ -62,6 +62,12 @@ background: rgba(255, 255, 255, 0.06); } +.magnify-sidebar-section-header:focus-visible { + outline: 2px solid var(--p-primary-color, #3b82f6); + outline-offset: -2px; + background: rgba(255, 255, 255, 0.06); +} + .magnify-sidebar-section-header svg { width: 12px; height: 12px; @@ -125,6 +131,11 @@ cursor: pointer; } +.magnify-slider:focus-visible { + outline: 2px solid var(--p-primary-color, #3b82f6); + outline-offset: 2px; +} + .magnify-slider::-webkit-slider-thumb { appearance: none; width: 14px; @@ -171,6 +182,11 @@ transition: background 0.2s; } +.magnify-toggle:focus-visible { + outline: 2px solid var(--p-primary-color, #3b82f6); + outline-offset: 2px; +} + .magnify-toggle.active { background: var(--p-primary-color, #3b82f6); } @@ -212,6 +228,11 @@ border-color: var(--p-primary-color, #3b82f6); } +.magnify-select:focus-visible { + outline: 2px solid var(--p-primary-color, #3b82f6); + border-color: var(--p-primary-color, #3b82f6); +} + /* Inspector styles */ .magnify-inspector-section { padding: 12px 16px; @@ -305,6 +326,12 @@ transform: scale(0.98); } +.magnify-btn:focus-visible { + outline: 2px solid white; + outline-offset: -2px; + box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.5); +} + .magnify-btn-secondary { background: rgba(255, 255, 255, 0.1); } @@ -336,6 +363,11 @@ background: transparent; } +.magnify-color-input:focus-visible { + outline: 2px solid var(--p-primary-color, #3b82f6); + outline-offset: 2px; +} + .magnify-color-input::-webkit-color-swatch-wrapper { padding: 2px; } @@ -349,4 +381,4 @@ font-size: 11px; font-family: monospace; color: var(--descrip-text, #999); -} \ No newline at end of file +} diff --git a/src/sidebar/SidebarSettings.ts b/src/sidebar/SidebarSettings.ts index c2d4f4b..d40a786 100644 --- a/src/sidebar/SidebarSettings.ts +++ b/src/sidebar/SidebarSettings.ts @@ -71,6 +71,7 @@ export function createSlider( slider.max = String(max); slider.step = String(step); slider.value = String(value); + slider.setAttribute('aria-valuetext', `${value}${unit}`); // Prevent ComfyUI from capturing mouse events during drag slider.addEventListener('mousedown', (e) => e.stopPropagation()); @@ -81,6 +82,7 @@ export function createSlider( e.stopPropagation(); const newValue = parseFloat(slider.value); valueEl.textContent = `${newValue}${unit}`; + slider.setAttribute('aria-valuetext', `${newValue}${unit}`); if (onInput) onInput(newValue); }); diff --git a/web/sidebar.css b/web/sidebar.css index 6f458a3..9be093d 100644 --- a/web/sidebar.css +++ b/web/sidebar.css @@ -62,6 +62,12 @@ background: rgba(255, 255, 255, 0.06); } +.magnify-sidebar-section-header:focus-visible { + outline: 2px solid var(--p-primary-color, #3b82f6); + outline-offset: -2px; + background: rgba(255, 255, 255, 0.06); +} + .magnify-sidebar-section-header svg { width: 12px; height: 12px; @@ -125,6 +131,11 @@ cursor: pointer; } +.magnify-slider:focus-visible { + outline: 2px solid var(--p-primary-color, #3b82f6); + outline-offset: 2px; +} + .magnify-slider::-webkit-slider-thumb { appearance: none; width: 14px; @@ -171,6 +182,11 @@ transition: background 0.2s; } +.magnify-toggle:focus-visible { + outline: 2px solid var(--p-primary-color, #3b82f6); + outline-offset: 2px; +} + .magnify-toggle.active { background: var(--p-primary-color, #3b82f6); } @@ -212,6 +228,11 @@ border-color: var(--p-primary-color, #3b82f6); } +.magnify-select:focus-visible { + outline: 2px solid var(--p-primary-color, #3b82f6); + border-color: var(--p-primary-color, #3b82f6); +} + /* Inspector styles */ .magnify-inspector-section { padding: 12px 16px; @@ -305,6 +326,12 @@ transform: scale(0.98); } +.magnify-btn:focus-visible { + outline: 2px solid white; + outline-offset: -2px; + box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.5); +} + .magnify-btn-secondary { background: rgba(255, 255, 255, 0.1); } @@ -336,6 +363,11 @@ background: transparent; } +.magnify-color-input:focus-visible { + outline: 2px solid var(--p-primary-color, #3b82f6); + outline-offset: 2px; +} + .magnify-color-input::-webkit-color-swatch-wrapper { padding: 2px; } @@ -349,4 +381,4 @@ font-size: 11px; font-family: monospace; color: var(--descrip-text, #999); -} \ No newline at end of file +} diff --git a/web/sidebar/SidebarSettings.js b/web/sidebar/SidebarSettings.js index f8f99c1..bce8c55 100644 --- a/web/sidebar/SidebarSettings.js +++ b/web/sidebar/SidebarSettings.js @@ -27,6 +27,7 @@ function createSlider(label, value, min, max, step, unit, onChange, tooltip, onI slider.max = String(max); slider.step = String(step); slider.value = String(value); + slider.setAttribute("aria-valuetext", `${value}${unit}`); slider.addEventListener("mousedown", (e) => e.stopPropagation()); slider.addEventListener("touchstart", (e) => e.stopPropagation()); slider.addEventListener("pointerdown", (e) => e.stopPropagation()); @@ -34,6 +35,7 @@ function createSlider(label, value, min, max, step, unit, onChange, tooltip, onI e.stopPropagation(); const newValue = parseFloat(slider.value); valueEl.textContent = `${newValue}${unit}`; + slider.setAttribute("aria-valuetext", `${newValue}${unit}`); if (onInput) onInput(newValue); }); slider.addEventListener("change", (e) => {