feat(buttons): replace role="button" with button elements for improved accessibility

This commit is contained in:
Cell
2026-04-07 12:52:21 +08:00
parent 66dc7bc5af
commit e527289140
13 changed files with 277 additions and 133 deletions
+6 -1
View File
@@ -32,7 +32,12 @@ iframe {
@include border-radius;
}
[role='button'] {
button {
padding: 0;
border: 0;
background: transparent;
color: inherit;
appearance: none;
cursor: pointer;
}
+1 -1
View File
@@ -172,11 +172,11 @@ header {
.search-toggle {
left: 0.5rem;
right: auto;
@include focus-visible-ring;
#header-desktop.open & {
left: 1rem;
}
@include focus-visible-ring;
}
.search-loading {
+1 -1
View File
@@ -62,7 +62,7 @@
}
}
@mixin focus-visible-ring($size: 1.5em, $offset: 0.2rem) {
@mixin focus-visible-ring($size: 1.3em, $offset: 0.2rem) {
&:focus-visible {
outline: none;
+1 -2
View File
@@ -14,15 +14,14 @@
padding: 0.5rem 0.625rem;
color: fixit-var(global-font-secondary-color);
transition: color 0.4s ease;
@include border-radius(full);
background-color: light-dark(#f1f1f1, #1e1e1f);
@include border-radius(full);
&:hover,
&:active,
&:focus {
outline: none;
color: fixit-var(global-font-color);
cursor: pointer;
}
@include media('print') {
+2 -2
View File
@@ -236,7 +236,7 @@ pre {
padding: 0.4rem;
}
[role='button'] {
button {
padding: 0.4rem;
@include media('print') {
@@ -276,7 +276,7 @@ pre {
&.line-wrapping .line-wrap-btn,
&:not(.line-nos-hidden) .line-nos-btn,
.code-header [role='button']:hover,
.code-header button:hover,
&:has([contenteditable='true']) .edit-btn {
color: fixit-var(global-link-hover-color);
}
@@ -6,6 +6,7 @@
// Diagram copy button styles
.diagram-copy-btn {
box-sizing: content-box;
width: 1rem;
height: 1rem;
position: absolute;
+234 -97
View File
@@ -25,10 +25,12 @@ class FixIt {
this.isDark = isDarkMode();
this.newScrollTop = getScrollTop();
this.oldScrollTop = this.newScrollTop;
this.maskOverlays = new Map();
this.activeMaskOverlay = null;
this.activeTocId = null;
this.scrollEventSet = new Set();
this.resizeEventSet = new Set();
this.switchThemeEventSet = new Set();
this.clickMaskEventSet = new Set();
this.beforeprintEventSet = new Set();
this.afterprintEventSet = new Set();
window.objectFitImages && objectFitImages();
@@ -68,17 +70,54 @@ 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();
registerMaskOverlay(name, handlers) {
this.maskOverlays.set(name, handlers);
}
}, false);
});
syncMaskState() {
document.getElementById('mask')?.classList.toggle('blur', Boolean(this.activeMaskOverlay));
}
openMaskOverlay(name) {
this.disableScrollEvent = true;
const overlay = this.maskOverlays.get(name);
if (!overlay) return;
if (this.activeMaskOverlay && this.activeMaskOverlay !== name) {
this.closeMaskOverlay(this.activeMaskOverlay, true);
}
overlay.onOpen?.();
this.activeMaskOverlay = name;
this.syncMaskState();
}
closeMaskOverlay(name, skipSync = false) {
this.disableScrollEvent = false;
const overlay = this.maskOverlays.get(name);
if (!overlay) return;
overlay.onClose?.();
if (this.activeMaskOverlay === name) {
this.activeMaskOverlay = null;
}
!skipSync && this.syncMaskState();
}
toggleMaskOverlay(name) {
const overlay = this.maskOverlays.get(name);
if (!overlay) return;
const isActive = overlay.isActive?.() ?? this.activeMaskOverlay === name;
if (this.activeMaskOverlay === name && isActive) {
this.closeMaskOverlay(name);
return;
}
this.openMaskOverlay(name);
}
closeActiveMaskOverlay() {
if (!this.activeMaskOverlay) {
this.syncMaskState();
return;
}
this.closeMaskOverlay(this.activeMaskOverlay);
}
initMenu() {
@@ -95,19 +134,28 @@ class FixIt {
initMenuMobile() {
const $menuToggleMobile = document.getElementById('menu-toggle-mobile');
const $menuMobile = document.getElementById('menu-mobile');
const $mask = document.getElementById('mask');
$menuToggleMobile.addEventListener('click', (event) => {
$mask.classList.toggle('blur');
$menuToggleMobile.classList.toggle('active');
$menuMobile.classList.toggle('active');
$menuToggleMobile.setAttribute('aria-expanded', $menuToggleMobile.classList.contains('active') ? 'true' : 'false');
if (!$menuToggleMobile || !$menuMobile) return;
this.registerMaskOverlay('menu-mobile', {
isActive: () => $menuMobile.classList.contains('active'),
onOpen: () => {
$menuToggleMobile.classList.add('active');
$menuMobile.classList.add('active');
$menuToggleMobile.setAttribute('aria-expanded', 'true');
},
onClose: () => {
$menuToggleMobile.classList.remove('active');
$menuMobile.classList.remove('active');
$menuToggleMobile.setAttribute('aria-expanded', 'false');
},
});
$menuToggleMobile.addEventListener('click', () => {
this.toggleMaskOverlay('menu-mobile');
}, 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
forEach(document.querySelectorAll('.menu-item>.nested-item'), ($nestedItem) => {
$nestedItem.addEventListener('click', function () {
@@ -213,10 +261,33 @@ class FixIt {
const $searchLoading = document.getElementById(`search-loading-${suffix}`);
const $searchClear = document.getElementById(`search-clear-${suffix}`);
const $searchCancel = document.getElementById('search-cancel-mobile');
const $mask = document.getElementById('mask');
const $menuToggleMobile = document.getElementById('menu-toggle-mobile');
const $menuMobile = document.getElementById('menu-mobile');
if (!$header || !$searchInput || !$searchToggle || !$searchLoading || !$searchClear) return;
const setSearchExpanded = (expanded) => {
$searchToggle?.setAttribute('aria-expanded', expanded ? 'true' : 'false');
};
const overlayName = `search-${suffix}`;
const openSearch = () => {
if (_isMobile && $menuToggleMobile && $menuMobile) {
this.disableScrollEvent = true;
$menuToggleMobile.classList.add('active');
$menuMobile.classList.add('active');
$menuToggleMobile.setAttribute('aria-expanded', 'true');
}
$header.classList.add('open');
setSearchExpanded(true);
!_isMobile && $searchInput.focus();
};
const closeSearch = () => {
if (_isMobile && $menuToggleMobile && $menuMobile) {
this.disableScrollEvent = false;
$menuToggleMobile.classList.remove('active');
$menuMobile.classList.remove('active');
$menuToggleMobile.setAttribute('aria-expanded', 'false');
}
this._resetSearchUI($header, $searchLoading, $searchClear, _isMobile ? this._searchMobile : this._searchDesktop);
};
// goto the PostChat panel rather than search results
if (searchConfig.type === 'post-chat' && window.postChatUser) {
@@ -234,42 +305,36 @@ class FixIt {
if (_isMobile) {
this._searchMobileOnce = true;
this.registerMaskOverlay(overlayName, {
isActive: () => $header.classList.contains('open'),
onOpen: openSearch,
onClose: closeSearch,
});
$searchInput.addEventListener('focus', () => {
$mask.classList.add('blur');
$header.classList.add('open');
setSearchExpanded(true);
this.openMaskOverlay(overlayName);
}, 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);
this.closeMaskOverlay(overlayName);
}, false);
$searchClear.addEventListener('click', () => {
this.disableScrollEvent = false;
$searchClear.style.display = 'none';
this._searchMobile && this._searchMobile.autocomplete.setVal('');
}, false);
this._searchMobileOnClickMask = this._searchMobileOnClickMask || (() => {
this._resetSearchUI($header, $searchLoading, $searchClear, this._searchMobile);
});
this.clickMaskEventSet.add(this._searchMobileOnClickMask);
} else {
this._searchDesktopOnce = true;
this.registerMaskOverlay(overlayName, {
isActive: () => $header.classList.contains('open'),
onOpen: openSearch,
onClose: closeSearch,
});
$searchToggle.addEventListener('click', () => {
$mask.classList.add('blur');
$header.classList.add('open');
$searchInput.focus();
setSearchExpanded(true);
this.toggleMaskOverlay(overlayName);
}, false);
$searchClear.addEventListener('click', () => {
$searchClear.style.display = 'none';
this._searchDesktop && this._searchDesktop.autocomplete.setVal('');
}, false);
this._searchDesktopOnClickMask = this._searchDesktopOnClickMask || (() => {
this._resetSearchUI($header, $searchLoading, $searchClear, this._searchDesktop);
});
this.clickMaskEventSet.add(this._searchDesktopOnClickMask);
}
$searchInput.addEventListener('input', () => {
if ($searchInput.value === '') $searchClear.style.display = 'none';
@@ -428,7 +493,7 @@ class FixIt {
}
);
autosearch.on('autocomplete:selected', (_event, suggestion, _dataset, _context) => {
$mask?.click();
this.closeMaskOverlay(overlayName);
window.location.assign(suggestion.uri);
});
if (_isMobile) {
@@ -811,42 +876,121 @@ class FixIt {
stagingDOM.destroy();
}
/**
* Helper method to update TOC active state based on scroll position
* @param {HTMLElement} $tocContainer - TOC container element for parent traversal
* @param {HTMLCollection} $headingElements - Heading elements to track
* @param {number} indexOffset - Offset for active state calculation
*/
_updateTocActiveState($tocContainer, $headingElements, indexOffset) {
const $tocLinkElements = $tocContainer.querySelectorAll('a:first-child');
const $tocLiElements = $tocContainer.getElementsByTagName('li');
getVisibleHeaderOffset() {
const $desktopHeader = document.getElementById('header-desktop');
const $mobileHeader = document.getElementById('header-mobile');
const $header = [$desktopHeader, $mobileHeader].find(($el) => $el && window.getComputedStyle($el).display !== 'none');
if (!$header) return 0;
const isDesktop = $header.id === 'header-desktop';
const headerMode = isDesktop ? document.body.dataset.headerDesktop : document.body.dataset.headerMobile;
if (!['sticky', 'auto'].includes(headerMode)) return 0;
if (headerMode === 'auto' && $header.classList.contains('header__fadeOutUp')) return 0;
return $header.offsetHeight;
}
// Remove all active classes
forEach($tocLinkElements, ($tocLink) => {
$tocLink.classList.remove('active');
});
forEach($tocLiElements, ($tocLi) => {
$tocLi.classList.remove('has-active');
});
getBreadcrumbHeight() {
return document.querySelector('.breadcrumb-container')?.offsetHeight || 0;
}
// Calculate active TOC index
let activeTocIndex = $headingElements.length - 1;
for (let i = 0; i < $headingElements.length - 1; i++) {
const thisTop = $headingElements[i].getBoundingClientRect().top;
const nextTop = $headingElements[i + 1].getBoundingClientRect().top;
if ((i == 0 && thisTop > indexOffset) || (thisTop <= indexOffset && nextTop > indexOffset)) {
activeTocIndex = i;
getTocIndexOffset() {
return 20 + this.getVisibleHeaderOffset() + this.getBreadcrumbHeight();
}
getTocHeadingElements() {
return Array.from(document.getElementsByClassName('heading-element')).filter(($heading) => $heading.id);
}
getActiveTocHeading($headingElements, indexOffset = this.getTocIndexOffset()) {
if (!$headingElements.length) return null;
const threshold = window.scrollY + indexOffset + 1;
let $activeHeading = $headingElements[0];
for (const $heading of $headingElements) {
const headingTop = window.scrollY + $heading.getBoundingClientRect().top;
if (headingTop <= threshold) {
$activeHeading = $heading;
} else {
break;
}
}
return $activeHeading;
}
// Add active classes
if (activeTocIndex !== -1 && $tocLinkElements[activeTocIndex]) {
$tocLinkElements[activeTocIndex].classList.add('active');
let $parent = $tocLinkElements[activeTocIndex].parentElement;
while ($parent !== $tocContainer) {
getTocRoots() {
return [
document.getElementById('TableOfContents'),
document.querySelector('#toc-content-static > nav'),
document.querySelector('#toc-content-drawer > nav'),
].filter(Boolean);
}
getTocLinkById($tocRoot, id) {
if (!$tocRoot || !id) return null;
const targetHash = `#${id}`;
return Array.from($tocRoot.querySelectorAll('a[href^="#"]')).find(($link) => $link.getAttribute('href') === targetHash) || null;
}
applyTocActiveState($tocRoot, activeId) {
if (!$tocRoot) return;
forEach($tocRoot.querySelectorAll('a[href^="#"]'), ($tocLink) => {
$tocLink.classList.remove('active');
});
forEach($tocRoot.querySelectorAll('li'), ($tocLi) => {
$tocLi.classList.remove('has-active');
});
const $activeLink = this.getTocLinkById($tocRoot, activeId);
if (!$activeLink) return;
$activeLink.classList.add('active');
let $parent = $activeLink.closest('li');
while ($parent) {
$parent.classList.add('has-active');
$parent = $parent.parentElement.parentElement;
$parent = $parent.parentElement?.closest('li') || null;
}
}
scrollActiveTocLinkIntoView($tocRoot, activeId, $scrollContainer = $tocRoot) {
const $activeLink = this.getTocLinkById($tocRoot, activeId);
if (!$activeLink || !$scrollContainer) return;
const containerRect = $scrollContainer.getBoundingClientRect();
const linkRect = $activeLink.getBoundingClientRect();
const offsetTop = linkRect.top - containerRect.top;
const offsetBottom = linkRect.bottom - containerRect.bottom;
if (offsetTop < 0) {
$scrollContainer.scrollTop += offsetTop;
} else if (offsetBottom > 0) {
$scrollContainer.scrollTop += offsetBottom;
}
}
syncTocHeight() {
const $toc = document.getElementById('toc-auto');
const $tocContentAuto = document.getElementById('toc-content-auto');
if ($toc && $tocContentAuto) {
const maxHeight = Math.max(window.innerHeight - $tocContentAuto.getBoundingClientRect().top - 16);
$tocContentAuto.style.setProperty('--fi-toc-content-max-height', `${Math.floor(maxHeight)}px`);
}
}
syncTocActiveState() {
const $headingElements = this.getTocHeadingElements();
const $activeHeading = this.getActiveTocHeading($headingElements);
if (!$activeHeading?.id) return;
const activeId = $activeHeading.id;
const $tocRoots = this.getTocRoots();
forEach($tocRoots, ($tocRoot) => {
this.applyTocActiveState($tocRoot, activeId);
});
if (this.activeTocId !== activeId) {
this.activeTocId = activeId;
if (!isTocStatic()) {
const $autoTocRoot = document.getElementById('TableOfContents');
const $autoTocContainer = document.getElementById('toc-content-auto');
if ($autoTocRoot && $autoTocContainer) {
this.scrollActiveTocLinkIntoView($autoTocRoot, activeId, $autoTocContainer);
}
}
if (document.getElementById('toc-dialog')?.open) {
const $dialogTocRoot = document.querySelector('#toc-content-drawer > nav');
this.scrollActiveTocLinkIntoView($dialogTocRoot, activeId, $dialogTocRoot);
}
}
}
@@ -857,15 +1001,12 @@ class FixIt {
initToc() {
const $tocCore = document.getElementById('TableOfContents');
if ($tocCore === null) return;
const $headingElements = document.getElementsByClassName('heading-element');
const INDEX_OFFSET = 20 + this.breadcrumbHeight + (
document.body.dataset.headerDesktop !== 'normal' ? document.getElementById('header-desktop').offsetHeight : 0
);
// TOC Drawer Button Visibility
const openButton = document.querySelector("#toc-drawer-button");
if (openButton) {
openButton.classList.toggle('d-none', !isTocStatic());
}
this.activeTocId = null;
// TOC Static and TOC Dialog
if (isTocStatic()) {
const $tocContentStatic = document.getElementById('toc-content-static');
@@ -873,16 +1014,8 @@ class FixIt {
$tocCore.parentElement.removeChild($tocCore);
$tocContentStatic.appendChild($tocCore);
}
this._tocDialogOnScroll = this._tocDialogOnScroll || (() => {
this._updateTocActiveState(
document.querySelector('#toc-content-drawer>nav'),
$headingElements,
INDEX_OFFSET
);
});
this._tocDialogOnScroll();
this.scrollEventSet.add(this._tocDialogOnScroll);
this._tocOnScroll && this.scrollEventSet.delete(this._tocOnScroll);
this.syncTocHeight();
this.syncTocActiveState();
return;
}
@@ -895,12 +1028,8 @@ class FixIt {
const $toc = document.getElementById('toc-auto');
$toc.style.visibility = 'visible';
animateCSS($toc, ['animate__fadeIn', 'animate__faster'], true);
this._tocOnScroll = this._tocOnScroll || (() => {
this._updateTocActiveState($tocCore, $headingElements, INDEX_OFFSET);
});
this._tocOnScroll();
this.scrollEventSet.add(this._tocOnScroll);
this._tocDialogOnScroll && this.scrollEventSet.delete(this._tocDialogOnScroll);
this.syncTocHeight();
this.syncTocActiveState();
}
// TODO refactor use allow-discrete display property
@@ -931,9 +1060,13 @@ class FixIt {
openButton.addEventListener("click", () => {
dialog.showModal();
openButton.setAttribute('aria-expanded', 'true');
this.syncTocHeight();
this.syncTocActiveState();
const $dialogTocRoot = document.querySelector('#toc-content-drawer > nav');
this.scrollActiveTocLinkIntoView($dialogTocRoot, this.activeTocId, $dialogTocRoot);
document.activeElement?.blur();
});
dialog.addEventListener("click", (e) => {
dialog.addEventListener("click", () => {
dialog.close();
});
dialog.addEventListener("close", () => {
@@ -1473,7 +1606,6 @@ class FixIt {
this.initMathJax();
this.initJsonViewer();
this.initTabEvents(target);
this.initRoleButtons(target);
FileTree.init(target);
window.FixItMermaid?.init?.();
window.FixItAPlayer?.init?.();
@@ -1580,10 +1712,14 @@ class FixIt {
scrollIntoView('body');
});
window.addEventListener('scroll', (event) => {
if (this.disableScrollEvent) {
event.preventDefault();
return;
}
this.newScrollTop = getScrollTop();
const scroll = this.newScrollTop - this.oldScrollTop;
if (Math.abs(scroll) > ACCURACY) {
document.getElementById('mask').click();
this.closeActiveMaskOverlay();
const isScrollingDown = scroll > 0;
forEach($autoHeaders, ($header) => {
if (isScrollingDown) {
@@ -1627,6 +1763,8 @@ class FixIt {
for (let event of this.scrollEventSet) {
event();
}
this.syncTocHeight();
this.syncTocActiveState();
this.oldScrollTop = this.newScrollTop;
}, false);
}
@@ -1642,10 +1780,12 @@ class FixIt {
}
this.initToc();
this.initSearch();
this.syncTocHeight();
this.syncTocActiveState();
const _isMobile = isMobile();
if (_isMobile !== resizeBefore) {
document.getElementById('mask').click();
this.closeActiveMaskOverlay();
resizeBefore = _isMobile;
}
}, 100);
@@ -1656,10 +1796,7 @@ class FixIt {
onClickMask() {
document.getElementById('mask').addEventListener('click', (e) => {
if (!e.target.classList.contains('blur')) return;
for (let event of this.clickMaskEventSet) {
event();
}
e.target.classList.remove('blur');
this.closeActiveMaskOverlay();
}, false);
}
@@ -4,7 +4,7 @@
{{- if $config.copyable -}}
{{- $copyIcon := dict "Class" "fa-regular fa-clone fa-width-auto" | partial "plugin/icon.html" -}}
{{- $copyBtn := printf `<div class="code-copy-btn" aria-label="%s" role="button" tabindex="0" title="%s">%s</div>` (T "assets.copyToClipboard") (T "assets.copyToClipboard") $copyIcon -}}
{{- $copyBtn := printf `<button type="button" class="code-copy-btn" aria-label="%s" title="%s">%s</button>` (T "assets.copyToClipboard") (T "assets.copyToClipboard") $copyIcon -}}
{{- $wrapper = replaceRE `(<div class="code-wrapper">[\s\S]*?<\/div>)` (printf `$1%s` $copyBtn) $wrapper -}}
{{- end -}}
@@ -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 `<div class="code-expand-btn" aria-label="%s" role="button" tabindex="0">%s</div>` (T "assets.expandCode") $expandIcon -}}
{{- $expandBtn := printf `<button type="button" class="code-expand-btn" aria-label="%s">%s</button>` (T "assets.expandCode") $expandIcon -}}
{{- $wrapper = replaceRE `(<div class="code-wrapper">[\s\S]*?<\/div>)` (printf `$1%s` $expandBtn) $wrapper -}}
{{- return $wrapper -}}
+7 -7
View File
@@ -11,34 +11,34 @@
{{- $fullscreenBtn := "" -}}
{{- $foldIcon := dict "Class" "arrow fa-solid fa-chevron-down" | partial "plugin/icon.html" -}}
{{- $titleEl := printf `<span class="code-title">%s</span>` $foldIcon -}}
{{- $ellipsisBtn := printf `<span class="ellipses-btn" aria-label="Show more options" role="button" tabindex="0">%s</span>` (dict "Class" "fa-solid fa-ellipsis-h" | partial "plugin/icon.html") -}}
{{- $ellipsisBtn := printf `<button type="button" class="ellipses-btn" aria-label="Show more options">%s</button>` (dict "Class" "fa-solid fa-ellipsis-h" | partial "plugin/icon.html") -}}
{{- if $config.title -}}
{{- $titleEl = printf `<span class="code-title">%s<span class="title-inner">%s</span></span>` $foldIcon $config.title -}}
{{- end -}}
{{- if $config.linenostoggler -}}
{{- $lineNosIcon := dict "Class" "fa-solid fa-list-ol" | partial "plugin/icon.html" -}}
{{- $lineNosBtn = printf `<span class="action-btn line-nos-btn" aria-label="%s" role="button" tabindex="0" title="%s">%s</span>` (T "assets.toggleLineNumbers") (T "assets.toggleLineNumbers") $lineNosIcon -}}
{{- $lineNosBtn = printf `<button type="button" class="action-btn line-nos-btn" aria-label="%s" title="%s">%s</button>` (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 `<span class="action-btn line-wrap-btn" aria-label="%s" role="button" tabindex="0" title="%s">%s</span>` (T "assets.toggleLineWrap") (T "assets.toggleLineWrap") $lineWrapIcon -}}
{{- $lineWrapBtn = printf `<button type="button" class="action-btn line-wrap-btn" aria-label="%s" title="%s">%s</button>` (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 `<span class="action-btn edit-btn" aria-label="%s" role="button" tabindex="0" title="%s">%s</span>` (T "assets.toggleCodeEditable") (T "assets.toggleCodeEditable") $editIcon -}}
{{- $editBtn = printf `<button type="button" class="action-btn edit-btn" aria-label="%s" title="%s">%s</button>` (T "assets.toggleCodeEditable") (T "assets.toggleCodeEditable") $editIcon -}}
{{- end -}}
{{- if $config.copyable -}}
{{- $copyIcon := dict "Class" "fa-regular fa-clone" | partial "plugin/icon.html" -}}
{{- $copyBtn = printf `<span class="action-btn copy-btn" aria-label="%s" role="button" tabindex="0" title="%s" data-copied-text="%s">%s</span>` (T "assets.copyToClipboard") (T "assets.copyToClipboard") (T "assets.copiedText") $copyIcon -}}
{{- $copyBtn = printf `<button type="button" class="action-btn copy-btn" aria-label="%s" title="%s" data-copied-text="%s">%s</button>` (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 `<span class="action-btn download-btn" aria-label="%s" role="button" tabindex="0" title="%s">%s</span>` (T "assets.downloadCode") (T "assets.downloadCode") $downloadIcon -}}
{{- $downloadBtn = printf `<button type="button" class="action-btn download-btn" aria-label="%s" title="%s">%s</button>` (T "assets.downloadCode") (T "assets.downloadCode") $downloadIcon -}}
{{- end -}}
{{- if $config.fullscreen -}}
{{- $fullscreenIcon := dict "Class" "fa-solid fa-expand" | partial "plugin/icon.html" -}}
{{- $fullscreenBtn = printf `<span class="action-btn fullscreen-btn" aria-label="%s" role="button" tabindex="0" title="%s">%s</span>` (T "assets.toggleCodeFullscreen") (T "assets.toggleCodeFullscreen") $fullscreenIcon -}}
{{- $fullscreenBtn = printf `<button type="button" class="action-btn fullscreen-btn" aria-label="%s" title="%s">%s</button>` (T "assets.toggleCodeFullscreen") (T "assets.toggleCodeFullscreen") $fullscreenIcon -}}
{{- end -}}
{{- with $config.name -}}
{{- $titleEl = replace $titleEl `<span class="code-title">` (printf `<span class="code-title" data-name="%s">` .) -}}
+11 -9
View File
@@ -108,14 +108,16 @@
</span>
</li>
{{- end -}}
<li class="menu-item theme-switch" role="button" tabindex="0" aria-label="{{ T "header.switchTheme" }}" title="{{ T "header.switchTheme" }}">
<li class="menu-item theme-switch-item">
<button type="button" class="theme-switch" aria-label="{{ T "header.switchTheme" }}" title="{{ T "header.switchTheme" }}">
{{- dict "Class" "fa-solid fa-adjust" | partial "plugin/icon.html" -}}
</button>
</li>
{{- if hugo.IsMultilingual -}}
<li class="menu-item language-switch">
<span role="button" tabindex="0" aria-label="{{ T "header.selectLanguage" }}" title="{{ T "header.selectLanguage" }}">
<button type="button" aria-label="{{ T "header.selectLanguage" }}" title="{{ T "header.selectLanguage" }}">
{{- dict "Class" "fa-solid fa-language" | partial "plugin/icon.html" -}}
</span>
</button>
<ul class="sub-menu">
{{- if (eq .Kind "404") -}}
{{- range hugo.Sites -}}
@@ -187,12 +189,12 @@
{{- end -}}
{{- end -}}
</div>
<div class="theme-switch" role="button" tabindex="0" aria-label="{{ T "header.switchTheme" }}">
<button type="button" class="theme-switch" aria-label="{{ T "header.switchTheme" }}">
{{- dict "Class" "fa-solid fa-adjust" | partial "plugin/icon.html" -}}
</div>
<div class="menu-toggle" id="menu-toggle-mobile" role="button" tabindex="0" aria-controls="menu-mobile" aria-expanded="false" aria-labelledby="menu-mobile">
</button>
<button type="button" class="menu-toggle" id="menu-toggle-mobile" aria-controls="menu-mobile" aria-expanded="false" aria-labelledby="menu-mobile">
<span></span><span></span><span></span>
</div>
</button>
</div>
<nav>
<ul class="menu" id="menu-mobile">
@@ -286,10 +288,10 @@
<li class="menu-item menu-system">
{{- if hugo.IsMultilingual -}}
<span class="menu-system-item language-switch">
<span role="button" tabindex="0" aria-label="{{ T "header.selectLanguage" }}" title="{{ T "header.selectLanguage" }}">
<button type="button" aria-label="{{ T "header.selectLanguage" }}" title="{{ T "header.selectLanguage" }}">
{{- .Language.LanguageName -}}
{{- dict "Class" "dropdown-icon fa-solid fa-chevron-down" | partial "plugin/icon.html" -}}
</span>
</button>
<select id="language-select" class="language-select" onchange="location = this.value;">
{{- if eq .Kind "404" -}}
{{- range hugo.Sites -}}
+6 -6
View File
@@ -6,7 +6,7 @@
<div class="fixed-buttons">
{{- /* back to top button */ -}}
{{- if $backToTop.enable -}}
<div class="fixed-button back-to-top animate__faster d-none" role="button" tabindex="0" aria-label="{{ T `baseof.backToTop` }}">
<button type="button" class="fixed-button back-to-top animate__faster d-none" aria-label="{{ T `baseof.backToTop` }}">
{{- dict "Class" "fa-solid fa-arrow-up" | partial "plugin/icon.html" -}}
{{- if $backToTop.scrollpercent -}}
<svg viewBox="0 0 100 100">
@@ -14,19 +14,19 @@
<circle class="progress" cx="50" cy="50" r="50"></circle>
</svg>
{{- end -}}
</div>
</button>
{{- end -}}
{{- /* toc dialog button */ -}}
{{- if .Store.Get "showToc" -}}
<div id="toc-drawer-button" class="fixed-button toc-drawer-button d-none{{ with $params.password }} encrypted-hidden{{ end }}" role="button" tabindex="0" aria-label="{{ T `single.contents` }}" aria-controls="toc-dialog" aria-expanded="false">
<button type="button" id="toc-drawer-button" class="fixed-button toc-drawer-button d-none{{ with $params.password }} encrypted-hidden{{ end }}" aria-label="{{ T `single.contents` }}" aria-controls="toc-dialog" aria-expanded="false">
{{- dict "Class" "fa-solid fa-bars" | partial "plugin/icon.html" -}}
</div>
</button>
{{- end -}}
{{- /* comment button */ -}}
{{- if $comment.enable -}}
<div class="fixed-button view-comments d-none" role="button" tabindex="0" aria-label="{{ T `baseof.viewComments` }}">
<button type="button" class="fixed-button view-comments d-none" aria-label="{{ T `baseof.viewComments` }}">
{{- dict "Class" "fa-solid fa-comment" | partial "plugin/icon.html" -}}
</div>
</button>
{{- end -}}
</div>
{{- end -}}
@@ -1,6 +1,6 @@
{{- /* Diagram copy button */ -}}
{{- $label := .Label | default "Copy Diagram code" -}}
<div class="diagram-copy-btn" aria-label="{{ $label }}" role="button" tabindex="0">
<button type="button" class="diagram-copy-btn" aria-label="{{ $label }}">
{{- dict "Class" "fa-regular fa-clone fa-width-auto" | partial "plugin/icon.html" -}}
</div>
</button>