From 29ece683323f0d8bd3ea7691698078f91316b226 Mon Sep 17 00:00:00 2001 From: Cell <1024@lruihao.cn> Date: Fri, 3 Apr 2026 18:00:17 +0800 Subject: [PATCH] feat(accessibility): enhance accessibility features across components and improve focus management (#725) --- assets/css/_core/_layout/_header.scss | 26 ++++++++++------- assets/css/_core/_mixin/_compatibility.scss | 19 ++++++++++++ assets/css/_core/_widgets/_fixed-button.scss | 6 ++-- assets/js/theme.js | 29 ++++++++++++++++++- hugo.toml | 4 +-- layouts/_partials/function/code-copy-btn.html | 2 +- .../_partials/function/code-expand-btn.html | 2 +- layouts/_partials/function/code-header.html | 14 ++++----- layouts/_partials/layout/header.html | 16 +++++----- layouts/_partials/layout/widgets.html | 6 ++-- .../_partials/plugin/diagram-copy-btn.html | 2 +- layouts/posts/single.html | 2 +- 12 files changed, 89 insertions(+), 39 deletions(-) diff --git a/assets/css/_core/_layout/_header.scss b/assets/css/_core/_layout/_header.scss index be468e2c..d531fe37 100644 --- a/assets/css/_core/_layout/_header.scss +++ b/assets/css/_core/_layout/_header.scss @@ -70,7 +70,8 @@ header { font-size: fixit-var(header-title-font-size); font-family: fixit-var(header-title-font-family); font-weight: bold; - margin-right: 0.5rem; + margin-inline-end: 0.25rem; + padding-inline: 0.25rem; min-width: 10%; overflow: hidden; white-space: nowrap; @@ -175,6 +176,7 @@ header { #header-desktop.open & { left: 1rem; } + @include focus-visible-ring; } .search-loading { @@ -210,6 +212,9 @@ header { } .theme-switch { + position: relative; + @include focus-visible-ring; + i { display: inline-block; transform: rotate(225deg) scale(1); @@ -246,12 +251,14 @@ header { } .header-wrapper { - // use css native function to calculate max value - padding: 0 2rem 0 MAX(10vh, fixit-var(header-height)); + padding: 0 2rem; height: 100%; + &[data-github-corner='left'] { + padding-inline-start: fixit-var(header-height); + } &[data-github-corner='right'] { - padding-right: fixit-var(header-height); + padding-inline-end: fixit-var(header-height); } .header-title { @@ -311,11 +318,7 @@ header { } @include media('md', 'down') { - padding-inline-end: 1rem; - } - - @include media('sm', 'down') { - padding-inline-start: 1rem; + padding-inline: 1rem; } } @@ -358,13 +361,16 @@ header { } .menu-toggle { + position: relative; + @include focus-visible-ring; + span { display: block; background-color: fixit-var(global-font-color); width: 20px; height: 2px; - @include border-radius(full); transition: all 0.3s ease-in-out; + @include border-radius(full); } span:not(:last-child) { diff --git a/assets/css/_core/_mixin/_compatibility.scss b/assets/css/_core/_mixin/_compatibility.scss index 28725aa5..ebf47431 100644 --- a/assets/css/_core/_mixin/_compatibility.scss +++ b/assets/css/_core/_mixin/_compatibility.scss @@ -61,3 +61,22 @@ } } } + +@mixin focus-visible-ring($size: 1.5em, $offset: 0.2rem) { + &:focus-visible { + outline: none; + + &::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: $size; + height: $size; + transform: translate(-50%, -50%); + outline: -webkit-focus-ring-color auto 1px; + outline-offset: $offset; + pointer-events: none; + } + } +} diff --git a/assets/css/_core/_widgets/_fixed-button.scss b/assets/css/_core/_widgets/_fixed-button.scss index 31769ae7..59a41edd 100644 --- a/assets/css/_core/_widgets/_fixed-button.scss +++ b/assets/css/_core/_widgets/_fixed-button.scss @@ -23,10 +23,8 @@ cursor: pointer; } - &:active, - &:focus, - &:hover { - outline: none; + &:focus-visible { + outline: -webkit-focus-ring-color auto 1px; } @include media('print') { diff --git a/assets/js/theme.js b/assets/js/theme.js index 0c588239..a35248b8 100644 --- a/assets/js/theme.js +++ b/assets/js/theme.js @@ -68,6 +68,19 @@ class FixIt { this.config.twemoji && twemoji.parse(target); } + initRoleButtons(target = document) { + forEach(target.querySelectorAll('[role="button"]'), ($el) => { + if ($el.dataset.init) return; + $el.dataset.init = 'true'; + $el.addEventListener('keydown', (e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + $el.click(); + } + }, false); + }); + } + initMenu() { this.initMenuDesktop(); this.initMenuMobile(); @@ -87,10 +100,12 @@ class FixIt { $mask.classList.toggle('blur'); $menuToggleMobile.classList.toggle('active'); $menuMobile.classList.toggle('active'); + $menuToggleMobile.setAttribute('aria-expanded', $menuToggleMobile.classList.contains('active') ? 'true' : 'false'); }, false); this._menuMobileOnClickMask = this._menuMobileOnClickMask || (() => { $menuToggleMobile.classList.remove('active'); $menuMobile.classList.remove('active'); + $menuToggleMobile.setAttribute('aria-expanded', 'false'); }); this.clickMaskEventSet.add(this._menuMobileOnClickMask); // add nested menu toggler @@ -166,6 +181,7 @@ class FixIt { $searchLoading.style.display = 'none'; $searchClear.style.display = 'none'; searchInstance && searchInstance.autocomplete.setVal(''); + document.getElementById(`search-toggle-${$header.id.replace('header-', '')}`)?.setAttribute('aria-expanded', 'false'); } initSearch() { @@ -198,6 +214,9 @@ class FixIt { const $searchClear = document.getElementById(`search-clear-${suffix}`); const $searchCancel = document.getElementById('search-cancel-mobile'); const $mask = document.getElementById('mask'); + const setSearchExpanded = (expanded) => { + $searchToggle?.setAttribute('aria-expanded', expanded ? 'true' : 'false'); + }; // goto the PostChat panel rather than search results if (searchConfig.type === 'post-chat' && window.postChatUser) { @@ -218,11 +237,13 @@ class FixIt { $searchInput.addEventListener('focus', () => { $mask.classList.add('blur'); $header.classList.add('open'); + setSearchExpanded(true); }, false); $searchCancel.addEventListener('click', () => { $mask.classList.remove('blur'); document.getElementById('menu-toggle-mobile').classList.remove('active'); document.getElementById('menu-mobile').classList.remove('active'); + document.getElementById('menu-toggle-mobile').setAttribute('aria-expanded', 'false'); this._resetSearchUI($header, $searchLoading, $searchClear, this._searchMobile); }, false); $searchClear.addEventListener('click', () => { @@ -239,6 +260,7 @@ class FixIt { $mask.classList.add('blur'); $header.classList.add('open'); $searchInput.focus(); + setSearchExpanded(true); }, false); $searchClear.addEventListener('click', () => { $searchClear.style.display = 'none'; @@ -262,7 +284,7 @@ class FixIt { dropdownMenuContainer: `#search-dropdown-${suffix}`, clearOnSelected: true, cssClasses: { noPrefix: true }, - debug: true + debug: false }, { name: 'search', @@ -908,11 +930,15 @@ class FixIt { if (!dialog || !openButton) return; openButton.addEventListener("click", () => { dialog.showModal(); + openButton.setAttribute('aria-expanded', 'true'); document.activeElement?.blur(); }); dialog.addEventListener("click", (e) => { dialog.close(); }); + dialog.addEventListener("close", () => { + openButton.setAttribute('aria-expanded', 'false'); + }); } /** @@ -1447,6 +1473,7 @@ class FixIt { this.initMathJax(); this.initJsonViewer(); this.initTabEvents(target); + this.initRoleButtons(target); FileTree.init(target); window.FixItMermaid?.init?.(); window.FixItAPlayer?.init?.(); diff --git a/hugo.toml b/hugo.toml index 5bd39c00..82b4012a 100644 --- a/hugo.toml +++ b/hugo.toml @@ -492,8 +492,8 @@ tileColor = "#da532c" # FixIt 0.2.12 | CHANGED Android browser theme color [params.app.themeColor] -light = "#f8f8f8" -dark = "#252627" +light = "#f6f8fa" +dark = "#151b23" # Search config [params.search] diff --git a/layouts/_partials/function/code-copy-btn.html b/layouts/_partials/function/code-copy-btn.html index ad98354c..a6166afb 100644 --- a/layouts/_partials/function/code-copy-btn.html +++ b/layouts/_partials/function/code-copy-btn.html @@ -4,7 +4,7 @@ {{- if $config.copyable -}} {{- $copyIcon := dict "Class" "fa-regular fa-clone fa-width-auto" | partial "plugin/icon.html" -}} - {{- $copyBtn := printf `
%s
` (T "assets.copyToClipboard") (T "assets.copyToClipboard") $copyIcon -}} + {{- $copyBtn := printf `
%s
` (T "assets.copyToClipboard") (T "assets.copyToClipboard") $copyIcon -}} {{- $wrapper = replaceRE `(
[\s\S]*?<\/div>)` (printf `$1%s` $copyBtn) $wrapper -}} {{- end -}} diff --git a/layouts/_partials/function/code-expand-btn.html b/layouts/_partials/function/code-expand-btn.html index 7f80e5e3..72a32508 100644 --- a/layouts/_partials/function/code-expand-btn.html +++ b/layouts/_partials/function/code-expand-btn.html @@ -1,7 +1,7 @@ {{- /* Code expand button for code blocks */ -}} {{- $wrapper := .Wrapper -}} {{- $expandIcon := dict "Class" "fa-solid fa-angles-down" | partial "plugin/icon.html" -}} -{{- $expandBtn := printf `
%s
` (T "assets.expandCode") $expandIcon -}} +{{- $expandBtn := printf `
%s
` (T "assets.expandCode") $expandIcon -}} {{- $wrapper = replaceRE `(
[\s\S]*?<\/div>)` (printf `$1%s` $expandBtn) $wrapper -}} {{- return $wrapper -}} diff --git a/layouts/_partials/function/code-header.html b/layouts/_partials/function/code-header.html index 852fa44b..c14e9fb8 100644 --- a/layouts/_partials/function/code-header.html +++ b/layouts/_partials/function/code-header.html @@ -11,34 +11,34 @@ {{- $fullscreenBtn := "" -}} {{- $foldIcon := dict "Class" "arrow fa-solid fa-chevron-down" | partial "plugin/icon.html" -}} {{- $titleEl := printf `%s` $foldIcon -}} -{{- $ellipsisBtn := printf `%s` (dict "Class" "fa-solid fa-ellipsis-h" | partial "plugin/icon.html") -}} +{{- $ellipsisBtn := printf `%s` (dict "Class" "fa-solid fa-ellipsis-h" | partial "plugin/icon.html") -}} {{- if $config.title -}} {{- $titleEl = printf `%s%s` $foldIcon $config.title -}} {{- end -}} {{- if $config.linenostoggler -}} {{- $lineNosIcon := dict "Class" "fa-solid fa-list-ol" | partial "plugin/icon.html" -}} - {{- $lineNosBtn = printf `%s` (T "assets.toggleLineNumbers") (T "assets.toggleLineNumbers") $lineNosIcon -}} + {{- $lineNosBtn = printf `%s` (T "assets.toggleLineNumbers") (T "assets.toggleLineNumbers") $lineNosIcon -}} {{- end -}} {{- if $config.linewraptoggler -}} {{- $lineWrapIcon := dict "Class" "fa-solid fa-right-left" | partial "plugin/icon.html" -}} - {{- $lineWrapBtn = printf `%s` (T "assets.toggleLineWrap") (T "assets.toggleLineWrap") $lineWrapIcon -}} + {{- $lineWrapBtn = printf `%s` (T "assets.toggleLineWrap") (T "assets.toggleLineWrap") $lineWrapIcon -}} {{- end -}} {{- if $config.editable -}} {{- $editIcon := dict "Class" "fa-solid fa-pen-to-square" | partial "plugin/icon.html" -}} - {{- $editBtn = printf `%s` (T "assets.toggleCodeEditable") (T "assets.toggleCodeEditable") $editIcon -}} + {{- $editBtn = printf `%s` (T "assets.toggleCodeEditable") (T "assets.toggleCodeEditable") $editIcon -}} {{- end -}} {{- if $config.copyable -}} {{- $copyIcon := dict "Class" "fa-regular fa-clone" | partial "plugin/icon.html" -}} - {{- $copyBtn = printf `%s` (T "assets.copyToClipboard") (T "assets.copyToClipboard") (T "assets.copiedText") $copyIcon -}} + {{- $copyBtn = printf `%s` (T "assets.copyToClipboard") (T "assets.copyToClipboard") (T "assets.copiedText") $copyIcon -}} {{- end -}} {{- if $config.downloadable -}} {{- $downloadIcon := dict "Class" "fa-solid fa-download" | partial "plugin/icon.html" -}} - {{- $downloadBtn = printf `%s` (T "assets.downloadCode") (T "assets.downloadCode") $downloadIcon -}} + {{- $downloadBtn = printf `%s` (T "assets.downloadCode") (T "assets.downloadCode") $downloadIcon -}} {{- end -}} {{- if $config.fullscreen -}} {{- $fullscreenIcon := dict "Class" "fa-solid fa-expand" | partial "plugin/icon.html" -}} - {{- $fullscreenBtn = printf `%s` (T "assets.toggleCodeFullscreen") (T "assets.toggleCodeFullscreen") $fullscreenIcon -}} + {{- $fullscreenBtn = printf `%s` (T "assets.toggleCodeFullscreen") (T "assets.toggleCodeFullscreen") $fullscreenIcon -}} {{- end -}} {{- with $config.name -}} {{- $titleEl = replace $titleEl `` (printf `` .) -}} diff --git a/layouts/_partials/layout/header.html b/layouts/_partials/layout/header.html index 84f344a7..c163a1fa 100644 --- a/layouts/_partials/layout/header.html +++ b/layouts/_partials/layout/header.html @@ -96,8 +96,8 @@ {{- end -}} {{- if .Site.Params.search.enable -}} {{- end -}} - {{- if hugo.IsMultilingual -}}
-
+
{{- dict "Class" "fa-solid fa-adjust" | partial "plugin/icon.html" -}}
- @@ -200,7 +200,7 @@