refactor(a11y): comprehensive WCAG 2.2 AA accessibility improvements (#924)
* refactor(a11y): comprehensive WCAG 2.2 AA accessibility improvements
Add skip-to-content link, landmark regions, ARIA attributes, keyboard
navigation, focus styles, reduced-motion support, and i18n keys across
all layouts, partials, shortcodes, and JS components.
- Add skip nav link in baseof.html and id="content" on all <main> tags
- Fix 404 page lang/dir attributes and add <main> landmark
- Add aria-label to banner close, PDF iframe, search input/results
- Remove aria-hidden from back-to-top button
- Add aria-hidden to decorative external link icon
- Add role="tablist" to tabs, aria-expanded to filetree/dropdowns
- Wrap mermaid diagrams in role="img", asciinema in role="region"
- Change theme toggle <p> items to <button role="menuitem"> with
full keyboard navigation (Arrow/Home/End/Escape)
- Add arrow-key keyboard navigation to tabs component
- Separate sidebar collapsible button from link for independent
keyboard access with aria-expanded sync
- Sync aria-expanded on all dropdown toggles (theme, lang, navbar,
hamburger, page context menu)
- Add aria-live search status announcements
- Add 13 new i18n keys, replace hardcoded aria-label strings
- Add prefers-reduced-motion CSS override and focus-visible base styles
- Add aria-label swap on code copy ("Copied!" feedback for AT)
- Add aria-current to active TOC links
- Wrap filetree in <ul> container for proper list semantics
- Add unique aria-label to blog "Read more" links
- Document accessibility guidelines in AGENTS.md
* feat(a11y): enhance focus styles and accessibility for various components
- Add focus-visible styles to badges, buttons, and links for improved keyboard navigation.
- Update breadcrumb, sidebar, and TOC components to include focus-visible outlines.
- Introduce new classes for focus states in the badge and tabs shortcodes.
- Ensure consistent focus styles across all interactive elements to meet WCAG 2.2 AA standards.
* feat(a11y): implement new focus-visible utilities and enhance accessibility styles
- Introduce new utility classes for focus-visible states to improve keyboard navigation.
- Update various components including badges, buttons, and search inputs to utilize new focus-visible styles.
- Refactor existing focus styles to ensure consistency and compliance with accessibility standards.
- Enhance breadcrumb, sidebar, and TOC components with updated focus-visible classes for better user experience.
* chore: add .gitattributes to collapse generated files in PR diffs
* fix: enhance accessibility and improve documentation
- Added alt attributes to images in multiple language documentation files for better accessibility.
- Updated the navbar title partial to remove unnecessary title attribute.
- Improved search input accessibility by adding autocomplete="off".
- Enhanced search partials in both navbar and sidebar with location context.
- Updated SVG icons in various components to include aria-hidden and focusable attributes for improved accessibility compliance.
* fix: improve giscus theme toggle functionality
- Updated the theme toggle options selector to use a data attribute for better specificity.
- Modified the event listener to use a setTimeout for the theme update, ensuring smoother transitions when the theme switcher is clicked.
* fix: resolve axe-core WCAG AA violations across docs pages
Add aria-labels to Hugo task list checkboxes, fix asciinema player
timer accessible names, make Jupyter output cells keyboard-focusable,
and add missing heading hierarchy in shortcodes docs for fa/ja/zh-cn.
* feat: integrate accessibility testing with Playwright and enhance CI workflow
- Added Playwright configuration for accessibility testing.
- Implemented accessibility tests using axe-core for all English pages.
- Created a GitHub Actions workflow to automate accessibility tests on pull requests.
- Updated package dependencies to include @axe-core/playwright and @playwright/test.
- Enhanced sidebar component with data attributes for improved accessibility styling.
* fix: update base URL and improve accessibility labels across multiple languages
- Changed the base URL in Playwright configuration and CI workflow from localhost:3000 to localhost:1313.
- Added accessibility labels for screen readers in various language files, enhancing user experience for visually impaired users.
- Updated the Asciinema script to dynamically set the playback time label for better accessibility compliance.
* refactor: reorganize accessibility tests and update test directory structure
- Moved accessibility tests from the e2e directory to a new tests directory for better organization.
- Updated the test directory path in Playwright configuration.
- Refactored the accessibility test implementation to improve code clarity and maintainability.
* chore: update .gitignore to include Playwright test output directories
- Added entries for 'playwright-report/' and 'test-results/' to the .gitignore file to prevent cluttering the repository with test artifacts.
* refactor: enhance accessibility and improve focus styles across components
- Removed unused utility for focus visibility in CSS and consolidated focus-visible styles for better maintainability.
- Updated various components to use `role` attributes for improved accessibility, including menu items and buttons.
- Enhanced theme toggle and language switch components with appropriate ARIA roles and attributes for better screen reader support.
- Improved the handling of focus states in the navigation and context menus to ensure a consistent user experience.
* chore: update dependencies and enhance accessibility features
- Updated the 'serve' package version in package.json and package-lock.json for improved performance.
- Removed unused 'xml2js' dependency to streamline the project.
- Enhanced the Playwright configuration to better manage the web server setup for testing.
- Improved accessibility in the language switcher and navigation menu by refining focus management and keyboard interactions.
- Updated the back-to-top button to manage tabindex for better accessibility compliance.
* feat: enhance mobile menu accessibility and keyboard interactions
- Added ARIA attributes to manage visibility of the sidebar on mobile devices.
- Implemented focus management for the sidebar when the menu is toggled.
- Introduced keyboard support to close the menu with the Escape key.
- Improved overall accessibility for the hamburger menu and sidebar interactions.
* fix: refine mobile menu keyboard interaction and enhance navbar accessibility
- Updated the Escape key functionality to close the menu only on mobile devices.
- Added a new ARIA attribute to the hamburger menu button for improved accessibility.
This commit is contained in:
3
.gitattributes
vendored
Normal file
3
.gitattributes
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# Mark generated files so they are collapsed by default in GitHub diffs
|
||||
assets/css/compiled/main.css linguist-generated=true
|
||||
docs/hugo_stats.json linguist-generated=true
|
||||
62
.github/workflows/accessibility.yml
vendored
Normal file
62
.github/workflows/accessibility.yml
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
name: Accessibility Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
concurrency:
|
||||
group: accessibility-${{ github.head_ref || github.ref_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
a11y:
|
||||
runs-on: ubuntu-latest
|
||||
environment: accessibility
|
||||
env:
|
||||
HUGO_VERSION: 0.147.7
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.24"
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
|
||||
- name: Setup Hugo
|
||||
run: |
|
||||
wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \
|
||||
&& sudo dpkg -i ${{ runner.temp }}/hugo.deb
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Install Playwright Chromium
|
||||
run: npx playwright install chromium
|
||||
|
||||
- name: Build site
|
||||
run: npm run build
|
||||
|
||||
- name: Run accessibility tests
|
||||
run: npm run test:a11y
|
||||
|
||||
- name: Upload report
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: accessibility-report
|
||||
path: playwright-report/
|
||||
retention-days: 14
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -3,3 +3,7 @@ public/
|
||||
resources/
|
||||
|
||||
.hugo_build.lock
|
||||
|
||||
# Playwright
|
||||
playwright-report/
|
||||
test-results/
|
||||
|
||||
38
AGENTS.md
38
AGENTS.md
@@ -94,6 +94,28 @@ The `docs/` directory serves as both documentation and testing ground:
|
||||
- Chroma syntax highlighting themes in `assets/css/chroma/`
|
||||
- CSS compilation requires Node.js dependencies (PostCSS, Tailwind CSS v4+)
|
||||
|
||||
#### Rebuilding CSS after template changes
|
||||
|
||||
Tailwind CSS relies on `docs/hugo_stats.json` to know which HTML tags, classes, and IDs are actually used in the built site, so it can tree-shake unused styles. When you modify layouts, partials, or shortcodes you must **regenerate `hugo_stats.json` first**, then rebuild the CSS:
|
||||
|
||||
1. **Generate `docs/hugo_stats.json`** — Run Hugo with the `dev.toml` config (which sets `build.buildStats.enable = true`):
|
||||
|
||||
```bash
|
||||
# Using npm (starts a dev server that writes hugo_stats.json on every rebuild):
|
||||
npm run dev:theme
|
||||
|
||||
# Or a one-shot build using the raw Hugo command:
|
||||
hugo --config=hugo.yaml,../dev.toml --themesDir=../.. --source=docs
|
||||
```
|
||||
|
||||
2. **Build the CSS** — With an up-to-date `hugo_stats.json` in place, compile the stylesheet:
|
||||
|
||||
```bash
|
||||
npm run build:css
|
||||
```
|
||||
|
||||
> **Why two steps?** `dev.toml` mounts `docs/hugo_stats.json` into the Hugo asset pipeline (`assets/notwatching/hugo_stats.json`) and configures a cache-buster so that changes to the stats file trigger a CSS recompile during `dev:theme`. When running outside the dev server you need to perform these steps manually in order.
|
||||
|
||||
### Customization Points
|
||||
|
||||
- Custom partials: `layouts/_partials/custom/`
|
||||
@@ -147,6 +169,22 @@ The `docs/` directory serves as both documentation and testing ground:
|
||||
- Compiled output goes to `assets/css/compiled/main.css`
|
||||
- Prettier formatting for Go templates and code consistency
|
||||
|
||||
### Accessibility (WCAG Compliance)
|
||||
|
||||
All new features and UI changes must follow the [Web Content Accessibility Guidelines (WCAG) 2.2](https://www.w3.org/TR/WCAG22/) at the **AA** conformance level. Key requirements:
|
||||
|
||||
- **Semantic HTML**: Use appropriate elements (`<nav>`, `<main>`, `<aside>`, `<button>`, `<ul>`, etc.) instead of generic `<div>`/`<span>` where applicable.
|
||||
- **ARIA attributes**: Add `aria-label`, `aria-expanded`, `aria-controls`, `aria-current`, `role`, and other ARIA attributes to interactive components (menus, toggles, dropdowns, modals) so screen readers can interpret them.
|
||||
- **Keyboard navigation**: All interactive elements must be reachable and operable via keyboard (`Tab`, `Enter`, `Escape`, arrow keys). Manage focus appropriately when opening/closing menus, modals, and drawers.
|
||||
- **Focus indicators**: Never remove visible focus outlines. Use the existing `hextra-focus` utility or equivalent visible focus ring styles.
|
||||
- **Color contrast**: Text and interactive elements must meet WCAG AA contrast ratios (4.5:1 for normal text, 3:1 for large text). Verify in both light and dark modes.
|
||||
- **Images and icons**: Decorative SVGs/icons should have `aria-hidden="true"`. Meaningful images need descriptive `alt` text.
|
||||
- **Skip links and landmarks**: Preserve existing skip-navigation links and ARIA landmark roles (`role="navigation"`, `role="search"`, etc.).
|
||||
- **Live regions**: Use `aria-live` for dynamic content updates (e.g., search results, status messages) so assistive technology announces changes.
|
||||
- **Form controls**: Associate `<label>` elements with inputs. Provide accessible names for buttons that contain only icons.
|
||||
|
||||
When introducing a new component or modifying an existing one, verify it works with keyboard-only navigation and review the rendered HTML for proper semantics and ARIA usage.
|
||||
|
||||
### Testing & Quality Assurance
|
||||
|
||||
- Test all changes in `docs/` before releasing
|
||||
|
||||
2
assets/css/compiled/main.css
generated
2
assets/css/compiled/main.css
generated
File diff suppressed because one or more lines are too long
@@ -1,7 +1,3 @@
|
||||
.hextra-archive-timeline {
|
||||
border-left: 2px solid rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.dark .hextra-archive-timeline {
|
||||
border-left-color: rgba(255, 255, 255, 0.15);
|
||||
@apply hx:border-l-2 hx:border-black/15 hx:dark:border-white/15;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
li {
|
||||
@apply hx:mx-2.5 hx:wrap-break-word hx:rounded-md hx:contrast-more:border hx:text-gray-800 hx:contrast-more:border-transparent hx:dark:text-gray-300;
|
||||
a {
|
||||
@apply hx:focus-visible:outline-none hx:focus:outline-none hx:block hx:scroll-m-12 hx:px-2.5 hx:py-2;
|
||||
@apply hx:focus-visible:outline-none hx:block hx:scroll-m-12 hx:px-2.5 hx:py-2;
|
||||
}
|
||||
|
||||
.hextra-search-title {
|
||||
|
||||
@@ -9,13 +9,13 @@
|
||||
}
|
||||
|
||||
.hextra-sidebar-container {
|
||||
li > div {
|
||||
li > .hextra-sidebar-children {
|
||||
@apply hx:h-0;
|
||||
}
|
||||
li.open > div {
|
||||
li.open > .hextra-sidebar-children {
|
||||
@apply hx:h-auto hx:pt-1;
|
||||
}
|
||||
li.open > a > span > svg > path {
|
||||
li.open > .hextra-sidebar-item > .hextra-sidebar-collapsible-button > svg > path {
|
||||
@apply hx:rotate-90;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,12 +43,37 @@ body {
|
||||
@apply hx:outline-none hx:ring-2 hx:ring-primary-200 hx:ring-offset-1 hx:ring-offset-primary-300 hx:dark:ring-primary-800 hx:dark:ring-offset-primary-700;
|
||||
}
|
||||
|
||||
@utility hextra-focus-visible {
|
||||
@apply hx:focus-visible:outline-none hx:focus-visible:ring-2 hx:focus-visible:ring-primary-200 hx:focus-visible:ring-offset-1 hx:focus-visible:ring-offset-primary-300 hx:dark:focus-visible:ring-primary-800 hx:dark:focus-visible:ring-offset-primary-700;
|
||||
}
|
||||
|
||||
@utility hextra-focus-visible-inset {
|
||||
@apply hx:focus-visible:outline-none hx:focus-visible:ring-inset hx:focus-visible:ring-2 hx:focus-visible:ring-primary-200 hx:dark:focus-visible:ring-primary-800 hx:focus-visible:ring-offset-0;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
abbr:where([title]) {
|
||||
cursor: help;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*, *::before, *::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
:where(a, button, [role="tab"], [role="menuitem"], [role="menuitemradio"], input, select, textarea, [tabindex="0"]):not(
|
||||
[class*="hextra-focus-visible"]
|
||||
):focus-visible {
|
||||
@apply hx:hextra-focus;
|
||||
}
|
||||
}
|
||||
|
||||
@import "./typography.css";
|
||||
@import "./highlight.css";
|
||||
@import "./components/cards.css";
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
|
||||
span:target + &,
|
||||
:hover > &,
|
||||
&:focus {
|
||||
&:focus-visible {
|
||||
@apply hx:opacity-100;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,17 +6,20 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
document.addEventListener("scroll", (e) => {
|
||||
if (window.scrollY > 300) {
|
||||
backToTop.classList.remove("hx:opacity-0");
|
||||
backToTop.removeAttribute("tabindex");
|
||||
} else {
|
||||
backToTop.classList.add("hx:opacity-0");
|
||||
backToTop.setAttribute("tabindex", "-1");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function scrollUp() {
|
||||
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
window.scroll({
|
||||
top: 0,
|
||||
left: 0,
|
||||
behavior: "smooth",
|
||||
behavior: prefersReducedMotion ? 'auto' : 'smooth',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -25,6 +25,27 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
return svg;
|
||||
}
|
||||
|
||||
// Make scrollable code blocks focusable for keyboard users.
|
||||
const updateScrollableCodeBlocks = () => {
|
||||
document.querySelectorAll('.hextra-code-block pre, .highlight pre').forEach(function (pre) {
|
||||
if (pre.scrollWidth > pre.clientWidth) {
|
||||
pre.setAttribute('tabindex', '0');
|
||||
} else {
|
||||
pre.removeAttribute('tabindex');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
updateScrollableCodeBlocks();
|
||||
|
||||
let resizeRaf;
|
||||
window.addEventListener('resize', () => {
|
||||
if (resizeRaf) {
|
||||
cancelAnimationFrame(resizeRaf);
|
||||
}
|
||||
resizeRaf = requestAnimationFrame(updateScrollableCodeBlocks);
|
||||
});
|
||||
|
||||
document.querySelectorAll('.hextra-code-copy-btn').forEach(function (button) {
|
||||
// Add copy and success icons
|
||||
button.querySelector('.hextra-copy-icon')?.appendChild(getCopyIcon());
|
||||
@@ -52,8 +73,12 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
}
|
||||
navigator.clipboard.writeText(code).then(function () {
|
||||
button.classList.add('copied');
|
||||
var originalLabel = button.getAttribute('aria-label');
|
||||
var copiedLabel = button.dataset.copiedLabel || 'Copied!';
|
||||
button.setAttribute('aria-label', copiedLabel);
|
||||
setTimeout(function () {
|
||||
button.classList.remove('copied');
|
||||
button.setAttribute('aria-label', originalLabel);
|
||||
}, 1000);
|
||||
}).catch(function (err) {
|
||||
console.error('Failed to copy text: ', err);
|
||||
|
||||
@@ -7,7 +7,9 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
Array.from(folder.children).forEach(function (el) {
|
||||
el.dataset.state = el.dataset.state === "open" ? "closed" : "open";
|
||||
});
|
||||
folder.nextElementSibling.dataset.state = folder.nextElementSibling.dataset.state === "open" ? "closed" : "open";
|
||||
var newState = folder.nextElementSibling.dataset.state === "open" ? "closed" : "open";
|
||||
folder.nextElementSibling.dataset.state = newState;
|
||||
folder.setAttribute('aria-expanded', newState === 'open' ? 'true' : 'false');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,25 +1,102 @@
|
||||
(function () {
|
||||
const languageSwitchers = document.querySelectorAll('.hextra-language-switcher');
|
||||
const closeSwitcher = (switcher, focusSwitcher = false) => {
|
||||
switcher.dataset.state = 'closed';
|
||||
switcher.setAttribute('aria-expanded', 'false');
|
||||
const optionsElement = switcher.nextElementSibling;
|
||||
optionsElement.classList.add('hx:hidden');
|
||||
if (focusSwitcher) {
|
||||
switcher.focus();
|
||||
}
|
||||
};
|
||||
|
||||
const openSwitcher = (switcher, focusTarget = "none") => {
|
||||
switcher.dataset.state = 'open';
|
||||
switcher.setAttribute('aria-expanded', 'true');
|
||||
const optionsElement = switcher.nextElementSibling;
|
||||
if (optionsElement.classList.contains('hx:hidden')) {
|
||||
toggleMenu(switcher);
|
||||
} else {
|
||||
resizeMenu(switcher);
|
||||
}
|
||||
|
||||
if (focusTarget !== "none") {
|
||||
const items = Array.from(optionsElement.querySelectorAll('[role="menuitem"]'));
|
||||
if (items.length > 0) {
|
||||
const target = focusTarget === "last" ? items[items.length - 1] : items[0];
|
||||
target.focus();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
languageSwitchers.forEach((switcher) => {
|
||||
switcher.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
switcher.dataset.state = switcher.dataset.state === 'open' ? 'closed' : 'open';
|
||||
if (switcher.dataset.state === 'open') {
|
||||
closeSwitcher(switcher);
|
||||
} else {
|
||||
openSwitcher(switcher);
|
||||
}
|
||||
});
|
||||
|
||||
toggleMenu(switcher);
|
||||
switcher.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'ArrowDown') {
|
||||
e.preventDefault();
|
||||
openSwitcher(switcher, 'first');
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
e.preventDefault();
|
||||
openSwitcher(switcher, 'last');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener("resize", () => languageSwitchers.forEach(resizeMenu))
|
||||
document.querySelectorAll('.hextra-language-options[role=menu]').forEach((menu) => {
|
||||
menu.addEventListener('keydown', (e) => {
|
||||
const items = Array.from(menu.querySelectorAll('[role="menuitem"]'));
|
||||
if (items.length === 0) return;
|
||||
|
||||
// Dismiss language switcher when clicking outside
|
||||
const currentIndex = items.indexOf(document.activeElement);
|
||||
let newIndex;
|
||||
|
||||
switch (e.key) {
|
||||
case 'ArrowDown':
|
||||
e.preventDefault();
|
||||
newIndex = (currentIndex + 1) % items.length;
|
||||
items[newIndex].focus();
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
e.preventDefault();
|
||||
newIndex = (currentIndex - 1 + items.length) % items.length;
|
||||
items[newIndex].focus();
|
||||
break;
|
||||
case 'Home':
|
||||
e.preventDefault();
|
||||
items[0].focus();
|
||||
break;
|
||||
case 'End':
|
||||
e.preventDefault();
|
||||
items[items.length - 1].focus();
|
||||
break;
|
||||
case 'Escape': {
|
||||
e.preventDefault();
|
||||
const switcher = menu.previousElementSibling;
|
||||
if (switcher) {
|
||||
closeSwitcher(switcher, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener("resize", () => languageSwitchers.forEach(resizeMenu));
|
||||
|
||||
// Dismiss language switcher when clicking outside.
|
||||
document.addEventListener('click', (e) => {
|
||||
if (e.target.closest('.hextra-language-switcher') === null) {
|
||||
if (!e.target.closest('.hextra-language-switcher') && !e.target.closest('.hextra-language-options')) {
|
||||
languageSwitchers.forEach((switcher) => {
|
||||
switcher.dataset.state = 'closed';
|
||||
const optionsElement = switcher.nextElementSibling;
|
||||
optionsElement.classList.add('hx:hidden');
|
||||
closeSwitcher(switcher);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -3,6 +3,24 @@
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const menu = document.querySelector('.hextra-hamburger-menu');
|
||||
const sidebarContainer = document.querySelector('.hextra-sidebar-container');
|
||||
const mobileQuery = window.matchMedia('(max-width: 767px)');
|
||||
|
||||
function isMenuOpen() {
|
||||
return menu.querySelector('svg').classList.contains('open');
|
||||
}
|
||||
|
||||
// On mobile, the sidebar is off-screen so hide it from assistive tech
|
||||
function syncAriaHidden() {
|
||||
if (mobileQuery.matches) {
|
||||
sidebarContainer.setAttribute('aria-hidden', isMenuOpen() ? 'false' : 'true');
|
||||
} else {
|
||||
sidebarContainer.removeAttribute('aria-hidden');
|
||||
}
|
||||
}
|
||||
|
||||
// Set initial state
|
||||
syncAriaHidden();
|
||||
mobileQuery.addEventListener('change', syncAriaHidden);
|
||||
|
||||
function toggleMenu() {
|
||||
// Toggle the hamburger menu
|
||||
@@ -15,6 +33,19 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
// When the menu is open, we want to prevent the body from scrolling
|
||||
document.body.classList.toggle('hx:overflow-hidden');
|
||||
document.body.classList.toggle('hx:md:overflow-auto');
|
||||
|
||||
// Sync aria-expanded and aria-hidden
|
||||
const isOpen = isMenuOpen();
|
||||
menu.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
|
||||
syncAriaHidden();
|
||||
|
||||
// Move focus into sidebar when opening, restore when closing
|
||||
if (isOpen) {
|
||||
const firstFocusable = sidebarContainer.querySelector('a, button, input, [tabindex="0"]');
|
||||
if (firstFocusable) firstFocusable.focus();
|
||||
} else {
|
||||
menu.focus();
|
||||
}
|
||||
}
|
||||
|
||||
menu.addEventListener('click', (e) => {
|
||||
@@ -22,6 +53,13 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
toggleMenu();
|
||||
});
|
||||
|
||||
// Close menu on Escape key (mobile only)
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape' && mobileQuery.matches && isMenuOpen()) {
|
||||
toggleMenu();
|
||||
}
|
||||
});
|
||||
|
||||
// Select all anchor tags in the sidebar container
|
||||
const sidebarLinks = sidebarContainer.querySelectorAll('a');
|
||||
|
||||
|
||||
@@ -1,61 +1,125 @@
|
||||
(function () {
|
||||
const hiddenClass = "hx:hidden";
|
||||
const dropdownToggles = document.querySelectorAll(".hextra-nav-menu-toggle");
|
||||
const closeDropdown = (toggle, focusToggle = false) => {
|
||||
toggle.dataset.state = "closed";
|
||||
toggle.setAttribute("aria-expanded", "false");
|
||||
const menuItemsElement = toggle.nextElementSibling;
|
||||
menuItemsElement.classList.add(hiddenClass);
|
||||
if (focusToggle) {
|
||||
toggle.focus();
|
||||
}
|
||||
};
|
||||
|
||||
const openDropdown = (toggle, focusTarget = "none") => {
|
||||
// Close all other dropdowns first.
|
||||
dropdownToggles.forEach((otherToggle) => {
|
||||
if (otherToggle !== toggle) {
|
||||
closeDropdown(otherToggle);
|
||||
}
|
||||
});
|
||||
|
||||
toggle.dataset.state = "open";
|
||||
toggle.setAttribute("aria-expanded", "true");
|
||||
const menuItemsElement = toggle.nextElementSibling;
|
||||
|
||||
// Position dropdown centered with toggle.
|
||||
menuItemsElement.style.position = "absolute";
|
||||
menuItemsElement.style.top = "100%";
|
||||
menuItemsElement.style.left = "50%";
|
||||
menuItemsElement.style.transform = "translateX(-50%)";
|
||||
menuItemsElement.style.zIndex = "1000";
|
||||
menuItemsElement.classList.remove(hiddenClass);
|
||||
|
||||
if (focusTarget !== "none") {
|
||||
const items = Array.from(menuItemsElement.querySelectorAll('[role="menuitem"]'));
|
||||
if (items.length > 0) {
|
||||
const target = focusTarget === "last" ? items[items.length - 1] : items[0];
|
||||
target.focus();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
dropdownToggles.forEach((toggle) => {
|
||||
toggle.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
// Close all other dropdowns first
|
||||
dropdownToggles.forEach((otherToggle) => {
|
||||
if (otherToggle !== toggle) {
|
||||
otherToggle.dataset.state = "closed";
|
||||
const otherMenuItems = otherToggle.nextElementSibling;
|
||||
otherMenuItems.classList.add(hiddenClass);
|
||||
}
|
||||
});
|
||||
|
||||
// Toggle current dropdown
|
||||
// Toggle current dropdown.
|
||||
const isOpen = toggle.dataset.state === "open";
|
||||
toggle.dataset.state = isOpen ? "closed" : "open";
|
||||
const menuItemsElement = toggle.nextElementSibling;
|
||||
|
||||
if (!isOpen) {
|
||||
// Position dropdown centered with toggle
|
||||
menuItemsElement.style.position = "absolute";
|
||||
menuItemsElement.style.top = "100%";
|
||||
menuItemsElement.style.left = "50%";
|
||||
menuItemsElement.style.transform = "translateX(-50%)";
|
||||
menuItemsElement.style.zIndex = "1000";
|
||||
|
||||
// Show dropdown
|
||||
menuItemsElement.classList.remove(hiddenClass);
|
||||
if (isOpen) {
|
||||
closeDropdown(toggle);
|
||||
} else {
|
||||
// Hide dropdown
|
||||
menuItemsElement.classList.add(hiddenClass);
|
||||
openDropdown(toggle);
|
||||
}
|
||||
});
|
||||
|
||||
toggle.addEventListener("keydown", (e) => {
|
||||
if (e.key === "ArrowDown") {
|
||||
e.preventDefault();
|
||||
openDropdown(toggle, "first");
|
||||
} else if (e.key === "ArrowUp") {
|
||||
e.preventDefault();
|
||||
openDropdown(toggle, "last");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Dismiss dropdown when clicking outside
|
||||
document.querySelectorAll(".hextra-nav-menu-items[role=menu]").forEach((menu) => {
|
||||
menu.addEventListener("keydown", (e) => {
|
||||
const items = Array.from(menu.querySelectorAll('[role="menuitem"]'));
|
||||
if (items.length === 0) return;
|
||||
|
||||
const currentIndex = items.indexOf(document.activeElement);
|
||||
let newIndex;
|
||||
|
||||
switch (e.key) {
|
||||
case "ArrowDown":
|
||||
e.preventDefault();
|
||||
newIndex = (currentIndex + 1) % items.length;
|
||||
items[newIndex].focus();
|
||||
break;
|
||||
case "ArrowUp":
|
||||
e.preventDefault();
|
||||
newIndex = (currentIndex - 1 + items.length) % items.length;
|
||||
items[newIndex].focus();
|
||||
break;
|
||||
case "Home":
|
||||
e.preventDefault();
|
||||
items[0].focus();
|
||||
break;
|
||||
case "End":
|
||||
e.preventDefault();
|
||||
items[items.length - 1].focus();
|
||||
break;
|
||||
case "Escape": {
|
||||
e.preventDefault();
|
||||
const toggle = menu.previousElementSibling;
|
||||
if (toggle) {
|
||||
closeDropdown(toggle, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Dismiss dropdown when clicking outside.
|
||||
document.addEventListener("click", (e) => {
|
||||
if (e.target.closest(".hextra-nav-menu-toggle") === null) {
|
||||
if (!e.target.closest(".hextra-nav-menu-toggle") && !e.target.closest(".hextra-nav-menu-items")) {
|
||||
dropdownToggles.forEach((toggle) => {
|
||||
toggle.dataset.state = "closed";
|
||||
const menuItemsElement = toggle.nextElementSibling;
|
||||
menuItemsElement.classList.add(hiddenClass);
|
||||
closeDropdown(toggle);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Close dropdowns on escape key
|
||||
// Close dropdowns on escape key.
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Escape") {
|
||||
dropdownToggles.forEach((toggle) => {
|
||||
toggle.dataset.state = "closed";
|
||||
const menuItemsElement = toggle.nextElementSibling;
|
||||
menuItemsElement.classList.add(hiddenClass);
|
||||
if (toggle.dataset.state === "open") {
|
||||
closeDropdown(toggle, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -67,6 +67,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
dropdownToggles.forEach(t => {
|
||||
if (t !== toggle) {
|
||||
t.dataset.state = 'closed';
|
||||
t.setAttribute('aria-expanded', 'false');
|
||||
const otherContainer = t.closest('.hextra-page-context-menu');
|
||||
const otherMenu = otherContainer.querySelector('.hextra-page-context-menu-dropdown');
|
||||
const otherChevron = t.querySelector('[data-chevron]');
|
||||
@@ -79,6 +80,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
// Toggle current
|
||||
toggle.dataset.state = isOpen ? 'closed' : 'open';
|
||||
toggle.setAttribute('aria-expanded', isOpen ? 'false' : 'true');
|
||||
menu.classList.toggle('hx:hidden', isOpen);
|
||||
|
||||
// Rotate chevron icon
|
||||
@@ -95,6 +97,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
if (isOutside) {
|
||||
dropdownToggles.forEach(toggle => {
|
||||
toggle.dataset.state = 'closed';
|
||||
toggle.setAttribute('aria-expanded', 'false');
|
||||
const container = toggle.closest('.hextra-page-context-menu');
|
||||
const menu = container.querySelector('.hextra-page-context-menu-dropdown');
|
||||
const chevron = toggle.querySelector('[data-chevron]');
|
||||
@@ -106,6 +109,19 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
});
|
||||
|
||||
// Close dropdown on Escape key and return focus to toggle
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
dropdownToggles.forEach(toggle => {
|
||||
if (toggle.dataset.state === 'open') {
|
||||
const container = toggle.closest('.hextra-page-context-menu');
|
||||
closeDropdown(container);
|
||||
toggle.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Helper to close dropdown
|
||||
const closeDropdown = (container) => {
|
||||
if (!container) return;
|
||||
@@ -117,6 +133,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
const chevron = toggle.querySelector('[data-chevron]');
|
||||
toggle.dataset.state = 'closed';
|
||||
toggle.setAttribute('aria-expanded', 'false');
|
||||
menu.classList.add('hx:hidden');
|
||||
if (chevron) {
|
||||
chevron.style.transform = '';
|
||||
|
||||
@@ -8,9 +8,10 @@ function enableCollapsibles() {
|
||||
buttons.forEach(function (button) {
|
||||
button.addEventListener("click", function (e) {
|
||||
e.preventDefault();
|
||||
const list = button.parentElement.parentElement;
|
||||
const list = button.closest('li');
|
||||
if (list) {
|
||||
list.classList.toggle("open")
|
||||
list.classList.toggle("open");
|
||||
button.setAttribute('aria-expanded', list.classList.contains('open') ? 'true' : 'false');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,14 +7,15 @@
|
||||
tab.setAttribute('aria-selected', 'true');
|
||||
tab.tabIndex = 0;
|
||||
} else {
|
||||
tab.removeAttribute('aria-selected');
|
||||
tab.removeAttribute('tabindex');
|
||||
tab.setAttribute('aria-selected', 'false');
|
||||
tab.tabIndex = -1;
|
||||
}
|
||||
});
|
||||
const panelsContainer = container.parentElement.nextElementSibling;
|
||||
if (!panelsContainer) return;
|
||||
Array.from(panelsContainer.children).forEach((panel, i) => {
|
||||
panel.dataset.state = i === index ? 'selected' : '';
|
||||
panel.setAttribute('aria-hidden', i === index ? 'false' : 'true');
|
||||
if (i === index) {
|
||||
panel.tabIndex = 0;
|
||||
} else {
|
||||
@@ -53,5 +54,48 @@
|
||||
updateGroup(container, index);
|
||||
}
|
||||
});
|
||||
|
||||
// Keyboard navigation for tabs
|
||||
button.addEventListener('keydown', function (e) {
|
||||
const container = button.parentElement;
|
||||
const tabs = Array.from(container.querySelectorAll('.hextra-tabs-toggle'));
|
||||
const currentIndex = tabs.indexOf(button);
|
||||
let newIndex;
|
||||
|
||||
switch (e.key) {
|
||||
case 'ArrowRight':
|
||||
case 'ArrowDown':
|
||||
e.preventDefault();
|
||||
newIndex = (currentIndex + 1) % tabs.length;
|
||||
break;
|
||||
case 'ArrowLeft':
|
||||
case 'ArrowUp':
|
||||
e.preventDefault();
|
||||
newIndex = (currentIndex - 1 + tabs.length) % tabs.length;
|
||||
break;
|
||||
case 'Home':
|
||||
e.preventDefault();
|
||||
newIndex = 0;
|
||||
break;
|
||||
case 'End':
|
||||
e.preventDefault();
|
||||
newIndex = tabs.length - 1;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (container.dataset.tabGroup) {
|
||||
const tabGroupValue = container.dataset.tabGroup;
|
||||
const key = encodeURIComponent(tabGroupValue);
|
||||
document
|
||||
.querySelectorAll('[data-tab-group="' + tabGroupValue + '"]')
|
||||
.forEach((grp) => updateGroup(grp, newIndex));
|
||||
localStorage.setItem('hextra-tab-' + key, newIndex.toString());
|
||||
} else {
|
||||
updateGroup(container, newIndex);
|
||||
}
|
||||
tabs[newIndex].focus();
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
15
assets/js/core/task-list.js
Normal file
15
assets/js/core/task-list.js
Normal file
@@ -0,0 +1,15 @@
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
// Hugo task lists render bare checkboxes; provide an accessible name.
|
||||
document.querySelectorAll("main#content li > input[type='checkbox']").forEach(function (checkbox) {
|
||||
if (checkbox.hasAttribute("aria-label") || checkbox.hasAttribute("aria-labelledby")) {
|
||||
return;
|
||||
}
|
||||
var listItem = checkbox.closest("li");
|
||||
if (!listItem) return;
|
||||
|
||||
var labelText = listItem.textContent.replace(/\s+/g, " ").trim();
|
||||
if (labelText) {
|
||||
checkbox.setAttribute("aria-label", labelText);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -4,12 +4,15 @@
|
||||
const themes = ["light", "dark"];
|
||||
|
||||
const themeToggleButtons = document.querySelectorAll(".hextra-theme-toggle");
|
||||
const themeToggleOptions = document.querySelectorAll(".hextra-theme-toggle-options p");
|
||||
const themeToggleOptions = document.querySelectorAll(".hextra-theme-toggle-options button[role=menuitemradio]");
|
||||
|
||||
function applyTheme(theme) {
|
||||
theme = themes.includes(theme) ? theme : "system";
|
||||
|
||||
themeToggleButtons.forEach((btn) => btn.parentElement.dataset.theme = theme );
|
||||
themeToggleOptions.forEach((option) => {
|
||||
option.setAttribute('aria-checked', option.dataset.item === theme ? 'true' : 'false');
|
||||
});
|
||||
|
||||
localStorage.setItem("color-theme", theme);
|
||||
}
|
||||
@@ -36,7 +39,16 @@
|
||||
toggler.addEventListener("click", function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
toggler.dataset.state = toggler.dataset.state === 'open' ? 'closed' : 'open';
|
||||
toggleMenu(toggler);
|
||||
const isOpen = toggler.dataset.state === 'open';
|
||||
toggler.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
|
||||
|
||||
// Focus first menuitem when opening
|
||||
if (isOpen) {
|
||||
const firstItem = toggler.nextElementSibling.querySelector('button[role=menuitemradio]');
|
||||
if (firstItem) firstItem.focus();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -47,11 +59,50 @@
|
||||
if (e.target.closest('.hextra-theme-toggle') === null) {
|
||||
themeToggleButtons.forEach((toggler) => {
|
||||
toggler.dataset.state = 'closed';
|
||||
toggler.setAttribute('aria-expanded', 'false');
|
||||
toggler.nextElementSibling.classList.add('hx:hidden');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Keyboard navigation for the theme menu
|
||||
document.querySelectorAll('.hextra-theme-toggle-options[role=menu]').forEach(function (menu) {
|
||||
menu.addEventListener('keydown', function (e) {
|
||||
const items = Array.from(menu.querySelectorAll('button[role=menuitemradio]'));
|
||||
const currentIndex = items.indexOf(document.activeElement);
|
||||
let newIndex;
|
||||
|
||||
switch (e.key) {
|
||||
case 'ArrowDown':
|
||||
e.preventDefault();
|
||||
newIndex = (currentIndex + 1) % items.length;
|
||||
items[newIndex].focus();
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
e.preventDefault();
|
||||
newIndex = (currentIndex - 1 + items.length) % items.length;
|
||||
items[newIndex].focus();
|
||||
break;
|
||||
case 'Home':
|
||||
e.preventDefault();
|
||||
items[0].focus();
|
||||
break;
|
||||
case 'End':
|
||||
e.preventDefault();
|
||||
items[items.length - 1].focus();
|
||||
break;
|
||||
case 'Escape':
|
||||
e.preventDefault();
|
||||
var toggler = menu.previousElementSibling;
|
||||
toggler.dataset.state = 'closed';
|
||||
toggler.setAttribute('aria-expanded', 'false');
|
||||
menu.classList.add('hx:hidden');
|
||||
toggler.focus();
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Listen for system theme changes
|
||||
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => {
|
||||
if (localStorage.getItem("color-theme") === "system") {
|
||||
|
||||
@@ -47,10 +47,12 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
// Remove active class from previous link
|
||||
if (currentActiveLink) {
|
||||
currentActiveLink.classList.remove("hextra-toc-active");
|
||||
currentActiveLink.removeAttribute("aria-current");
|
||||
}
|
||||
|
||||
// Add active class to current link
|
||||
targetLink.classList.add("hextra-toc-active");
|
||||
targetLink.setAttribute("aria-current", "location");
|
||||
currentActiveLink = targetLink;
|
||||
}
|
||||
},
|
||||
@@ -74,8 +76,10 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
|
||||
if (currentActiveLink) {
|
||||
currentActiveLink.classList.remove("hextra-toc-active");
|
||||
currentActiveLink.removeAttribute("aria-current");
|
||||
}
|
||||
targetLink.classList.add("hextra-toc-active");
|
||||
targetLink.setAttribute("aria-current", "location");
|
||||
currentActiveLink = targetLink;
|
||||
|
||||
// Re-enable observer after scroll settles
|
||||
|
||||
@@ -21,6 +21,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
|
||||
(function () {
|
||||
const searchDataURL = '{{ $searchData.RelPermalink }}';
|
||||
const resultsFoundTemplate = '{{ (T "resultsFound") | default "%d results found" }}';
|
||||
|
||||
const inputElements = document.querySelectorAll('.hextra-search-input');
|
||||
for (const el of inputElements) {
|
||||
@@ -389,6 +390,12 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
|
||||
if (!results.length) {
|
||||
resultsElement.innerHTML = `<span class="hextra-search-no-result">{{ $noResultsFound | safeHTML }}</span>`;
|
||||
// Announce no results to screen readers
|
||||
const wrapper = resultsElement.closest('.hextra-search-wrapper');
|
||||
const statusEl = wrapper ? wrapper.querySelector('.hextra-search-status') : null;
|
||||
if (statusEl) {
|
||||
statusEl.textContent = '{{ $noResultsFound | safeHTML }}';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -472,5 +479,14 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
}
|
||||
resultsElement.appendChild(fragment);
|
||||
resultsElement.dataset.count = results.length;
|
||||
|
||||
// Announce results count to screen readers
|
||||
const wrapper = resultsElement.closest('.hextra-search-wrapper');
|
||||
const statusEl = wrapper ? wrapper.querySelector('.hextra-search-status') : null;
|
||||
if (statusEl) {
|
||||
statusEl.textContent = results.length > 0
|
||||
? resultsFoundTemplate.replace('%d', results.length.toString())
|
||||
: '{{ $noResultsFound | safeHTML }}';
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -14,7 +14,7 @@ prev: /docs
|
||||
|
||||
میتوانید با استفاده از مخزن قالب فوق به سرعت شروع به کار کنید.
|
||||
|
||||
<img src="https://docs.github.com/assets/cb-77734/mw-1440/images/help/repository/use-this-template-button.webp" width="500">
|
||||
<img src="https://docs.github.com/assets/cb-77734/mw-1440/images/help/repository/use-this-template-button.webp" width="500" alt="صفحه مخزن GitHub که دکمه Use this template را نشان میدهد">
|
||||
|
||||
ما یک [گردش کار GitHub Actions](https://docs.github.com/en/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site#publishing-with-a-custom-github-actions-workflow) ارائه کردهایم که میتواند به صورت خودکار سایت شما را ساخته و در GitHub Pages مستقر کند و به صورت رایگان میزبانی کند.
|
||||
برای گزینههای بیشتر، [استقرار سایت](../guide/deploy-site) را بررسی کنید.
|
||||
|
||||
@@ -14,7 +14,7 @@ prev: /docs
|
||||
|
||||
上記のテンプレートリポジトリを使用して、すぐに始めることができます。
|
||||
|
||||
<img src="https://docs.github.com/assets/cb-77734/mw-1440/images/help/repository/use-this-template-button.webp" width="500">
|
||||
<img src="https://docs.github.com/assets/cb-77734/mw-1440/images/help/repository/use-this-template-button.webp" width="500" alt="Use this template ボタンが表示された GitHub リポジトリページ">
|
||||
|
||||
[GitHub Actions ワークフロー](https://docs.github.com/ja/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site#publishing-with-a-custom-github-actions-workflow)を提供しており、サイトを自動的にビルドして GitHub Pages にデプロイし、無料でホストすることができます。
|
||||
その他のオプションについては、[サイトのデプロイ](../guide/deploy-site)を確認してください。
|
||||
|
||||
@@ -14,7 +14,7 @@ prev: /docs
|
||||
|
||||
You could quickly get started by using the above template repository.
|
||||
|
||||
<img src="https://docs.github.com/assets/cb-77734/mw-1440/images/help/repository/use-this-template-button.webp" width="500">
|
||||
<img src="https://docs.github.com/assets/cb-77734/mw-1440/images/help/repository/use-this-template-button.webp" width="500" alt="GitHub repository page showing the Use this template button">
|
||||
|
||||
We have provided a [GitHub Actions workflow](https://docs.github.com/en/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site#publishing-with-a-custom-github-actions-workflow) which can help automatically build and deploy your site to GitHub Pages, and host it for free.
|
||||
For more options, check out [Deploy Site](../guide/deploy-site).
|
||||
|
||||
@@ -14,7 +14,7 @@ prev: /docs
|
||||
|
||||
您可以通过使用上述模板仓库快速开始。
|
||||
|
||||
<img src="https://docs.github.com/assets/cb-77734/mw-1440/images/help/repository/use-this-template-button.webp" width="500">
|
||||
<img src="https://docs.github.com/assets/cb-77734/mw-1440/images/help/repository/use-this-template-button.webp" width="500" alt="显示“Use this template”按钮的 GitHub 仓库页面">
|
||||
|
||||
我们提供了一个[GitHub Actions工作流](https://docs.github.com/en/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site#publishing-with-a-custom-github-actions-workflow),可以帮助自动构建并将您的站点部署到GitHub Pages,并免费托管。
|
||||
更多选项,请查看[部署站点](../guide/deploy-site)。
|
||||
|
||||
@@ -9,6 +9,8 @@ next: /docs/guide/deploy-site
|
||||
این شورتکدها کمتر پایدار در نظر گرفته میشوند و ممکن است هر زمان تغییر کنند.
|
||||
{{< /callout >}}
|
||||
|
||||
## نشان
|
||||
|
||||
### أمثلة
|
||||
|
||||
{{< badge "default" >}}
|
||||
|
||||
@@ -9,6 +9,8 @@ next: /docs/guide/deploy-site
|
||||
これらのショートコードは安定性が低く、いつでも変更される可能性があります。
|
||||
{{< /callout >}}
|
||||
|
||||
## バッジ
|
||||
|
||||
### 例
|
||||
|
||||
{{< badge "default" >}}
|
||||
|
||||
@@ -9,6 +9,8 @@ next: /docs/guide/deploy-site
|
||||
这些短代码稳定性较低,可能随时变更。
|
||||
{{< /callout >}}
|
||||
|
||||
## 徽章
|
||||
|
||||
### 示例
|
||||
|
||||
{{< badge "default" >}}
|
||||
|
||||
47
docs/hugo_stats.json
generated
47
docs/hugo_stats.json
generated
@@ -30,6 +30,7 @@
|
||||
"h5",
|
||||
"h6",
|
||||
"head",
|
||||
"header",
|
||||
"hr",
|
||||
"html",
|
||||
"iframe",
|
||||
@@ -147,10 +148,13 @@
|
||||
"hextra-scrollbar",
|
||||
"hextra-search-input",
|
||||
"hextra-search-results",
|
||||
"hextra-search-status",
|
||||
"hextra-search-wrapper",
|
||||
"hextra-sidebar-active-item",
|
||||
"hextra-sidebar-children",
|
||||
"hextra-sidebar-collapsible-button",
|
||||
"hextra-sidebar-container",
|
||||
"hextra-sidebar-item",
|
||||
"hextra-steps",
|
||||
"hextra-success-icon",
|
||||
"hextra-tabs-panel",
|
||||
@@ -160,10 +164,12 @@
|
||||
"hextra-toc",
|
||||
"hide-tail",
|
||||
"highlight",
|
||||
"hover:hx:no-underline",
|
||||
"hx:-mb-0.5",
|
||||
"hx:-ml-2",
|
||||
"hx:-mr-2",
|
||||
"hx:-mt-20",
|
||||
"hx:-translate-y-1/2",
|
||||
"hx:absolute",
|
||||
"hx:active:bg-gray-400/20",
|
||||
"hx:active:opacity-50",
|
||||
@@ -212,6 +218,7 @@
|
||||
"hx:bg-yellow-50",
|
||||
"hx:block",
|
||||
"hx:border",
|
||||
"hx:border-0",
|
||||
"hx:border-amber-200",
|
||||
"hx:border-b",
|
||||
"hx:border-b-2",
|
||||
@@ -294,9 +301,9 @@
|
||||
"hx:dark:border-white/10",
|
||||
"hx:dark:border-yellow-200/30",
|
||||
"hx:dark:contrast-more:border-neutral-400",
|
||||
"hx:dark:focus:bg-dark",
|
||||
"hx:dark:focus:ring-primary-800",
|
||||
"hx:dark:focus-visible:bg-dark",
|
||||
"hx:dark:from-gray-100",
|
||||
"hx:dark:group-hover:text-gray-50",
|
||||
"hx:dark:hidden",
|
||||
"hx:dark:hover:bg-gray-100/5",
|
||||
"hx:dark:hover:bg-neutral-700",
|
||||
@@ -358,11 +365,19 @@
|
||||
"hx:flex",
|
||||
"hx:flex-col",
|
||||
"hx:flex-wrap",
|
||||
"hx:focus:bg-white",
|
||||
"hx:focus:hextra-focus",
|
||||
"hx:focus:outline-hidden",
|
||||
"hx:focus:ring-4",
|
||||
"hx:focus:ring-primary-300",
|
||||
"hx:focus-visible:bg-primary-500",
|
||||
"hx:focus-visible:bg-white",
|
||||
"hx:focus-visible:fixed",
|
||||
"hx:focus-visible:font-medium",
|
||||
"hx:focus-visible:left-2",
|
||||
"hx:focus-visible:not-sr-only",
|
||||
"hx:focus-visible:px-4",
|
||||
"hx:focus-visible:py-2",
|
||||
"hx:focus-visible:rounded-md",
|
||||
"hx:focus-visible:text-sm",
|
||||
"hx:focus-visible:text-white",
|
||||
"hx:focus-visible:top-2",
|
||||
"hx:focus-visible:z-50",
|
||||
"hx:font-bold",
|
||||
"hx:font-extrabold",
|
||||
"hx:font-medium",
|
||||
@@ -383,10 +398,13 @@
|
||||
"hx:group",
|
||||
"hx:group-[.copied]/copybtn:block",
|
||||
"hx:group-[.copied]/copybtn:hidden",
|
||||
"hx:group-data-[active=true]:dark:text-primary-600",
|
||||
"hx:group-data-[active=true]:text-primary-800",
|
||||
"hx:group-data-[theme=dark]:hidden",
|
||||
"hx:group-data-[theme=light]:hidden",
|
||||
"hx:group-data-[theme=system]:hidden",
|
||||
"hx:group-hover/code:opacity-100",
|
||||
"hx:group-hover:text-gray-900",
|
||||
"hx:group-hover:underline",
|
||||
"hx:group-open:before:rotate-90",
|
||||
"hx:group/code",
|
||||
@@ -403,6 +421,8 @@
|
||||
"hx:h-[18px]",
|
||||
"hx:h-full",
|
||||
"hx:h-px",
|
||||
"hx:hextra-focus-visible",
|
||||
"hx:hextra-focus-visible-inset",
|
||||
"hx:hidden",
|
||||
"hx:hover:bg-gray-100",
|
||||
"hx:hover:bg-gray-800/5",
|
||||
@@ -462,13 +482,17 @@
|
||||
"hx:ltr:pl-5",
|
||||
"hx:ltr:pl-6",
|
||||
"hx:ltr:pl-8",
|
||||
"hx:ltr:pr-0",
|
||||
"hx:ltr:pr-1",
|
||||
"hx:ltr:pr-2",
|
||||
"hx:ltr:pr-4",
|
||||
"hx:ltr:pr-8",
|
||||
"hx:ltr:pr-9",
|
||||
"hx:ltr:right-1.5",
|
||||
"hx:ltr:right-2",
|
||||
"hx:ltr:right-3",
|
||||
"hx:ltr:rotate-180",
|
||||
"hx:ltr:rounded-l-lg",
|
||||
"hx:ltr:rounded-r-lg",
|
||||
"hx:ltr:text-right",
|
||||
"hx:m-[11px]",
|
||||
"hx:max-h-(--menu-height)",
|
||||
@@ -554,6 +578,7 @@
|
||||
"hx:overflow-y-hidden",
|
||||
"hx:overscroll-contain",
|
||||
"hx:overscroll-x-contain",
|
||||
"hx:p-0",
|
||||
"hx:p-0.5",
|
||||
"hx:p-1",
|
||||
"hx:p-1.5",
|
||||
@@ -608,14 +633,17 @@
|
||||
"hx:rtl:before:rotate-180",
|
||||
"hx:rtl:border-r",
|
||||
"hx:rtl:left-1.5",
|
||||
"hx:rtl:left-2",
|
||||
"hx:rtl:left-3",
|
||||
"hx:rtl:md:right-auto",
|
||||
"hx:rtl:ml-auto",
|
||||
"hx:rtl:mr-1",
|
||||
"hx:rtl:mr-3",
|
||||
"hx:rtl:mr-auto",
|
||||
"hx:rtl:pl-1",
|
||||
"hx:rtl:pl-2",
|
||||
"hx:rtl:pl-4",
|
||||
"hx:rtl:pl-8",
|
||||
"hx:rtl:pl-9",
|
||||
"hx:rtl:pr-12",
|
||||
"hx:rtl:pr-16",
|
||||
@@ -625,6 +653,8 @@
|
||||
"hx:rtl:pr-6",
|
||||
"hx:rtl:pr-8",
|
||||
"hx:rtl:rotate-270",
|
||||
"hx:rtl:rounded-l-lg",
|
||||
"hx:rtl:rounded-r-lg",
|
||||
"hx:rtl:text-left",
|
||||
"hx:scroll-my-6",
|
||||
"hx:scroll-py-6",
|
||||
@@ -688,6 +718,7 @@
|
||||
"hx:text-yellow-900",
|
||||
"hx:to-gray-600",
|
||||
"hx:top-0",
|
||||
"hx:top-1/2",
|
||||
"hx:top-16",
|
||||
"hx:top-8",
|
||||
"hx:top-[40%]",
|
||||
|
||||
30
i18n/cs.yaml
30
i18n/cs.yaml
@@ -1,15 +1,43 @@
|
||||
# Accessibility labels (screen reader only)
|
||||
closeBanner: "Zavřít banner"
|
||||
menu: "Menu"
|
||||
mermaidDiagram: "Diagram"
|
||||
pdfViewer: "Prohlížeč PDF"
|
||||
permalinkLabel: "Trvalý odkaz na tuto sekci"
|
||||
playbackTime: "Doba přehrávání"
|
||||
searchResults: "Výsledky vyhledávání"
|
||||
skipToContent: "Přejít na obsah"
|
||||
tableOfContents: "Obsah"
|
||||
terminalRecording: "Záznam terminálu"
|
||||
togglePageContextMenu: "Přepnout kontextové menu stránky"
|
||||
toggleSection: "Přepnout sekci"
|
||||
|
||||
# Accessibility live-region/status text
|
||||
resultsFound: "%d výsledků nalezeno"
|
||||
|
||||
# User-facing UI text
|
||||
archives: "Archiv"
|
||||
backToTop: "Zpět nahoru"
|
||||
changeLanguage: "Změnit jazyk"
|
||||
changeTheme: "Změnit vzhled"
|
||||
copy: "Kopírovat"
|
||||
copied: "Zkopírováno!"
|
||||
copyAsMarkdown: "Kopírovat jako Markdown"
|
||||
copyPage: "Kopírovat stránku"
|
||||
copyCode: "Zkopírovat kód"
|
||||
copyright: "© 2025 Hextra Project."
|
||||
dark: "Tmavý"
|
||||
editThisPage: "Upravit tuto stránku na GitHubu →"
|
||||
lastUpdated: "Naposledy změněno"
|
||||
light: "Světlý"
|
||||
next: "Další"
|
||||
noResultsFound: "Nebylo nic nalezeno."
|
||||
onThisPage: "Na této stránce"
|
||||
tags: "Tagy"
|
||||
more: "Více"
|
||||
poweredBy: "Powered by Hextra"
|
||||
previous: "Předchozí"
|
||||
readMore: "Přečíst víc →"
|
||||
searchPlaceholder: "Hledat..."
|
||||
system: "Systém"
|
||||
tags: "Tagy"
|
||||
viewAsMarkdown: "Zobrazit jako Markdown"
|
||||
|
||||
30
i18n/de.yaml
30
i18n/de.yaml
@@ -1,15 +1,43 @@
|
||||
# Accessibility labels (screen reader only)
|
||||
closeBanner: "Banner schließen"
|
||||
menu: "Menü"
|
||||
mermaidDiagram: "Diagramm"
|
||||
pdfViewer: "PDF-Betrachter"
|
||||
permalinkLabel: "Permalink für diesen Abschnitt"
|
||||
playbackTime: "Wiedergabezeit"
|
||||
searchResults: "Suchergebnisse"
|
||||
skipToContent: "Zum Inhalt springen"
|
||||
tableOfContents: "Inhaltsverzeichnis"
|
||||
terminalRecording: "Terminalaufzeichnung"
|
||||
togglePageContextMenu: "Seitenkontextmenü umschalten"
|
||||
toggleSection: "Abschnitt umschalten"
|
||||
|
||||
# Accessibility live-region/status text
|
||||
resultsFound: "%d Ergebnisse gefunden"
|
||||
|
||||
# User-facing UI text
|
||||
archives: "Archiv"
|
||||
backToTop: "Nach oben"
|
||||
changeLanguage: "Sprache ändern"
|
||||
changeTheme: "Darstellung ändern"
|
||||
copy: "Kopieren"
|
||||
copied: "Kopiert!"
|
||||
copyAsMarkdown: "Als Markdown kopieren"
|
||||
copyPage: "Seite kopieren"
|
||||
copyCode: "Code kopieren"
|
||||
copyright: "© 2025 Hextra Project."
|
||||
dark: "Dunkel"
|
||||
editThisPage: "Diese Seite auf GitHub bearbeiten →"
|
||||
lastUpdated: "Zuletzt aktualisiert am"
|
||||
light: "Hell"
|
||||
next: "Weiter"
|
||||
noResultsFound: "Keine Ergebnisse gefunden."
|
||||
onThisPage: "Auf dieser Seite"
|
||||
tags: "Schlagwörter"
|
||||
more: "Mehr"
|
||||
poweredBy: "Unterstützt durch Hextra"
|
||||
previous: "Zurück"
|
||||
readMore: "Mehr lesen →"
|
||||
searchPlaceholder: "Suchen..."
|
||||
system: "System"
|
||||
tags: "Schlagwörter"
|
||||
viewAsMarkdown: "Als Markdown anzeigen"
|
||||
|
||||
18
i18n/en.yaml
18
i18n/en.yaml
@@ -1,3 +1,21 @@
|
||||
# Accessibility labels (screen reader only)
|
||||
closeBanner: "Close banner"
|
||||
menu: "Menu"
|
||||
mermaidDiagram: "Diagram"
|
||||
pdfViewer: "PDF viewer"
|
||||
permalinkLabel: "Permalink for this section"
|
||||
playbackTime: "Playback time"
|
||||
searchResults: "Search results"
|
||||
skipToContent: "Skip to content"
|
||||
tableOfContents: "Table of contents"
|
||||
terminalRecording: "Terminal recording"
|
||||
togglePageContextMenu: "Toggle page context menu"
|
||||
toggleSection: "Toggle section"
|
||||
|
||||
# Accessibility live-region/status text
|
||||
resultsFound: "%d results found"
|
||||
|
||||
# User-facing UI text
|
||||
archives: "Archives"
|
||||
backToTop: "Scroll to top"
|
||||
changeLanguage: "Change language"
|
||||
|
||||
25
i18n/es.yaml
25
i18n/es.yaml
@@ -1,6 +1,29 @@
|
||||
# Accessibility labels (screen reader only)
|
||||
closeBanner: "Cerrar banner"
|
||||
menu: "Menú"
|
||||
mermaidDiagram: "Diagrama"
|
||||
pdfViewer: "Visor de PDF"
|
||||
permalinkLabel: "Enlace permanente a esta sección"
|
||||
playbackTime: "Tiempo de reproducción"
|
||||
searchResults: "Resultados de búsqueda"
|
||||
skipToContent: "Saltar al contenido"
|
||||
tableOfContents: "Tabla de contenidos"
|
||||
terminalRecording: "Grabación de terminal"
|
||||
togglePageContextMenu: "Alternar menú contextual de la página"
|
||||
toggleSection: "Alternar sección"
|
||||
|
||||
# Accessibility live-region/status text
|
||||
resultsFound: "%d resultados encontrados"
|
||||
|
||||
# User-facing UI text
|
||||
archives: "Archivos"
|
||||
backToTop: "Subir al inicio"
|
||||
changeLanguage: "Cambiar idioma"
|
||||
changeTheme: "Cambiar tema"
|
||||
copy: "Copiar"
|
||||
copied: "¡Copiado!"
|
||||
copyAsMarkdown: "Copiar como Markdown"
|
||||
copyPage: "Copiar página"
|
||||
copyCode: "Copiar código"
|
||||
copyright: "© 2025 Proyecto Hextra."
|
||||
dark: "Oscuro"
|
||||
@@ -10,9 +33,11 @@ light: "Claro"
|
||||
next: "Siguiente"
|
||||
noResultsFound: "No hubo resultados."
|
||||
onThisPage: "En esta página"
|
||||
more: "Más"
|
||||
poweredBy: "Con tecnología de Hextra"
|
||||
previous: "Anterior"
|
||||
readMore: "Leer más →"
|
||||
searchPlaceholder: "Buscar..."
|
||||
system: "Sistema"
|
||||
tags: "Etiquetas"
|
||||
viewAsMarkdown: "Ver como Markdown"
|
||||
|
||||
31
i18n/fa.yaml
31
i18n/fa.yaml
@@ -1,22 +1,43 @@
|
||||
# Accessibility labels (screen reader only)
|
||||
closeBanner: "بستن بنر"
|
||||
menu: "منو"
|
||||
mermaidDiagram: "نمودار"
|
||||
pdfViewer: "نمایشگر PDF"
|
||||
permalinkLabel: "پیوند ثابت به این بخش"
|
||||
playbackTime: "زمان پخش"
|
||||
searchResults: "نتایج جستجو"
|
||||
skipToContent: "رفتن به محتوا"
|
||||
tableOfContents: "فهرست مطالب"
|
||||
terminalRecording: "ضبط ترمینال"
|
||||
togglePageContextMenu: "تغییر وضعیت منوی زمینه صفحه"
|
||||
toggleSection: "تغییر وضعیت بخش"
|
||||
|
||||
# Accessibility live-region/status text
|
||||
resultsFound: "%d نتیجه یافت شد"
|
||||
|
||||
# User-facing UI text
|
||||
archives: "آرشیو"
|
||||
backToTop: "به بالا بروید"
|
||||
changeLanguage: "تغییر زبان"
|
||||
changeTheme: "تغییر تم"
|
||||
copy: "کپی"
|
||||
copied: "کپی شد!"
|
||||
copyAsMarkdown: "کپی به عنوان Markdown"
|
||||
copyCode: "کپی کد"
|
||||
copyPage: "کپی صفحه"
|
||||
copyCode: "کپی کد"
|
||||
copyright: "© ۲۰۲۴ پروژه هگزترا."
|
||||
dark: "تیره"
|
||||
editThisPage: "ویرایش این صفحه در گیتهاب ←"
|
||||
lastUpdated: "آخرین بهروزرسانی در"
|
||||
light: "روشن"
|
||||
next: "بعدی"
|
||||
noResultsFound: "هیچ نتیجهای پیدا نشد."
|
||||
onThisPage: "در این صفحه"
|
||||
more: "بیشتر"
|
||||
tags: "برچسبها"
|
||||
viewAsMarkdown: "مشاهده به عنوان Markdown"
|
||||
poweredBy: "طراحی شده توسط هگزترا"
|
||||
previous: "قبلی"
|
||||
readMore: "ادامه مطلب ←"
|
||||
searchPlaceholder: "جستجو..."
|
||||
previous: "قبلی"
|
||||
next: "بعدی"
|
||||
system: "سیستم"
|
||||
tags: "برچسبها"
|
||||
viewAsMarkdown: "مشاهده به عنوان Markdown"
|
||||
|
||||
27
i18n/fr.yaml
27
i18n/fr.yaml
@@ -1,6 +1,29 @@
|
||||
# Accessibility labels (screen reader only)
|
||||
closeBanner: "Fermer la bannière"
|
||||
menu: "Menu"
|
||||
mermaidDiagram: "Diagramme"
|
||||
pdfViewer: "Lecteur PDF"
|
||||
permalinkLabel: "Lien permanent vers cette section"
|
||||
playbackTime: "Temps de lecture"
|
||||
searchResults: "Résultats de recherche"
|
||||
skipToContent: "Aller au contenu"
|
||||
tableOfContents: "Table des matières"
|
||||
terminalRecording: "Enregistrement du terminal"
|
||||
togglePageContextMenu: "Basculer le menu contextuel de la page"
|
||||
toggleSection: "Basculer la section"
|
||||
|
||||
# Accessibility live-region/status text
|
||||
resultsFound: "%d résultats trouvés"
|
||||
|
||||
# User-facing UI text
|
||||
archives: "Archives"
|
||||
backToTop: "Revenir en haut"
|
||||
changeLanguage: "Changer la langue"
|
||||
changeTheme: "Thème d'affichage"
|
||||
copy: "Copier"
|
||||
copied: "Copié !"
|
||||
copyAsMarkdown: "Copier en Markdown"
|
||||
copyPage: "Copier la page"
|
||||
copyCode: "Copier le code"
|
||||
copyright: "© 2025 Hextra Project."
|
||||
dark: "Sombre"
|
||||
@@ -10,9 +33,11 @@ light: "Clair"
|
||||
next: "Suivant"
|
||||
noResultsFound: "Pas de résultats trouvés"
|
||||
onThisPage: "Sur cette page"
|
||||
more: "Plus"
|
||||
poweredBy: "Propulsé par Hextra"
|
||||
previous: "Précdent"
|
||||
previous: "Précédent"
|
||||
readMore: "Lire plus →"
|
||||
searchPlaceholder: "Rechercher..."
|
||||
system: "Système"
|
||||
tags: "Étiquettes"
|
||||
viewAsMarkdown: "Voir en Markdown"
|
||||
|
||||
30
i18n/he.yaml
30
i18n/he.yaml
@@ -1,15 +1,43 @@
|
||||
# Accessibility labels (screen reader only)
|
||||
closeBanner: "סגור באנר"
|
||||
menu: "תפריט"
|
||||
mermaidDiagram: "תרשים"
|
||||
pdfViewer: "מציג PDF"
|
||||
permalinkLabel: "קישור קבוע לפסקה זו"
|
||||
playbackTime: "זמן ניגון"
|
||||
searchResults: "תוצאות חיפוש"
|
||||
skipToContent: "דלג לתוכן"
|
||||
tableOfContents: "תוכן עניינים"
|
||||
terminalRecording: "הקלטת מסוף"
|
||||
togglePageContextMenu: "הצג/הסתר תפריט ההקשר של הדף"
|
||||
toggleSection: "הצג/הסתר מקטע"
|
||||
|
||||
# Accessibility live-region/status text
|
||||
resultsFound: "%d תוצאות נמצאו"
|
||||
|
||||
# User-facing UI text
|
||||
archives: "ארכיון"
|
||||
backToTop: "גלול למעלה"
|
||||
changeLanguage: "שנה שפה"
|
||||
changeTheme: "שנה ערכת צבעים"
|
||||
copy: "העתק"
|
||||
copied: "הועתק!"
|
||||
copyAsMarkdown: "העתק כ-Markdown"
|
||||
copyPage: "העתק עמוד"
|
||||
copyCode: "העתק קוד"
|
||||
copyright: "© 2025 פרוייקט Hextra"
|
||||
dark: "כהה"
|
||||
editThisPage: "← ערוך עמוד זה בגיטהאב"
|
||||
lastUpdated: "עודכן לאחרונה ב"
|
||||
light: "בהיר"
|
||||
next: "הבא"
|
||||
noResultsFound: "לא נמצאו תוצאות."
|
||||
onThisPage: "בעמוד זה"
|
||||
tags: "תגיות"
|
||||
more: "עוד"
|
||||
poweredBy: "Hextra מופעל על-ידי"
|
||||
previous: "הקודם"
|
||||
readMore: "← קרא עוד"
|
||||
searchPlaceholder: "חיפוש..."
|
||||
system: "מערכת"
|
||||
tags: "תגיות"
|
||||
viewAsMarkdown: "הצג כ-Markdown"
|
||||
|
||||
21
i18n/it.yaml
21
i18n/it.yaml
@@ -1,3 +1,22 @@
|
||||
# Accessibility labels (screen reader only)
|
||||
closeBanner: "Chiudi banner"
|
||||
menu: "Menu"
|
||||
mermaidDiagram: "Diagramma"
|
||||
pdfViewer: "Visualizzatore PDF"
|
||||
permalinkLabel: "Link permanente a questa sezione"
|
||||
playbackTime: "Tempo di riproduzione"
|
||||
searchResults: "Risultati della ricerca"
|
||||
skipToContent: "Vai al contenuto"
|
||||
tableOfContents: "Indice dei contenuti"
|
||||
terminalRecording: "Registrazione del terminale"
|
||||
togglePageContextMenu: "Attiva/disattiva il menu contestuale della pagina"
|
||||
toggleSection: "Attiva/disattiva sezione"
|
||||
|
||||
# Accessibility live-region/status text
|
||||
resultsFound: "%d risultati trovati"
|
||||
|
||||
# User-facing UI text
|
||||
archives: "Archivi"
|
||||
backToTop: "Torna all'inizio"
|
||||
changeLanguage: "Cambia lingua"
|
||||
changeTheme: "Cambia tema"
|
||||
@@ -6,6 +25,7 @@ copied: "Copiato!"
|
||||
copyAsMarkdown: "Copia come Markdown"
|
||||
copyPage: "Copia pagina"
|
||||
copyCode: "Copia codice"
|
||||
copyright: "© 2025 Hextra Project."
|
||||
dark: "Scuro"
|
||||
editThisPage: "Modifica questa pagina su GitHub →"
|
||||
lastUpdated: "Ultimo aggiornamento il"
|
||||
@@ -13,6 +33,7 @@ light: "Chiaro"
|
||||
next: "Successivo"
|
||||
noResultsFound: "Nessun risultato trovato."
|
||||
onThisPage: "In questa pagina"
|
||||
more: "Altro"
|
||||
poweredBy: "Realizzato da Hextra"
|
||||
previous: "Precedente"
|
||||
readMore: "Per saperne di più →"
|
||||
|
||||
31
i18n/ja.yaml
31
i18n/ja.yaml
@@ -1,22 +1,43 @@
|
||||
# Accessibility labels (screen reader only)
|
||||
closeBanner: "バナーを閉じる"
|
||||
menu: "メニュー"
|
||||
mermaidDiagram: "図"
|
||||
pdfViewer: "PDFビューアー"
|
||||
permalinkLabel: "このセクションへのパーマリンク"
|
||||
playbackTime: "再生時間"
|
||||
searchResults: "検索結果"
|
||||
skipToContent: "コンテンツにスキップ"
|
||||
tableOfContents: "目次"
|
||||
terminalRecording: "ターミナル録画"
|
||||
togglePageContextMenu: "ページコンテキストメニューの切り替え"
|
||||
toggleSection: "セクションの切り替え"
|
||||
|
||||
# Accessibility live-region/status text
|
||||
resultsFound: "%d件の結果が見つかりました"
|
||||
|
||||
# User-facing UI text
|
||||
archives: "アーカイブ"
|
||||
backToTop: "トップにスクロール"
|
||||
changeLanguage: "言語を変更"
|
||||
changeTheme: "テーマを変更"
|
||||
copy: "コピー"
|
||||
copied: "コピーしました!"
|
||||
copyAsMarkdown: "Markdownとしてコピー"
|
||||
copyCode: "コードをコピー"
|
||||
copyPage: "ページをコピー"
|
||||
copyCode: "コードをコピー"
|
||||
copyright: "© 2025 Hextra プロジェクト。"
|
||||
dark: "ダーク"
|
||||
editThisPage: "このページをGitHubで編集 →"
|
||||
lastUpdated: "最終更新日"
|
||||
light: "ライト"
|
||||
next: "次へ"
|
||||
noResultsFound: "結果が見つかりませんでした。"
|
||||
onThisPage: "このページの内容"
|
||||
more: "その他"
|
||||
tags: "タグ"
|
||||
viewAsMarkdown: "Markdownとして表示"
|
||||
poweredBy: "提供元 Hextra"
|
||||
previous: "前へ"
|
||||
readMore: "もっと読む →"
|
||||
searchPlaceholder: "検索..."
|
||||
previous: "前へ"
|
||||
next: "次へ"
|
||||
system: "システム"
|
||||
tags: "タグ"
|
||||
viewAsMarkdown: "Markdownとして表示"
|
||||
|
||||
31
i18n/ko.yaml
31
i18n/ko.yaml
@@ -1,14 +1,43 @@
|
||||
# Accessibility labels (screen reader only)
|
||||
closeBanner: "배너 닫기"
|
||||
menu: "메뉴"
|
||||
mermaidDiagram: "다이어그램"
|
||||
pdfViewer: "PDF 뷰어"
|
||||
permalinkLabel: "이 섹션에 대한 고유 링크"
|
||||
playbackTime: "재생 시간"
|
||||
searchResults: "검색 결과"
|
||||
skipToContent: "본문으로 건너뛰기"
|
||||
tableOfContents: "목차"
|
||||
terminalRecording: "터미널 녹화"
|
||||
togglePageContextMenu: "페이지 컨텍스트 메뉴 전환"
|
||||
toggleSection: "섹션 전환"
|
||||
|
||||
# Accessibility live-region/status text
|
||||
resultsFound: "%d개의 결과를 찾았습니다"
|
||||
|
||||
# User-facing UI text
|
||||
archives: "아카이브"
|
||||
backToTop: "맨위로 스크롤"
|
||||
changeLanguage: "언어 변경"
|
||||
changeTheme: "테마 변경"
|
||||
copy: "복사"
|
||||
copied: "복사됨!"
|
||||
copyAsMarkdown: "Markdown으로 복사"
|
||||
copyPage: "페이지 복사"
|
||||
copyCode: "코드 복사"
|
||||
copyright: "© 2025 Hextra Project."
|
||||
dark: "어두운 테마"
|
||||
editThisPage: "GitHub에서 편집하기 →"
|
||||
lastUpdated: "마지막 수정 일자"
|
||||
light: "밝은 테마"
|
||||
next: "다음"
|
||||
noResultsFound: "결과 없음"
|
||||
onThisPage: "페이지 목차"
|
||||
tags: "태그"
|
||||
more: "더보기"
|
||||
poweredBy: "Hextra로 제작됨"
|
||||
previous: "이전"
|
||||
readMore: "더보기 →"
|
||||
searchPlaceholder: "검색..."
|
||||
system: "시스템"
|
||||
tags: "태그"
|
||||
viewAsMarkdown: "Markdown으로 보기"
|
||||
|
||||
31
i18n/nb.yaml
31
i18n/nb.yaml
@@ -1,14 +1,43 @@
|
||||
# Accessibility labels (screen reader only)
|
||||
closeBanner: "Lukk banner"
|
||||
menu: "Meny"
|
||||
mermaidDiagram: "Diagram"
|
||||
pdfViewer: "PDF-visning"
|
||||
permalinkLabel: "Permanent lenke til denne seksjonen"
|
||||
playbackTime: "Avspillingstid"
|
||||
searchResults: "Søkeresultater"
|
||||
skipToContent: "Hopp til innhold"
|
||||
tableOfContents: "Innholdsfortegnelse"
|
||||
terminalRecording: "Terminalopptak"
|
||||
togglePageContextMenu: "Veksle sidekontekstmeny"
|
||||
toggleSection: "Veksle seksjon"
|
||||
|
||||
# Accessibility live-region/status text
|
||||
resultsFound: "%d resultater funnet"
|
||||
|
||||
# User-facing UI text
|
||||
archives: "Arkiv"
|
||||
backToTop: "Gå til toppen"
|
||||
changeLanguage: "Endre språk"
|
||||
changeTheme: "Endre tema"
|
||||
copy: "Kopier"
|
||||
copied: "Kopiert!"
|
||||
copyAsMarkdown: "Kopier som Markdown"
|
||||
copyPage: "Kopier side"
|
||||
copyCode: "Kopier kode"
|
||||
copyright: "© 2025 Hextra-prosjektet."
|
||||
dark: "Mørk"
|
||||
editThisPage: "Rediger denne siden på GitHub →"
|
||||
lastUpdated: "Sist oppdatert"
|
||||
light: "Lys"
|
||||
next: "Neste"
|
||||
noResultsFound: "Fant ingen treff."
|
||||
onThisPage: "På denne siden"
|
||||
tags: "Stikkord"
|
||||
more: "Mer"
|
||||
poweredBy: "Powered by Hextra"
|
||||
previous: "Forrige"
|
||||
readMore: "Les mer →"
|
||||
searchPlaceholder: "Søk..."
|
||||
system: "System"
|
||||
tags: "Stikkord"
|
||||
viewAsMarkdown: "Vis som Markdown"
|
||||
|
||||
30
i18n/nl.yaml
30
i18n/nl.yaml
@@ -1,15 +1,43 @@
|
||||
# Accessibility labels (screen reader only)
|
||||
closeBanner: "Banner sluiten"
|
||||
menu: "Menu"
|
||||
mermaidDiagram: "Diagram"
|
||||
pdfViewer: "PDF-weergave"
|
||||
permalinkLabel: "Permanente link naar deze sectie"
|
||||
playbackTime: "Afspeeltijd"
|
||||
searchResults: "Zoekresultaten"
|
||||
skipToContent: "Ga naar inhoud"
|
||||
tableOfContents: "Inhoudsopgave"
|
||||
terminalRecording: "Terminalopname"
|
||||
togglePageContextMenu: "Paginacontextmenu wisselen"
|
||||
toggleSection: "Sectie wisselen"
|
||||
|
||||
# Accessibility live-region/status text
|
||||
resultsFound: "%d resultaten gevonden"
|
||||
|
||||
# User-facing UI text
|
||||
archives: "Archief"
|
||||
backToTop: "Terug naar boven"
|
||||
changeLanguage: "Taal veranderen"
|
||||
changeTheme: "Thema aanpassen"
|
||||
copy: "Kopiëren"
|
||||
copied: "Gekopieerd!"
|
||||
copyAsMarkdown: "Kopieer als Markdown"
|
||||
copyPage: "Pagina kopiëren"
|
||||
copyCode: "Kopieer code"
|
||||
copyright: "© 2025 Hextra Project."
|
||||
dark: "Donker"
|
||||
editThisPage: "Bewerk deze pagina op GitHub →"
|
||||
lastUpdated: "Laatst bijgewerkt op"
|
||||
light: "Licht"
|
||||
next: "Volgende"
|
||||
noResultsFound: "Geen resultaten gevonden."
|
||||
onThisPage: "Op deze pagina"
|
||||
tags: "Labels"
|
||||
more: "Meer"
|
||||
poweredBy: "Mogelijk gemaakt door Hextra"
|
||||
previous: "Vorige"
|
||||
readMore: "Lees meer →"
|
||||
searchPlaceholder: "Zoeken..."
|
||||
system: "Systeem"
|
||||
tags: "Labels"
|
||||
viewAsMarkdown: "Bekijk als Markdown"
|
||||
|
||||
31
i18n/nn.yaml
31
i18n/nn.yaml
@@ -1,14 +1,43 @@
|
||||
# Accessibility labels (screen reader only)
|
||||
closeBanner: "Lukk banner"
|
||||
menu: "Meny"
|
||||
mermaidDiagram: "Diagram"
|
||||
pdfViewer: "PDF-vising"
|
||||
permalinkLabel: "Permanent lenkje til denne seksjonen"
|
||||
playbackTime: "Avspelingstid"
|
||||
searchResults: "Søkjeresultat"
|
||||
skipToContent: "Hopp til innhald"
|
||||
tableOfContents: "Innhaldsliste"
|
||||
terminalRecording: "Terminalopptak"
|
||||
togglePageContextMenu: "Veksle sidekontekstmeny"
|
||||
toggleSection: "Veksle seksjon"
|
||||
|
||||
# Accessibility live-region/status text
|
||||
resultsFound: "%d resultat funne"
|
||||
|
||||
# User-facing UI text
|
||||
archives: "Arkiv"
|
||||
backToTop: "Gå til toppen"
|
||||
changeLanguage: "Endre språk"
|
||||
changeTheme: "Endre tema"
|
||||
copy: "Kopier"
|
||||
copied: "Kopiert!"
|
||||
copyAsMarkdown: "Kopier som Markdown"
|
||||
copyPage: "Kopier side"
|
||||
copyCode: "Kopier kode"
|
||||
copyright: "© 2025 Hextra-prosjektet."
|
||||
dark: "Mørk"
|
||||
editThisPage: "Rediger denne sida på GitHub →"
|
||||
lastUpdated: "Sist oppdatert"
|
||||
light: "Ljos"
|
||||
next: "Neste"
|
||||
noResultsFound: "Fann ingen treff."
|
||||
onThisPage: "På denne sida"
|
||||
tags: "Stikkord"
|
||||
more: "Meir"
|
||||
poweredBy: "Powered by Hextra"
|
||||
previous: "Førre"
|
||||
readMore: "Les meir →"
|
||||
searchPlaceholder: "Søk..."
|
||||
system: "System"
|
||||
tags: "Stikkord"
|
||||
viewAsMarkdown: "Vis som Markdown"
|
||||
|
||||
31
i18n/pt.yaml
31
i18n/pt.yaml
@@ -1,14 +1,43 @@
|
||||
# Accessibility labels (screen reader only)
|
||||
closeBanner: "Fechar banner"
|
||||
menu: "Menu"
|
||||
mermaidDiagram: "Diagrama"
|
||||
pdfViewer: "Visualizador de PDF"
|
||||
permalinkLabel: "Link permanente para esta secção"
|
||||
playbackTime: "Tempo de reprodução"
|
||||
searchResults: "Resultados da pesquisa"
|
||||
skipToContent: "Saltar para o conteúdo"
|
||||
tableOfContents: "Índice"
|
||||
terminalRecording: "Gravação de terminal"
|
||||
togglePageContextMenu: "Alternar menu de contexto da página"
|
||||
toggleSection: "Alternar secção"
|
||||
|
||||
# Accessibility live-region/status text
|
||||
resultsFound: "%d resultados encontrados"
|
||||
|
||||
# User-facing UI text
|
||||
archives: "Arquivo"
|
||||
backToTop: "Voltar ao topo"
|
||||
changeLanguage: "Mudar a língua"
|
||||
changeTheme: "Mudar tema"
|
||||
copy: "Copiar"
|
||||
copied: "Copiado!"
|
||||
copyAsMarkdown: "Copiar como Markdown"
|
||||
copyPage: "Copiar página"
|
||||
copyCode: "Copiar código"
|
||||
copyright: "© 2025 Projecto Hextra."
|
||||
dark: "Escuro"
|
||||
editThisPage: "Edita esta página no GitHub →"
|
||||
lastUpdated: "Última modificação"
|
||||
light: "Claro"
|
||||
next: "Seguinte"
|
||||
noResultsFound: "Nenhum resultado encontrado"
|
||||
onThisPage: "Nesta página"
|
||||
tags: "Etiquetas"
|
||||
more: "Mais"
|
||||
poweredBy: "Com a tecnologia de Hextra"
|
||||
previous: "Anterior"
|
||||
readMore: "Ler mais →"
|
||||
searchPlaceholder: "Procurar..."
|
||||
system: "Sistema"
|
||||
tags: "Etiquetas"
|
||||
viewAsMarkdown: "Ver como Markdown"
|
||||
|
||||
30
i18n/ro.yaml
30
i18n/ro.yaml
@@ -1,15 +1,43 @@
|
||||
# Accessibility labels (screen reader only)
|
||||
closeBanner: "Închide bannerul"
|
||||
menu: "Meniu"
|
||||
mermaidDiagram: "Diagramă"
|
||||
pdfViewer: "Vizualizator PDF"
|
||||
permalinkLabel: "Link permanent către această secțiune"
|
||||
playbackTime: "Timp de redare"
|
||||
searchResults: "Rezultatele căutării"
|
||||
skipToContent: "Salt la conținut"
|
||||
tableOfContents: "Cuprins"
|
||||
terminalRecording: "Înregistrare terminal"
|
||||
togglePageContextMenu: "Comutare meniu contextual pagină"
|
||||
toggleSection: "Comutare secțiune"
|
||||
|
||||
# Accessibility live-region/status text
|
||||
resultsFound: "%d rezultate găsite"
|
||||
|
||||
# User-facing UI text
|
||||
archives: "Arhivă"
|
||||
backToTop: "Înapoi sus"
|
||||
changeLanguage: "Schimbă limba"
|
||||
changeTheme: "Schimbă tema"
|
||||
copy: "Copiază"
|
||||
copied: "Copiat!"
|
||||
copyAsMarkdown: "Copiază ca Markdown"
|
||||
copyPage: "Copiază pagina"
|
||||
copyCode: "Copiază codul"
|
||||
copyright: "© 2025 Hextra Project."
|
||||
dark: "Întuneric"
|
||||
editThisPage: "Editați această pagină pe GitHub ←"
|
||||
lastUpdated: "Ultima actualizare la"
|
||||
light: "Lumină"
|
||||
next: "Următor"
|
||||
noResultsFound: "Nici un rezultat găsit."
|
||||
onThisPage: "Pe această pagină"
|
||||
tags: "Etichete"
|
||||
more: "Mai mult"
|
||||
poweredBy: "Susținut de Hextra"
|
||||
previous: "Anterior"
|
||||
readMore: "Citește mai mult ←"
|
||||
searchPlaceholder: "Caută..."
|
||||
system: "Sistem"
|
||||
tags: "Etichete"
|
||||
viewAsMarkdown: "Vizualizează ca Markdown"
|
||||
|
||||
58
i18n/ru.yaml
58
i18n/ru.yaml
@@ -1,15 +1,43 @@
|
||||
backToTop: 'Прокрутить к началу'
|
||||
changeLanguage: 'Изменить язык'
|
||||
changeTheme: 'Изменить тему'
|
||||
copyCode: 'Скопировать код'
|
||||
copyright: '2025 Проект Hextra.'
|
||||
dark: 'Темная'
|
||||
editThisPage: 'Отредактировать страницу на GitHub →'
|
||||
lastUpdated: 'Последнее обновление'
|
||||
light: 'Светлая'
|
||||
noResultsFound: 'Ничего не найдено.'
|
||||
onThisPage: 'На этой странице'
|
||||
tags: 'Теги'
|
||||
poweredBy: 'При поддержке Hextra'
|
||||
readMore: 'Читать далее →'
|
||||
searchPlaceholder: 'Поиск...'
|
||||
# Accessibility labels (screen reader only)
|
||||
closeBanner: "Закрыть баннер"
|
||||
menu: "Меню"
|
||||
mermaidDiagram: "Диаграмма"
|
||||
pdfViewer: "Просмотр PDF"
|
||||
permalinkLabel: "Постоянная ссылка на этот раздел"
|
||||
playbackTime: "Время воспроизведения"
|
||||
searchResults: "Результаты поиска"
|
||||
skipToContent: "Перейти к содержимому"
|
||||
tableOfContents: "Содержание"
|
||||
terminalRecording: "Запись терминала"
|
||||
togglePageContextMenu: "Переключить контекстное меню страницы"
|
||||
toggleSection: "Переключить раздел"
|
||||
|
||||
# Accessibility live-region/status text
|
||||
resultsFound: "%d результатов найдено"
|
||||
|
||||
# User-facing UI text
|
||||
archives: "Архив"
|
||||
backToTop: "Прокрутить к началу"
|
||||
changeLanguage: "Изменить язык"
|
||||
changeTheme: "Изменить тему"
|
||||
copy: "Копировать"
|
||||
copied: "Скопировано!"
|
||||
copyAsMarkdown: "Копировать как Markdown"
|
||||
copyPage: "Копировать страницу"
|
||||
copyCode: "Скопировать код"
|
||||
copyright: "2025 Проект Hextra."
|
||||
dark: "Темная"
|
||||
editThisPage: "Отредактировать страницу на GitHub →"
|
||||
lastUpdated: "Последнее обновление"
|
||||
light: "Светлая"
|
||||
next: "Далее"
|
||||
noResultsFound: "Ничего не найдено."
|
||||
onThisPage: "На этой странице"
|
||||
more: "Ещё"
|
||||
poweredBy: "При поддержке Hextra"
|
||||
previous: "Назад"
|
||||
readMore: "Читать далее →"
|
||||
searchPlaceholder: "Поиск..."
|
||||
system: "Система"
|
||||
tags: "Теги"
|
||||
viewAsMarkdown: "Просмотреть как Markdown"
|
||||
|
||||
31
i18n/sw.yaml
31
i18n/sw.yaml
@@ -1,14 +1,43 @@
|
||||
# Accessibility labels (screen reader only)
|
||||
closeBanner: "Funga bango"
|
||||
menu: "Menyu"
|
||||
mermaidDiagram: "Mchoro"
|
||||
pdfViewer: "Kitazamaji cha PDF"
|
||||
permalinkLabel: "Kiungo cha kudumu kwa sehemu hii"
|
||||
playbackTime: "Muda wa kucheza"
|
||||
searchResults: "Matokeo ya utafutaji"
|
||||
skipToContent: "Ruka hadi maudhui"
|
||||
tableOfContents: "Jedwali la yaliyomo"
|
||||
terminalRecording: "Rekodi ya terminal"
|
||||
togglePageContextMenu: "Badili menyu ya muktadha wa ukurasa"
|
||||
toggleSection: "Badili sehemu"
|
||||
|
||||
# Accessibility live-region/status text
|
||||
resultsFound: "Matokeo %d yamepatikana"
|
||||
|
||||
# User-facing UI text
|
||||
archives: "Kumbukumbu"
|
||||
backToTop: "Tembeza hadi juu"
|
||||
changeLanguage: "Badilisha lugha"
|
||||
changeTheme: "Badilisha mandhari"
|
||||
copy: "Nakili"
|
||||
copied: "Imenakiliwa!"
|
||||
copyAsMarkdown: "Nakili kama Markdown"
|
||||
copyPage: "Nakili ukurasa"
|
||||
copyCode: "Nakili msimbo"
|
||||
copyright: "© 2025 Hextra Project."
|
||||
dark: "Meusi"
|
||||
editThisPage: "Hariri ukurasa huu kwenye GitHub →"
|
||||
lastUpdated: "Ilisasishwa mwisho"
|
||||
light: "Meupe"
|
||||
next: "Ifuatayo"
|
||||
noResultsFound: "Hakuna matokeo yalipopatikana."
|
||||
onThisPage: "Kwenye ukurasa huu"
|
||||
tags: "Lebo"
|
||||
more: "Zaidi"
|
||||
poweredBy: "Inaendeshwa na Hextra"
|
||||
previous: "Iliyotangulia"
|
||||
readMore: "Soma zaidi →"
|
||||
searchPlaceholder: "Tafuta..."
|
||||
system: "Mfumo"
|
||||
tags: "Lebo"
|
||||
viewAsMarkdown: "Tazama kama Markdown"
|
||||
|
||||
30
i18n/uk.yaml
30
i18n/uk.yaml
@@ -1,15 +1,43 @@
|
||||
# Accessibility labels (screen reader only)
|
||||
closeBanner: "Закрити банер"
|
||||
menu: "Меню"
|
||||
mermaidDiagram: "Діаграма"
|
||||
pdfViewer: "Перегляд PDF"
|
||||
permalinkLabel: "Постійне посилання на цей розділ"
|
||||
playbackTime: "Час відтворення"
|
||||
searchResults: "Результати пошуку"
|
||||
skipToContent: "Перейти до вмісту"
|
||||
tableOfContents: "Зміст"
|
||||
terminalRecording: "Запис терміналу"
|
||||
togglePageContextMenu: "Перемкнути контекстне меню сторінки"
|
||||
toggleSection: "Перемкнути розділ"
|
||||
|
||||
# Accessibility live-region/status text
|
||||
resultsFound: "%d результатів знайдено"
|
||||
|
||||
# User-facing UI text
|
||||
archives: "Архів"
|
||||
backToTop: "Прокрутити до початку"
|
||||
changeLanguage: "Змінити мову"
|
||||
changeTheme: "Змінити тему"
|
||||
copy: "Копіювати"
|
||||
copied: "Скопійовано!"
|
||||
copyAsMarkdown: "Копіювати як Markdown"
|
||||
copyPage: "Копіювати сторінку"
|
||||
copyCode: "Скопіювати код"
|
||||
copyright: "2025 Проєкт Hextra."
|
||||
dark: "Темна"
|
||||
editThisPage: "Редагувати цю сторінку на GitHub →"
|
||||
lastUpdated: "Востаннє оновлено"
|
||||
light: "Світла"
|
||||
next: "Далі"
|
||||
noResultsFound: "Не знайдено результатів"
|
||||
onThisPage: "На цій сторінці"
|
||||
tags: "Теги"
|
||||
more: "Ще"
|
||||
poweredBy: "Працює завдяки Hextra"
|
||||
previous: "Назад"
|
||||
readMore: "Читати більше →"
|
||||
searchPlaceholder: "Пошук..."
|
||||
system: "Система"
|
||||
tags: "Теги"
|
||||
viewAsMarkdown: "Переглянути як Markdown"
|
||||
|
||||
31
i18n/vi.yaml
31
i18n/vi.yaml
@@ -1,14 +1,43 @@
|
||||
# Accessibility labels (screen reader only)
|
||||
closeBanner: "Đóng biểu ngữ"
|
||||
menu: "Menu"
|
||||
mermaidDiagram: "Sơ đồ"
|
||||
pdfViewer: "Trình xem PDF"
|
||||
permalinkLabel: "Liên kết cố định đến phần này"
|
||||
playbackTime: "Thời gian phát"
|
||||
searchResults: "Kết quả tìm kiếm"
|
||||
skipToContent: "Chuyển đến nội dung"
|
||||
tableOfContents: "Mục lục"
|
||||
terminalRecording: "Bản ghi terminal"
|
||||
togglePageContextMenu: "Bật/tắt menu ngữ cảnh trang"
|
||||
toggleSection: "Bật/tắt mục"
|
||||
|
||||
# Accessibility live-region/status text
|
||||
resultsFound: "%d kết quả được tìm thấy"
|
||||
|
||||
# User-facing UI text
|
||||
archives: "Lưu trữ"
|
||||
backToTop: "Lướt lên đầu trang"
|
||||
changeLanguage: "Đổi ngôn ngữ"
|
||||
changeTheme: "Đổi chủ đề"
|
||||
copy: "Sao chép"
|
||||
copied: "Đã sao chép!"
|
||||
copyAsMarkdown: "Sao chép dạng Markdown"
|
||||
copyPage: "Sao chép trang"
|
||||
copyCode: "Sao chép mã"
|
||||
copyright: "© 2025 Hextra Project."
|
||||
dark: "Tối"
|
||||
editThisPage: "Sửa trang này trên GitHub →"
|
||||
lastUpdated: "Lần cuối cập nhật lúc"
|
||||
light: "Sáng"
|
||||
next: "Tiếp"
|
||||
noResultsFound: "Không tìm thấy kết quả."
|
||||
onThisPage: "Ở trang này"
|
||||
tags: "Thẻ"
|
||||
more: "Thêm"
|
||||
poweredBy: "Chạy bởi Hextra"
|
||||
previous: "Trước"
|
||||
readMore: "Đọc thêm →"
|
||||
searchPlaceholder: "Tìm kiếm..."
|
||||
system: "Hệ thống"
|
||||
tags: "Thẻ"
|
||||
viewAsMarkdown: "Xem dạng Markdown"
|
||||
|
||||
@@ -1,23 +1,43 @@
|
||||
# Accessibility labels (screen reader only)
|
||||
closeBanner: "关闭横幅"
|
||||
menu: "菜单"
|
||||
mermaidDiagram: "图表"
|
||||
pdfViewer: "PDF 查看器"
|
||||
permalinkLabel: "此章节的永久链接"
|
||||
playbackTime: "播放时间"
|
||||
searchResults: "搜索结果"
|
||||
skipToContent: "跳至内容"
|
||||
tableOfContents: "目录"
|
||||
terminalRecording: "终端录像"
|
||||
togglePageContextMenu: "切换页面上下文菜单"
|
||||
toggleSection: "切换章节"
|
||||
|
||||
# Accessibility live-region/status text
|
||||
resultsFound: "找到 %d 个结果"
|
||||
|
||||
# User-facing UI text
|
||||
archives: "归档"
|
||||
backToTop: "返回顶部"
|
||||
changeLanguage: "切换语言"
|
||||
changeTheme: "切换主题"
|
||||
copy: "复制"
|
||||
copied: "已复制!"
|
||||
copyAsMarkdown: "复制为 Markdown"
|
||||
copyCode: "复制代码"
|
||||
copyPage: "复制页面"
|
||||
copyCode: "复制代码"
|
||||
copyright: "© 2025 Hextra Project."
|
||||
dark: "深色"
|
||||
editThisPage: "在 GitHub 上编辑此页 →"
|
||||
lastUpdated: "最后更新于"
|
||||
light: "浅色"
|
||||
next: "下一页"
|
||||
noResultsFound: "无结果"
|
||||
onThisPage: "此页上"
|
||||
more: "更多"
|
||||
tags: "标签"
|
||||
viewAsMarkdown: "以 Markdown 查看"
|
||||
poweredBy: "由 Hextra 驱动"
|
||||
previous: "上一页"
|
||||
readMore: "更多 →"
|
||||
searchPlaceholder: "搜索文档..."
|
||||
previous: "上一页"
|
||||
next: "下一页"
|
||||
system: "跟随系统"
|
||||
tags: "标签"
|
||||
viewAsMarkdown: "以 Markdown 查看"
|
||||
|
||||
@@ -1,14 +1,43 @@
|
||||
# Accessibility labels (screen reader only)
|
||||
closeBanner: "關閉橫幅"
|
||||
menu: "選單"
|
||||
mermaidDiagram: "圖表"
|
||||
pdfViewer: "PDF 檢視器"
|
||||
permalinkLabel: "此章節的永久連結"
|
||||
playbackTime: "播放時間"
|
||||
searchResults: "搜尋結果"
|
||||
skipToContent: "跳至內容"
|
||||
tableOfContents: "目錄"
|
||||
terminalRecording: "終端機錄影"
|
||||
togglePageContextMenu: "切換頁面內容選單"
|
||||
toggleSection: "切換章節"
|
||||
|
||||
# Accessibility live-region/status text
|
||||
resultsFound: "找到 %d 個結果"
|
||||
|
||||
# User-facing UI text
|
||||
archives: "歸檔"
|
||||
backToTop: "返回頂部"
|
||||
changeLanguage: "切換語言"
|
||||
changeTheme: "切換主題"
|
||||
copy: "複製"
|
||||
copied: "已複製!"
|
||||
copyAsMarkdown: "複製為 Markdown"
|
||||
copyPage: "複製頁面"
|
||||
copyCode: "複製程式碼"
|
||||
copyright: "© 2025 Hextra Project."
|
||||
dark: "深色"
|
||||
editThisPage: "在 GitHub 上編輯此頁 →"
|
||||
lastUpdated: "最後更新於"
|
||||
light: "淺色"
|
||||
next: "下一頁"
|
||||
noResultsFound: "無結果"
|
||||
onThisPage: "此頁上"
|
||||
tags: "標籤"
|
||||
more: "更多"
|
||||
poweredBy: "由 Hextra 驅動"
|
||||
previous: "上一頁"
|
||||
readMore: "更多 →"
|
||||
searchPlaceholder: "搜尋文檔..."
|
||||
system: "系統"
|
||||
tags: "標籤"
|
||||
viewAsMarkdown: "以 Markdown 檢視"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<html lang="{{ .Site.Language.Lang | default "en" }}" dir="{{ .Site.Language.LanguageDirection | default "ltr" }}">
|
||||
<body
|
||||
style='font-family:system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"; height:100vh; text-align:center; display:flex; flex-direction:column; align-items:center; justify-content:center'
|
||||
>
|
||||
<div>
|
||||
<main id="content">
|
||||
<style>
|
||||
body {
|
||||
color: #000;
|
||||
@@ -35,6 +35,6 @@
|
||||
<div style="display: inline-block; text-align: left">
|
||||
<h2 style="font-size: 14px; font-weight: 400; line-height: 49px; margin: 0">This page could not be found.</h2>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<pre class="mermaid hx:mt-6">
|
||||
{{ .Inner | htmlEscape | safeHTML }}
|
||||
</pre>
|
||||
<div role="img" aria-label="{{ (T "mermaidDiagram") | default "Diagram" }}">
|
||||
<pre class="mermaid hx:mt-6">
|
||||
{{ .Inner | htmlEscape | safeHTML }}
|
||||
</pre>
|
||||
</div>
|
||||
{{- .Page.Store.Set "hasMermaid" true -}}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
{{- .Text | safeHTML -}}
|
||||
{{- if gt .Level 1 -}}
|
||||
<span class="hx:absolute hx:-mt-20" id="{{ .Anchor | safeURL }}"></span>
|
||||
<a href="#{{ .Anchor | safeURL }}" class="subheading-anchor" aria-label="Permalink for this section"></a>
|
||||
<a href="#{{ .Anchor | safeURL }}" class="subheading-anchor" aria-label="{{ (T "permalinkLabel") | default "Permalink for this section" }}"></a>
|
||||
{{- end -}}
|
||||
</h{{ .Level }}>
|
||||
{{- /* Drop trailing newlines */ -}}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
>
|
||||
{{- .Text | safeHTML -}}
|
||||
{{- if and .Page.Site.Params.externalLinkDecoration $isExternal -}}
|
||||
{{- partial "utils/icon.html" (dict "name" "arrow-up-right" "attributes" `class="hx:inline hx:rtl:rotate-270 hx:align-baseline" height="1em"`) -}}
|
||||
{{- partial "utils/icon.html" (dict "name" "arrow-up-right" "attributes" `class="hx:inline hx:rtl:rotate-270 hx:align-baseline" height="1em" aria-hidden="true"`) -}}
|
||||
{{- end -}}
|
||||
</a>
|
||||
{{- end -}}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
{{- end -}}
|
||||
<button
|
||||
class="hextra-banner-close-button hx:cursor-pointer hx:absolute hx:right-0 hx:text-white hx:font-bold hx:leading-none hx:hover:opacity-75 hx:transition hx:w-10 hx:h-10 hx:-mr-2 hx:md:mr-0 hx:flex hx:items-center hx:justify-center"
|
||||
aria-label="{{ (T "closeBanner") | default "Close banner" }}"
|
||||
>
|
||||
{{- partial "utils/icon.html" (dict "name" "x" "attributes" "height=16") -}}
|
||||
</button>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
{{- range $page.Ancestors.Reverse }}
|
||||
{{- if not .IsHome }}
|
||||
<div class="hx:whitespace-nowrap hx:transition-colors hx:min-w-[24px] hx:overflow-hidden hx:text-ellipsis hx:hover:text-gray-900 hx:dark:hover:text-gray-100">
|
||||
<a href="{{ .RelPermalink }}">{{- partial "utils/title" . -}}</a>
|
||||
<a href="{{ .RelPermalink }}" class="hx:inline-block hx:rounded-sm hx:hextra-focus-visible-inset">{{- partial "utils/title" . -}}</a>
|
||||
</div>
|
||||
{{- partial "utils/icon.html" (dict "name" "chevron-right" "attributes" "class=\"hx:w-3.5 hx:shrink-0 hx:rtl:-rotate-180\"") -}}
|
||||
{{ end -}}
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
<button
|
||||
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
|
||||
title="{{ $copyCode }}"
|
||||
aria-label="{{ $copyCode }}"
|
||||
data-copied-label="{{ (T "copied") | default "Copied!" }}"
|
||||
>
|
||||
<div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
|
||||
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
|
||||
|
||||
@@ -71,10 +71,14 @@
|
||||
// Listen for system theme changes
|
||||
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", setGiscusTheme);
|
||||
|
||||
// Update giscus theme when theme switcher is clicked
|
||||
const themeToggleOptions = document.querySelectorAll(".hextra-theme-toggle-options p");
|
||||
// Update giscus theme when theme switcher is clicked.
|
||||
const themeToggleOptions = document.querySelectorAll(".hextra-theme-toggle-options [data-item]");
|
||||
if (themeToggleOptions) {
|
||||
themeToggleOptions.forEach(toggle => toggle.addEventListener('click', setGiscusTheme));
|
||||
themeToggleOptions.forEach(toggle => {
|
||||
toggle.addEventListener('click', () => {
|
||||
setTimeout(setGiscusTheme, 0);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
{{- $pageURL := $.Permalink -}}
|
||||
{{- $pageTitle := $.Title -}}
|
||||
<div class="hextra-page-context-menu hx:relative hx:inline-flex hx:shrink-0">
|
||||
<div class="hx:inline-flex hx:overflow-hidden hx:rounded-lg hx:border hx:border-gray-200 hx:transition-colors hx:hover:border-gray-300 hx:dark:border-neutral-800 hx:dark:hover:border-neutral-700">
|
||||
<div class="hx:inline-flex hx:rounded-lg hx:border hx:border-gray-200 hx:transition-colors hx:hover:border-gray-300 hx:dark:border-neutral-800 hx:dark:hover:border-neutral-700">
|
||||
<button
|
||||
class="hextra-page-context-menu-copy hx:group/copybtn hx:inline-flex hx:cursor-pointer hx:items-center hx:gap-1.5 hx:bg-transparent hx:px-2.5 hx:py-1 hx:text-sm hx:font-medium hx:text-gray-700 hx:transition-colors hx:hover:bg-slate-50 hx:dark:text-gray-300 hx:dark:hover:bg-neutral-900"
|
||||
class="hextra-page-context-menu-copy hx:group/copybtn hx:inline-flex hx:cursor-pointer hx:items-center hx:gap-1.5 hx:bg-transparent hx:px-2.5 hx:py-1 hx:text-sm hx:font-medium hx:text-gray-700 hx:transition-colors hx:hover:bg-slate-50 hx:dark:text-gray-300 hx:dark:hover:bg-neutral-900 hx:ltr:rounded-l-lg hx:rtl:rounded-r-lg hx:hextra-focus-visible-inset"
|
||||
data-url="{{ $markdownURL }}"
|
||||
title="{{ i18n "copyAsMarkdown" }}"
|
||||
aria-label="{{ i18n "copyAsMarkdown" }}"
|
||||
@@ -25,19 +25,22 @@
|
||||
<span>{{ i18n "copyPage" }}</span>
|
||||
</button>
|
||||
<button
|
||||
class="hextra-page-context-menu-toggle hx:inline-flex hx:cursor-pointer hx:items-center hx:justify-center hx:bg-transparent hx:px-1.5 hx:py-1 hx:text-gray-500 hx:transition-colors hx:hover:bg-slate-50 hx:hover:text-gray-700 hx:dark:text-gray-400 hx:dark:hover:bg-neutral-900 hx:dark:hover:text-gray-300"
|
||||
class="hextra-page-context-menu-toggle hx:inline-flex hx:cursor-pointer hx:items-center hx:justify-center hx:bg-transparent hx:px-1.5 hx:py-1 hx:text-gray-500 hx:transition-colors hx:hover:bg-slate-50 hx:hover:text-gray-700 hx:dark:text-gray-400 hx:dark:hover:bg-neutral-900 hx:dark:hover:text-gray-300 hx:ltr:rounded-r-lg hx:rtl:rounded-l-lg hx:hextra-focus-visible-inset"
|
||||
data-state="closed"
|
||||
aria-label="Toggle page context menu"
|
||||
aria-label="{{ (T "togglePageContextMenu") | default "Toggle page context menu" }}"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="menu"
|
||||
>
|
||||
<div class="hx:size-4 hx:transition-transform hx:duration-200" data-chevron>
|
||||
{{- partial "utils/icon.html" (dict "name" "chevron-down" "attributes" "height=16 width=16") -}}
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<ul class="hextra-page-context-menu-dropdown not-prose hx:hidden hx:absolute hx:top-full hx:left-0 hx:sm:left-auto hx:sm:right-0 hx:mt-1 hx:z-20 hx:max-h-64 hx:overflow-auto hx:rounded-lg hx:border hx:border-gray-200 hx:bg-white hx:p-1 hx:text-sm hx:shadow-lg hx:dark:border-neutral-700 hx:dark:bg-neutral-900">
|
||||
<li>
|
||||
<ul class="hextra-page-context-menu-dropdown not-prose hx:hidden hx:absolute hx:top-full hx:left-0 hx:sm:left-auto hx:sm:right-0 hx:mt-1 hx:z-20 hx:max-h-64 hx:overflow-auto hx:rounded-lg hx:border hx:border-gray-200 hx:bg-white hx:p-1 hx:text-sm hx:shadow-lg hx:dark:border-neutral-700 hx:dark:bg-neutral-900" role="menu">
|
||||
<li role="none">
|
||||
<button
|
||||
data-action="copy"
|
||||
role="menuitem"
|
||||
class="hx:flex hx:w-full hx:cursor-pointer hx:select-none hx:items-center hx:gap-2 hx:whitespace-nowrap hx:rounded-sm hx:px-2 hx:py-1.5 hx:text-sm hx:text-gray-700 hx:outline-none hx:transition-colors hx:hover:bg-gray-100 hx:hover:text-gray-900 hx:dark:text-gray-300 hx:dark:hover:bg-neutral-800 hx:dark:hover:text-gray-100"
|
||||
>
|
||||
<div class="hx:size-4 hx:shrink-0 hx:text-gray-500 hx:dark:text-gray-400">
|
||||
@@ -46,10 +49,11 @@
|
||||
{{ i18n "copyAsMarkdown" }}
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<li role="none">
|
||||
<button
|
||||
data-action="view"
|
||||
data-url="{{ $markdownURL }}"
|
||||
role="menuitem"
|
||||
class="hx:flex hx:w-full hx:cursor-pointer hx:select-none hx:items-center hx:gap-2 hx:whitespace-nowrap hx:rounded-sm hx:px-2 hx:py-1.5 hx:text-sm hx:text-gray-700 hx:outline-none hx:transition-colors hx:hover:bg-gray-100 hx:hover:text-gray-900 hx:dark:text-gray-300 hx:dark:hover:bg-neutral-800 hx:dark:hover:text-gray-100"
|
||||
>
|
||||
<div class="hx:size-4 hx:shrink-0 hx:text-gray-500 hx:dark:text-gray-400">
|
||||
@@ -62,11 +66,12 @@
|
||||
<li class="hx:my-1 hx:h-px hx:bg-gray-200 hx:dark:bg-neutral-700" role="separator"></li>
|
||||
{{- range $customLinks -}}
|
||||
{{- $linkURL := partial "utils/template-url.html" (dict "template" .url "values" (dict "url" $pageURL "title" $pageTitle "markdown_url" $markdownURL)) -}}
|
||||
<li>
|
||||
<li role="none">
|
||||
<a
|
||||
href="{{ $linkURL }}"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
role="menuitem"
|
||||
class="hx:flex hx:w-full hx:cursor-pointer hx:select-none hx:items-center hx:gap-2 hx:whitespace-nowrap hx:rounded-sm hx:px-2 hx:py-1.5 hx:text-sm hx:text-gray-700 hx:outline-none hx:transition-colors hx:hover:bg-gray-100 hx:hover:text-gray-900 hx:dark:text-gray-300 hx:dark:hover:bg-neutral-800 hx:dark:hover:text-gray-100"
|
||||
>
|
||||
{{- with .icon -}}
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
class="hextra-language-switcher hx:cursor-pointer hx:rounded-md hx:text-left hx:font-medium {{ $class }} hx:grow"
|
||||
type="button"
|
||||
aria-label="{{ $changeLanguage }}"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="menu"
|
||||
>
|
||||
<div class="hx:flex hx:items-center hx:gap-2 hx:capitalize">
|
||||
{{- partial "utils/icon" (dict "name" $iconName "attributes" (printf "height=%d" $iconHeight)) -}}
|
||||
@@ -28,12 +30,14 @@
|
||||
<ul
|
||||
class="hextra-language-options hx:hidden hx:z-20 hx:max-h-64 hx:overflow-auto hx:rounded-lg hx:border hx:border-gray-200 hx:bg-white hx:p-1 hx:text-sm hx:shadow-lg hx:dark:border-neutral-700 hx:dark:bg-neutral-900"
|
||||
style="position: fixed; inset: auto auto 0px 0px; margin: 0px; min-width: 100px;"
|
||||
role="menu"
|
||||
>
|
||||
{{ range site.Languages }}
|
||||
{{ $link := partial "utils/lang-link" (dict "lang" .Lang "context" $page) }}
|
||||
<li class="hx:flex hx:flex-col">
|
||||
<li role="none" class="hx:flex hx:flex-col">
|
||||
<a
|
||||
href="{{ $link }}"
|
||||
role="menuitem"
|
||||
class="hx:text-gray-700 hx:dark:text-gray-300 hx:hover:bg-gray-100 hx:hover:text-gray-900 hx:dark:hover:bg-neutral-800 hx:dark:hover:text-gray-100 hx:relative hx:cursor-pointer hx:whitespace-nowrap hx:rounded-sm hx:py-1.5 hx:transition-colors hx:ltr:pl-3 hx:ltr:pr-9 hx:rtl:pr-3 hx:rtl:pl-9"
|
||||
>
|
||||
{{- .LanguageName -}}
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
class="hextra-nav-menu-toggle hx:cursor-pointer hx:text-sm hx:contrast-more:text-gray-700 hx:contrast-more:dark:text-gray-100 hx:relative hx:-ml-2 hx:whitespace-nowrap hx:p-2 hx:flex hx:items-center hx:gap-1 {{ $activeClass }}"
|
||||
type="button"
|
||||
aria-label="{{ or (T $item.Identifier) $item.Name | safeHTML }}"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="menu"
|
||||
>
|
||||
{{- if $icon -}}
|
||||
<span class="hx:inline-flex hx:items-center">
|
||||
@@ -38,6 +40,7 @@
|
||||
<ul
|
||||
class="hextra-nav-menu-items hx:hidden hx:z-20 hx:max-h-64 hx:overflow-auto hx:rounded-lg hx:border hx:border-gray-200 hx:bg-white hx:p-1 hx:text-sm hx:shadow-lg hx:dark:border-neutral-700 hx:dark:bg-neutral-900"
|
||||
style="min-width: 100px;"
|
||||
role="menu"
|
||||
>
|
||||
{{ range $item.Children }}
|
||||
{{- $link := .URL -}}
|
||||
@@ -47,10 +50,11 @@
|
||||
{{- $link = relLangURL (strings.TrimPrefix "/" .) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
<li class="hextra-nav-menu-item hx:flex hx:flex-col">
|
||||
<li role="none" class="hextra-nav-menu-item hx:flex hx:flex-col">
|
||||
<a
|
||||
href="{{ $link }}"
|
||||
{{ if $external }}target="_blank" rel="noreferrer"{{ end }}
|
||||
role="menuitem"
|
||||
class="hx:text-gray-600 hx:hover:text-gray-800 hx:dark:text-gray-400 hx:dark:hover:text-gray-200 hx:relative hx:cursor-pointer hx:whitespace-nowrap hx:rounded-sm hx:py-1.5 hx:transition-colors hx:ltr:pl-3 hx:ltr:pr-9 hx:rtl:pr-3 hx:rtl:pl-9 hx:flex hx:items-center hx:gap-1 hx:hover:bg-gray-100 hx:dark:hover:bg-neutral-800"
|
||||
>
|
||||
{{- if and (eq .Params.type "link") .Params.icon -}}
|
||||
|
||||
@@ -11,6 +11,6 @@
|
||||
<img class="hx:mr-2 hx:hidden hx:dark:block" src="{{ $logoDarkPath | relURL }}" alt="{{ cond $displayTitle `Dark Logo` .Site.Title }}" height="{{ $logoHeight }}" width="{{ $logoWidth }}" />
|
||||
{{- end }}
|
||||
{{- if $displayTitle }}
|
||||
<span class="hx:mr-2 hx:font-extrabold hx:inline hx:select-none" title="{{ .Site.Title }}">{{- .Site.Title -}}</span>
|
||||
<span class="hx:mr-2 hx:font-extrabold hx:inline hx:select-none">{{- .Site.Title -}}</span>
|
||||
{{- end }}
|
||||
</a>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
{{- $currentPage := . -}}
|
||||
{{- range .Site.Menus.main -}}
|
||||
{{- if eq .Params.type "search" -}}
|
||||
{{- partial "search.html" (dict "params" .Params) -}}
|
||||
{{- partial "search.html" (dict "params" .Params "location" "navbar") -}}
|
||||
{{- else -}}
|
||||
{{- $link := .URL -}}
|
||||
{{- $external := strings.HasPrefix $link "http" -}}
|
||||
@@ -52,7 +52,7 @@
|
||||
{{- end -}}
|
||||
|
||||
|
||||
<button type="button" aria-label="Menu" class="hextra-hamburger-menu hx:cursor-pointer hx:-mr-2 hx:rounded-sm hx:p-2 hx:active:bg-gray-400/20 hx:md:hidden">
|
||||
<button type="button" aria-label="{{ (T "menu") | default "Menu" }}" aria-expanded="false" class="hextra-hamburger-menu hx:cursor-pointer hx:-mr-2 hx:rounded-sm hx:p-2 hx:active:bg-gray-400/20 hx:md:hidden hx:hextra-focus-visible-inset">
|
||||
{{- partial "utils/icon.html" (dict "name" "hamburger-menu" "attributes" (printf "height=%d" $iconHeight)) -}}
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
@@ -76,8 +76,21 @@
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
<script>
|
||||
<script data-playback-time="{{ (T "playbackTime") | default "Playback time" }}">
|
||||
const playbackTimeLabel =
|
||||
document.currentScript?.getAttribute("data-playback-time") || "Playback time";
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const observers = [];
|
||||
|
||||
const applyTimerA11y = (container) => {
|
||||
container.querySelectorAll(".ap-timer[role='textbox']").forEach((timer) => {
|
||||
if (!timer.getAttribute("aria-label")) {
|
||||
timer.setAttribute("aria-label", playbackTimeLabel);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Fix play button position issue
|
||||
const style = document.createElement("style");
|
||||
style.textContent = `
|
||||
@@ -108,7 +121,20 @@
|
||||
controls: true, // Always show user controls (bottom control bar)
|
||||
idleTimeLimit: 2, // Limit terminal inactivity to 2 seconds (compress pauses longer than 2s)
|
||||
});
|
||||
applyTimerA11y(el);
|
||||
const observer = new MutationObserver(() => applyTimerA11y(el));
|
||||
observer.observe(el, { childList: true, subtree: true });
|
||||
observers.push(observer);
|
||||
}
|
||||
});
|
||||
|
||||
// Prevent lingering observers when navigating away.
|
||||
window.addEventListener(
|
||||
"pagehide",
|
||||
() => {
|
||||
observers.forEach((observer) => observer.disconnect());
|
||||
},
|
||||
{ once: true },
|
||||
);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
<div class="hx:relative hx:flex hx:items-center hx:text-gray-900 hx:contrast-more:text-gray-800 hx:dark:text-gray-300 hx:contrast-more:dark:text-gray-300">
|
||||
<input
|
||||
placeholder="{{ $placeholder }}"
|
||||
class="hextra-search-input hx:focus:hextra-focus hx:block hx:w-full hx:appearance-none hx:rounded-lg hx:px-3 hx:py-2 hx:transition-colors hx:text-base hx:leading-tight hx:md:text-sm hx:bg-black/[.05] hx:dark:bg-gray-50/10 hx:focus:bg-white hx:dark:focus:bg-dark hx:placeholder:text-gray-500 hx:dark:placeholder:text-gray-400 hx:contrast-more:border hx:contrast-more:border-current"
|
||||
aria-label="{{ $placeholder }}"
|
||||
class="hextra-search-input hx:hextra-focus-visible hx:block hx:w-full hx:appearance-none hx:rounded-lg hx:px-3 hx:py-2 hx:transition-colors hx:text-base hx:leading-tight hx:md:text-sm hx:bg-black/[.05] hx:dark:bg-gray-50/10 hx:focus-visible:bg-white hx:dark:focus-visible:bg-dark hx:placeholder:text-gray-500 hx:dark:placeholder:text-gray-400 hx:contrast-more:border hx:contrast-more:border-current"
|
||||
type="search"
|
||||
autocomplete="off"
|
||||
value=""
|
||||
spellcheck="false"
|
||||
/>
|
||||
@@ -20,7 +22,9 @@
|
||||
<div>
|
||||
<ul
|
||||
class="hextra-search-results hextra-scrollbar hx:hidden hx:border hx:border-gray-200 hx:bg-white hx:text-gray-100 hx:dark:border-neutral-800 hx:dark:bg-neutral-900 hx:absolute hx:top-full hx:z-20 hx:mt-2 hx:overflow-auto hx:overscroll-contain hx:rounded-xl hx:py-2.5 hx:shadow-xl hx:max-h-[min(calc(50vh-11rem-env(safe-area-inset-bottom)),400px)] hx:md:max-h-[min(calc(100vh-5rem-env(safe-area-inset-bottom)),400px)] hx:inset-x-0 hx:ltr:md:left-auto hx:rtl:md:right-auto hx:contrast-more:border hx:contrast-more:border-gray-900 hx:contrast-more:dark:border-gray-50 hx:w-screen hx:min-h-[100px] hx:max-w-[min(calc(100vw-2rem),calc(100%+20rem))]"
|
||||
aria-label="{{ (T "searchResults") | default "Search results" }}"
|
||||
style="transition: max-height 0.2s ease 0s;"
|
||||
></ul>
|
||||
<div class="hextra-search-status hx:sr-only" aria-live="polite" role="status"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -32,9 +32,9 @@ The `tabs` parameter is a list of dict with the following keys:
|
||||
|
||||
{{- /* Keep HTML on single lines to avoid `>` being parsed as blockquote when nested in steps (#876) */ -}}
|
||||
<div class="hextra-scrollbar hx:overflow-x-auto hx:overflow-y-hidden hx:overscroll-x-contain">
|
||||
<div class="hx:mt-4 hx:flex hx:w-max hx:min-w-full hx:border-b hx:border-gray-200 hx:pb-px hx:dark:border-neutral-800"{{- if $enableSync }} data-tab-group="{{ delimit $dataTabGroup `,` }}"{{- end }}>
|
||||
<div class="hx:mt-4 hx:flex hx:w-max hx:min-w-full hx:border-b hx:border-gray-200 hx:pb-px hx:dark:border-neutral-800" role="tablist"{{- if $enableSync }} data-tab-group="{{ delimit $dataTabGroup `,` }}"{{- end }}>
|
||||
{{- range $i, $item := $tabs -}}
|
||||
<button class="hextra-tabs-toggle hx:cursor-pointer hx:data-[state=selected]:border-primary-500 hx:data-[state=selected]:text-primary-600 hx:data-[state=selected]:dark:border-primary-500 hx:data-[state=selected]:dark:text-primary-600 hx:mr-2 hx:rounded-t hx:p-2 hx:font-medium hx:leading-5 hx:transition-colors hx:-mb-0.5 hx:select-none hx:border-b-2 hx:border-transparent hx:text-gray-600 hx:hover:border-gray-200 hx:hover:text-black hx:dark:text-gray-200 hx:dark:hover:border-neutral-800 hx:dark:hover:text-white" role="tab" type="button" aria-controls="tabs-panel-{{ $globalID }}-{{ $item.id }}"{{- if eq $i $selectedIndex }} aria-selected="true" tabindex="0" data-state="selected"{{- end }}>
|
||||
<button class="hextra-tabs-toggle hx:cursor-pointer hx:data-[state=selected]:border-primary-500 hx:data-[state=selected]:text-primary-600 hx:data-[state=selected]:dark:border-primary-500 hx:data-[state=selected]:dark:text-primary-600 hx:mr-2 hx:rounded-t hx:p-2 hx:font-medium hx:leading-5 hx:transition-colors hx:-mb-0.5 hx:select-none hx:border-b-2 hx:border-transparent hx:text-gray-600 hx:hover:border-gray-200 hx:hover:text-black hx:dark:text-gray-200 hx:dark:hover:border-neutral-800 hx:dark:hover:text-white hx:hextra-focus-visible-inset" id="tabs-tab-{{ $globalID }}-{{ $item.id }}" role="tab" type="button" aria-controls="tabs-panel-{{ $globalID }}-{{ $item.id }}" aria-selected="{{ if eq $i $selectedIndex }}true{{ else }}false{{ end }}" tabindex="{{ if eq $i $selectedIndex }}0{{ else }}-1{{ end }}"{{- if eq $i $selectedIndex }} data-state="selected"{{- end }}>
|
||||
{{- $item.name -}}
|
||||
</button>
|
||||
{{- end -}}
|
||||
@@ -42,7 +42,7 @@ The `tabs` parameter is a list of dict with the following keys:
|
||||
</div>
|
||||
<div>
|
||||
{{- range $i, $item := $tabs -}}
|
||||
<div class="hextra-tabs-panel hx:rounded-sm hx:pt-6 hx:hidden hx:data-[state=selected]:block" id="tabs-panel-{{ $globalID }}-{{ $item.id }}" role="tabpanel"{{- if eq $i $selectedIndex }} tabindex="0" data-state="selected"{{- end }}>
|
||||
<div class="hextra-tabs-panel hx:rounded-sm hx:pt-6 hx:hidden hx:data-[state=selected]:block" id="tabs-panel-{{ $globalID }}-{{ $item.id }}" role="tabpanel" aria-labelledby="tabs-tab-{{ $globalID }}-{{ $item.id }}" aria-hidden="{{ if eq $i $selectedIndex }}false{{ else }}true{{ end }}"{{- if eq $i $selectedIndex }} tabindex="0" data-state="selected"{{- end }}>
|
||||
{{- $item.content | markdownify -}}
|
||||
</div>
|
||||
{{- end -}}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
{{- if (site.Params.search.enable | default true) -}}
|
||||
<!-- Search bar on small screen -->
|
||||
<div class="hx:px-4 hx:pt-4 hx:md:hidden">
|
||||
{{ partial "search.html" }}
|
||||
{{ partial "search.html" (dict "location" "sidebar") }}
|
||||
</div>
|
||||
{{- end -}}
|
||||
<div class="hextra-scrollbar hx:overflow-y-auto hx:overflow-x-hidden hx:p-4 hx:grow hx:md:h-[calc(100vh-var(--navbar-height)-var(--menu-height))]">
|
||||
@@ -91,7 +91,7 @@
|
||||
{{- $shouldOpen := or (.Params.sidebar.open) (.IsAncestor $page) $active | default true }}
|
||||
<li class="{{ if $shouldOpen }}open{{ end }}">
|
||||
{{- $linkTitle := partial "utils/title" . -}}
|
||||
{{- template "sidebar-item-link" dict "context" . "active" $active "title" $linkTitle "link" .RelPermalink -}}
|
||||
{{- template "sidebar-item-link" dict "context" . "active" $active "open" $shouldOpen "title" $linkTitle "link" .RelPermalink -}}
|
||||
{{- if and $toc $active (ne .Params.toc false) -}}
|
||||
{{- template "sidebar-toc" dict "page" . -}}
|
||||
{{- end -}}
|
||||
@@ -100,14 +100,14 @@
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- else -}}
|
||||
<div class="hx:ltr:pr-0 hx:overflow-hidden">
|
||||
<div class="hextra-sidebar-children hx:ltr:pr-1 hx:rtl:pl-1 hx:overflow-hidden">
|
||||
<ul class='hx:relative hx:flex hx:flex-col hx:gap-1 hx:before:absolute hx:before:inset-y-1 hx:before:w-px hx:before:bg-gray-200 hx:before:content-[""] hx:ltr:ml-3 hx:ltr:pl-3 hx:ltr:before:left-0 hx:rtl:mr-3 hx:rtl:pr-3 hx:rtl:before:right-0 hx:dark:before:bg-neutral-800'>
|
||||
{{- range $items.ByWeight }}
|
||||
{{- $active := eq $pageURL .RelPermalink -}}
|
||||
{{- $shouldOpen := or (.Params.sidebar.open) (.IsAncestor $page) $active | default true }}
|
||||
{{- $linkTitle := partial "utils/title" . -}}
|
||||
<li class="hx:flex hx:flex-col {{ if $shouldOpen }}open{{ end }}">
|
||||
{{- template "sidebar-item-link" dict "context" . "active" $active "title" $linkTitle "link" .RelPermalink -}}
|
||||
{{- template "sidebar-item-link" dict "context" . "active" $active "open" $shouldOpen "title" $linkTitle "link" .RelPermalink -}}
|
||||
{{- if and $toc $active (ne .Params.toc false) -}}
|
||||
{{ template "sidebar-toc" dict "page" . }}
|
||||
{{- end }}
|
||||
@@ -156,7 +156,7 @@
|
||||
{{- $link = relLangURL (strings.TrimPrefix "/" .) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
<li>{{ template "sidebar-item-link" dict "active" false "title" $name "link" $link }}</li>
|
||||
<li>{{ template "sidebar-item-link" dict "active" false "open" false "title" $name "link" $link }}</li>
|
||||
{{ end }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
@@ -164,27 +164,31 @@
|
||||
{{- define "sidebar-item-link" -}}
|
||||
{{- $external := strings.HasPrefix .link "http" -}}
|
||||
{{- $open := .open | default true -}}
|
||||
<a
|
||||
class="hx:flex hx:items-center hx:justify-between hx:gap-2 hx:cursor-pointer hx:rounded-sm hx:px-2 hx:py-1.5 hx:text-sm hx:transition-colors [-webkit-tap-highlight-color:transparent] [-webkit-touch-callout:none] [word-break:break-word]
|
||||
{{- if .active }}
|
||||
hextra-sidebar-active-item hx:bg-primary-100 hx:font-semibold hx:text-primary-800 hx:contrast-more:border hx:contrast-more:border-primary-500 hx:dark:bg-primary-400/10 hx:dark:text-primary-600 hx:contrast-more:dark:border-primary-500
|
||||
{{- else }}
|
||||
hx:text-gray-500 hx:hover:bg-gray-100 hx:hover:text-gray-900 hx:contrast-more:border hx:contrast-more:border-transparent hx:contrast-more:text-gray-900 hx:contrast-more:hover:border-gray-900 hx:dark:text-neutral-400 hx:dark:hover:bg-primary-100/5 hx:dark:hover:text-gray-50 hx:contrast-more:dark:text-gray-50 hx:contrast-more:dark:hover:border-gray-50
|
||||
{{- end -}}"
|
||||
href="{{ .link }}"
|
||||
{{ if $external }}target="_blank" rel="noreferrer"{{ end }}
|
||||
>
|
||||
{{- .title -}}
|
||||
{{- with .context }}
|
||||
{{- if or .RegularPages .Sections }}
|
||||
<span class="hextra-sidebar-collapsible-button">
|
||||
{{- template "sidebar-collapsible-button" -}}
|
||||
</span>
|
||||
{{- end }}
|
||||
{{ end -}}
|
||||
</a>
|
||||
{{- $hasChildren := false -}}
|
||||
{{- with .context }}{{ if or .RegularPages .Sections }}{{ $hasChildren = true }}{{ end }}{{ end -}}
|
||||
<div class="hextra-sidebar-item hx:group hx:relative hx:flex hx:items-center" data-active="{{ if .active }}true{{ else }}false{{ end }}">
|
||||
<a
|
||||
class="hx:flex hx:items-center hx:justify-between hx:gap-2 hx:grow hx:cursor-pointer hx:rounded-sm hx:px-2 hx:py-1.5 hx:text-sm hx:transition-colors [-webkit-tap-highlight-color:transparent] [-webkit-touch-callout:none]
|
||||
{{- if $hasChildren }} hx:ltr:pr-8 hx:rtl:pl-8{{- end }}
|
||||
hx:hextra-focus-visible-inset
|
||||
{{- if .active }}
|
||||
hextra-sidebar-active-item hx:bg-primary-100 hx:font-semibold hx:text-primary-800 hx:contrast-more:border hx:contrast-more:border-primary-500 hx:dark:bg-primary-400/10 hx:dark:text-primary-600 hx:contrast-more:dark:border-primary-500
|
||||
{{- else }}
|
||||
hx:text-gray-500 hx:hover:bg-gray-100 hx:hover:text-gray-900 hx:contrast-more:border hx:contrast-more:border-transparent hx:contrast-more:text-gray-900 hx:contrast-more:hover:border-gray-900 hx:dark:text-neutral-400 hx:dark:hover:bg-primary-100/5 hx:dark:hover:text-gray-50 hx:contrast-more:dark:text-gray-50 hx:contrast-more:dark:hover:border-gray-50
|
||||
{{- end -}}"
|
||||
href="{{ .link }}"
|
||||
{{ if $external }}target="_blank" rel="noreferrer"{{ end }}
|
||||
>
|
||||
<span class="hx:min-w-0 [word-break:break-word]">{{- .title -}}</span>
|
||||
</a>
|
||||
{{- if $hasChildren }}
|
||||
<button type="button" class="hextra-sidebar-collapsible-button hx:absolute hx:top-1/2 hx:-translate-y-1/2 hx:ltr:right-2 hx:rtl:left-2 hx:shrink-0 hx:cursor-pointer hx:p-0 hx:text-gray-500 hx:dark:text-neutral-400 hx:group-hover:text-gray-900 hx:dark:group-hover:text-gray-50 hx:group-data-[active=true]:text-primary-800 hx:group-data-[active=true]:dark:text-primary-600 hx:hextra-focus-visible-inset" aria-label="{{ (T "toggleSection") | default "Toggle section" }}" aria-expanded="{{ if $open }}true{{ else }}false{{ end }}">
|
||||
{{- template "sidebar-collapsible-button" -}}
|
||||
</button>
|
||||
{{- end }}
|
||||
</div>
|
||||
{{- end -}}
|
||||
|
||||
{{- define "sidebar-collapsible-button" -}}
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" class="hx:h-[18px] hx:min-w-[18px] hx:rounded-xs hx:p-0.5 hx:hover:bg-gray-800/5 hx:dark:hover:bg-gray-100/5"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" class="hx:origin-center hx:transition-transform hx:rtl:-rotate-180"></path></svg>
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true" focusable="false" class="hx:h-[18px] hx:min-w-[18px] hx:rounded-xs hx:p-0.5 hx:hover:bg-gray-800/5 hx:dark:hover:bg-gray-100/5"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" class="hx:origin-center hx:transition-transform hx:rtl:-rotate-180"></path></svg>
|
||||
{{- end -}}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
class="hextra-theme-toggle hx:cursor-pointer hx:rounded-md hx:text-left hx:font-medium {{ $class }} hx:grow"
|
||||
type="button"
|
||||
aria-label="{{ $changeTheme }}"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="menu"
|
||||
>
|
||||
<div class="hx:flex hx:items-center hx:gap-2 hx:capitalize">
|
||||
{{- partial "utils/icon.html" (dict "name" "sun" "attributes" (printf `height=%d class="hx:group-data-[theme=dark]:hidden hx:group-data-[theme=system]:hidden"` $iconHeight)) -}}
|
||||
@@ -30,39 +32,52 @@
|
||||
class="hextra-theme-toggle-options hx:hidden hx:z-20 hx:max-h-64 hx:overflow-auto hx:rounded-lg hx:border hx:border-gray-200 hx:bg-white hx:p-1 hx:text-sm hx:shadow-lg hx:dark:border-neutral-700 hx:dark:bg-neutral-900"
|
||||
style="position: fixed; inset: auto auto 0px 0px; margin: 0px; min-width: 100px;"
|
||||
data-theme="light"
|
||||
role="menu"
|
||||
>
|
||||
<li class="hx:flex hx:flex-col">
|
||||
<p
|
||||
<li role="none" class="hx:flex hx:flex-col">
|
||||
<button
|
||||
type="button"
|
||||
role="menuitemradio"
|
||||
aria-checked="true"
|
||||
tabindex="-1"
|
||||
data-item="light"
|
||||
class="hx:text-gray-700 hx:dark:text-gray-300 hx:hover:bg-gray-100 hx:hover:text-gray-900 hx:dark:hover:bg-neutral-800 hx:dark:hover:text-gray-100 hx:relative hx:cursor-pointer hx:whitespace-nowrap hx:rounded-sm hx:py-1.5 hx:transition-colors hx:ltr:pl-3 hx:ltr:pr-9 hx:rtl:pr-3 hx:rtl:pl-9"
|
||||
class="hx:text-gray-700 hx:dark:text-gray-300 hx:hover:bg-gray-100 hx:hover:text-gray-900 hx:dark:hover:bg-neutral-800 hx:dark:hover:text-gray-100 hx:relative hx:cursor-pointer hx:whitespace-nowrap hx:rounded-sm hx:py-1.5 hx:transition-colors hx:ltr:pl-3 hx:ltr:pr-9 hx:rtl:pr-3 hx:rtl:pl-9 hx:text-left hx:w-full hx:bg-transparent hx:border-0"
|
||||
>
|
||||
{{ $light }}
|
||||
<span class="hx:absolute hx:inset-y-0 hx:flex hx:items-center hx:ltr:right-3 hx:rtl:left-3 hx:group-data-[theme=dark]:hidden hx:group-data-[theme=system]:hidden">
|
||||
{{- partial "utils/icon" (dict "name" "check" "attributes" "height=1em width=1em") -}}
|
||||
</span>
|
||||
</p>
|
||||
</button>
|
||||
</li>
|
||||
<li class="hx:flex hx:flex-col">
|
||||
<p
|
||||
<li role="none" class="hx:flex hx:flex-col">
|
||||
<button
|
||||
type="button"
|
||||
role="menuitemradio"
|
||||
aria-checked="false"
|
||||
tabindex="-1"
|
||||
data-item="dark"
|
||||
class="hx:text-gray-700 hx:dark:text-gray-300 hx:hover:bg-gray-100 hx:hover:text-gray-900 hx:dark:hover:bg-neutral-800 hx:dark:hover:text-gray-100 hx:relative hx:cursor-pointer hx:whitespace-nowrap hx:rounded-sm hx:py-1.5 hx:transition-colors hx:ltr:pl-3 hx:ltr:pr-9 hx:rtl:pr-3 hx:rtl:pl-9"
|
||||
class="hx:text-gray-700 hx:dark:text-gray-300 hx:hover:bg-gray-100 hx:hover:text-gray-900 hx:dark:hover:bg-neutral-800 hx:dark:hover:text-gray-100 hx:relative hx:cursor-pointer hx:whitespace-nowrap hx:rounded-sm hx:py-1.5 hx:transition-colors hx:ltr:pl-3 hx:ltr:pr-9 hx:rtl:pr-3 hx:rtl:pl-9 hx:text-left hx:w-full hx:bg-transparent hx:border-0"
|
||||
>
|
||||
{{ $dark }}
|
||||
<span class="hx:absolute hx:inset-y-0 hx:flex hx:items-center hx:ltr:right-3 hx:rtl:left-3 hx:group-data-[theme=light]:hidden hx:group-data-[theme=system]:hidden">
|
||||
{{- partial "utils/icon" (dict "name" "check" "attributes" "height=1em width=1em") -}}
|
||||
</span>
|
||||
</p>
|
||||
</button>
|
||||
</li>
|
||||
<li class="hx:flex hx:flex-col">
|
||||
<p
|
||||
<li role="none" class="hx:flex hx:flex-col">
|
||||
<button
|
||||
type="button"
|
||||
role="menuitemradio"
|
||||
aria-checked="false"
|
||||
tabindex="-1"
|
||||
data-item="system"
|
||||
class="hx:text-gray-700 hx:dark:text-gray-300 hx:hover:bg-gray-100 hx:hover:text-gray-900 hx:dark:hover:bg-neutral-800 hx:dark:hover:text-gray-100 hx:relative hx:cursor-pointer hx:whitespace-nowrap hx:rounded-sm hx:py-1.5 hx:transition-colors hx:ltr:pl-3 hx:ltr:pr-9 hx:rtl:pr-3 hx:rtl:pl-9"
|
||||
class="hx:text-gray-700 hx:dark:text-gray-300 hx:hover:bg-gray-100 hx:hover:text-gray-900 hx:dark:hover:bg-neutral-800 hx:dark:hover:text-gray-100 hx:relative hx:cursor-pointer hx:whitespace-nowrap hx:rounded-sm hx:py-1.5 hx:transition-colors hx:ltr:pl-3 hx:ltr:pr-9 hx:rtl:pr-3 hx:rtl:pl-9 hx:text-left hx:w-full hx:bg-transparent hx:border-0"
|
||||
>
|
||||
{{ $system }}
|
||||
<span class="hx:absolute hx:inset-y-0 hx:flex hx:items-center hx:ltr:right-3 hx:rtl:left-3 hx:group-data-[theme=dark]:hidden hx:group-data-[theme=light]:hidden">
|
||||
{{- partial "utils/icon" (dict "name" "check" "attributes" "height=1em width=1em") -}}
|
||||
</span>
|
||||
</p>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -6,7 +6,7 @@
|
||||
{{- $editThisPage := (T "editThisPage") | default "Edit this page"}}
|
||||
{{- $backToTop := (T "backToTop") | default "Scroll to top" -}}
|
||||
|
||||
<nav class="hextra-toc hx:order-last hx:hidden hx:w-64 hx:shrink-0 hx:xl:block hx:print:hidden hx:px-4" aria-label="table of contents">
|
||||
<nav class="hextra-toc hx:order-last hx:hidden hx:w-64 hx:shrink-0 hx:xl:block hx:print:hidden hx:px-4" aria-label="{{ (T "tableOfContents") | default "Table of contents" }}">
|
||||
{{- if $toc }}
|
||||
<div class="hextra-scrollbar hx:sticky hx:top-16 hx:overflow-y-auto hx:pr-4 hx:pt-6 hx:text-sm [hyphens:auto] hx:max-h-[calc(100vh-var(--navbar-height)-env(safe-area-inset-bottom))] hx:ltr:-mr-4 hx:rtl:-ml-4">
|
||||
{{- with .Fragments.Headings -}}
|
||||
@@ -48,14 +48,14 @@
|
||||
{{- $editURL = urls.JoinPath $editURL $sourceDir $path -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
<a class="hx:text-xs hx:font-medium hx:text-gray-500 hx:hover:text-gray-900 hx:dark:text-gray-400 hx:dark:hover:text-gray-100 hx:contrast-more:text-gray-800 hx:contrast-more:dark:text-gray-50" href="{{ $editURL }}" target="_blank" rel="noreferrer">{{ $editThisPage }}</a>
|
||||
<a class="hx:inline-block hx:rounded-sm hx:text-xs hx:font-medium hx:text-gray-500 hx:hover:text-gray-900 hx:dark:text-gray-400 hx:dark:hover:text-gray-100 hx:contrast-more:text-gray-800 hx:contrast-more:dark:text-gray-50 hx:hextra-focus-visible-inset" href="{{ $editURL }}" target="_blank" rel="noreferrer">{{ $editThisPage }}</a>
|
||||
{{- end -}}
|
||||
{{/* Scroll To Top */}}
|
||||
<button aria-hidden="true" id="backToTop" onClick="scrollUp();" class="hx:cursor-pointer hx:transition-all hx:duration-75 hx:opacity-0 hx:text-xs hx:font-medium hx:text-gray-500 hx:hover:text-gray-900 hx:dark:text-gray-400 hx:dark:hover:text-gray-100 hx:contrast-more:text-gray-800 hx:contrast-more:dark:text-gray-50">
|
||||
<button id="backToTop" tabindex="-1" onClick="scrollUp();" class="hx:cursor-pointer hx:transition-all hx:duration-75 hx:opacity-0 hx:text-xs hx:font-medium hx:text-gray-500 hx:hover:text-gray-900 hx:dark:text-gray-400 hx:dark:hover:text-gray-100 hx:contrast-more:text-gray-800 hx:contrast-more:dark:text-gray-50">
|
||||
<span>
|
||||
{{- $backToTop -}}
|
||||
</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="hx:inline hx:ltr:ml-1 hx:rtl:mr-1 hx:h-3.5 hx:w-3.5 hx:rounded-full hx:border hx:border-gray-500 hx:hover:border-gray-900 hx:dark:border-gray-400 hx:dark:hover:border-gray-100 hx:contrast-more:border-gray-800 hx:contrast-more:dark:border-gray-50">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" focusable="false" class="hx:inline hx:ltr:ml-1 hx:rtl:mr-1 hx:h-3.5 hx:w-3.5 hx:rounded-full hx:border hx:border-gray-500 hx:hover:border-gray-900 hx:dark:border-gray-400 hx:dark:hover:border-gray-100 hx:contrast-more:border-gray-800 hx:contrast-more:dark:border-gray-50">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M4.5 15.75l7.5-7.5 7.5 7.5" />
|
||||
</svg>
|
||||
</button>
|
||||
@@ -78,7 +78,7 @@
|
||||
{{- range $headings }}
|
||||
{{- if .Title }}
|
||||
<li class="hx:my-2 hx:scroll-my-6 hx:scroll-py-6">
|
||||
<a class="{{ $class }} hx:inline-block hx:text-gray-500 hx:hover:text-gray-900 hx:dark:text-gray-400 hx:dark:hover:text-gray-300 hx:contrast-more:text-gray-900 hx:contrast-more:underline hx:contrast-more:dark:text-gray-50 hx:w-full hx:wrap-break-word" href="#{{ anchorize .ID }}">
|
||||
<a class="{{ $class }} hx:inline-block hx:rounded-sm hx:text-gray-500 hx:hover:text-gray-900 hx:dark:text-gray-400 hx:dark:hover:text-gray-300 hx:contrast-more:text-gray-900 hx:contrast-more:underline hx:contrast-more:dark:text-gray-50 hx:w-full hx:wrap-break-word hx:hextra-focus-visible-inset" href="#{{ anchorize .ID }}">
|
||||
{{- .Title | safeHTML | plainify | htmlUnescape }}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@@ -76,6 +76,8 @@
|
||||
{{- .Page.Store.Set "hasAsciinema" true -}}
|
||||
|
||||
<div class="asciinema-player"
|
||||
role="region"
|
||||
aria-label="{{ (T "terminalRecording") | default "Terminal recording" }}"
|
||||
data-cast-file="{{ $castFile }}"
|
||||
data-theme="{{ $theme }}"
|
||||
data-speed="{{ $speed }}"
|
||||
|
||||
@@ -24,7 +24,7 @@ or
|
||||
{{- $border := not (eq (.Get "border") false) | default true }}
|
||||
|
||||
{{- if $link -}}
|
||||
<a href="{{ $link }}" title="{{ $content | plainify }}" target="_blank">
|
||||
<a href="{{ $link }}" title="{{ $content | plainify }}" target="_blank" class="not-prose hx:inline-flex hx:align-middle hx:no-underline hover:hx:no-underline">
|
||||
{{- partial "shortcodes/badge.html" (dict
|
||||
"content" $content
|
||||
"color" $color
|
||||
|
||||
@@ -6,6 +6,8 @@ A file tree container.
|
||||
|
||||
<div class="hextra-filetree hx:mt-6 hx:select-none hx:text-sm hx:text-gray-800 hx:dark:text-gray-300 not-prose">
|
||||
<div class="hx:inline-block hx:rounded-lg hx:px-4 hx:py-2 hx:border hx:border-gray-200 hx:dark:border-neutral-800">
|
||||
{{- .InnerDeindent -}}
|
||||
<ul class="hx:flex hx:flex-col">
|
||||
{{- .InnerDeindent -}}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@ A folder in a file tree.
|
||||
{{- $state := .Get "state" | default "open" }}
|
||||
|
||||
<li class="hx:group hx:flex hx:list-none hx:flex-col">
|
||||
<button class="hextra-filetree-folder hx:inline-flex hx:cursor-pointer hx:items-center hx:py-1 hx:hover:opacity-60">
|
||||
<button class="hextra-filetree-folder hx:inline-flex hx:cursor-pointer hx:items-center hx:py-1 hx:hover:opacity-60" aria-expanded="{{ if eq $state "open" }}true{{ else }}false{{ end }}">
|
||||
<span data-state="{{ $state }}" class="hx:data-[state=open]:hidden">
|
||||
{{- partial "utils/icon" (dict "name" "folder" "attributes" "width=1em") -}}
|
||||
</span>
|
||||
|
||||
@@ -17,7 +17,7 @@ A shortcode for rendering a button with a link.
|
||||
|
||||
<a
|
||||
href="{{ $href }}"
|
||||
class="not-prose hx:font-medium hx:cursor-pointer hx:px-6 hx:py-3 hx:rounded-full hx:text-center hx:text-white hx:inline-block hx:bg-primary-600 hx:hover:bg-primary-700 hx:focus:outline-hidden hx:focus:ring-4 hx:focus:ring-primary-300 hx:dark:bg-primary-600 hx:dark:hover:bg-primary-700 hx:dark:focus:ring-primary-800 hx:transition-all hx:ease-in hx:duration-200"
|
||||
class="not-prose hx:font-medium hx:cursor-pointer hx:px-6 hx:py-3 hx:rounded-full hx:text-center hx:text-white hx:inline-block hx:bg-primary-600 hx:hover:bg-primary-700 hx:hextra-focus-visible hx:dark:bg-primary-600 hx:dark:hover:bg-primary-700 hx:transition-all hx:ease-in hx:duration-200"
|
||||
{{ with $style }}style="{{ . | safeCSS }}"{{ end }}
|
||||
{{ if $external }}target="_blank" rel="noreferrer"{{ end -}}
|
||||
>
|
||||
|
||||
@@ -49,7 +49,7 @@ Render Jupyter Notebook
|
||||
{{- $outputs := index $cell "outputs" -}}
|
||||
{{- with $outputs -}}
|
||||
<div class="hextra-jupyter-code-cell-outputs-container">
|
||||
<div class="hextra-jupyter-code-cell-outputs">
|
||||
<div class="hextra-jupyter-code-cell-outputs" tabindex="0">
|
||||
{{- range $output := . -}}
|
||||
{{- if eq (index $output "output_type") "display_data" -}}
|
||||
{{- $data := index $output "data" -}}
|
||||
|
||||
@@ -11,5 +11,5 @@ Shortcode to include a PDF file in a page.
|
||||
|
||||
|
||||
<div class="hextra-pdf">
|
||||
<iframe src="{{ $url | safeURL }}" width="100%" style="min-height: 32rem;" frameborder="0"></iframe>
|
||||
<iframe src="{{ $url | safeURL }}" width="100%" style="min-height: 32rem;" frameborder="0" title="{{ (T "pdfViewer") | default "PDF viewer" }}"></iframe>
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
{{ partial "sidebar.html" (dict "context" . "disableSidebar" true "displayPlaceholder" true) }}
|
||||
{{ partial "toc.html" (dict "Params" (dict "toc" false)) }}
|
||||
<article class="hx:w-full hx:break-words hx:flex hx:min-h-[calc(100vh-var(--navbar-height))] hx:min-w-0 hx:justify-center hx:pb-8 hx:pr-[calc(env(safe-area-inset-right)-1.5rem)]">
|
||||
<main class="hx:w-full hx:min-w-0 hextra-max-content-width hx:px-6 hx:pt-4 hx:md:px-12">
|
||||
<main id="content" class="hx:w-full hx:min-w-0 hextra-max-content-width hx:px-6 hx:pt-4 hx:md:px-12">
|
||||
<br class="hx:mt-1.5 hx:text-sm" />
|
||||
{{ if .Title }}<h1 class="hx:text-center hx:mt-2 hx:text-4xl hx:font-bold hx:tracking-tight hx:text-slate-900 hx:dark:text-slate-100">{{ .Title }}</h1>{{ end }}
|
||||
<div class="hx:mb-16"></div>
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
<html lang="{{ .Site.Language.Lang }}" dir="{{ .Site.Language.LanguageDirection | default `ltr` }}">
|
||||
{{- partial "head.html" . -}}
|
||||
<body>
|
||||
<a href="#content" class="hx:sr-only hx:focus-visible:not-sr-only hx:focus-visible:fixed hx:focus-visible:z-50 hx:focus-visible:top-2 hx:focus-visible:left-2 hx:focus-visible:bg-primary-500 hx:focus-visible:text-white hx:focus-visible:px-4 hx:focus-visible:py-2 hx:focus-visible:rounded-md hx:focus-visible:text-sm hx:focus-visible:font-medium">
|
||||
{{- (T "skipToContent") | default "Skip to content" -}}
|
||||
</a>
|
||||
{{- partial "banner.html" . -}}
|
||||
{{- partial "navbar.html" . -}}
|
||||
{{- block "main" . }}{{ end -}}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="hx:mx-auto hx:flex hextra-max-page-width">
|
||||
{{ partial "sidebar.html" (dict "context" . "disableSidebar" true "displayPlaceholder" true) }}
|
||||
<article class="hx:w-full hx:break-words hx:flex hx:min-h-[calc(100vh-var(--navbar-height))] hx:min-w-0 hx:justify-center hx:pb-8 hx:pr-[calc(env(safe-area-inset-right)-1.5rem)]">
|
||||
<main class="hx:w-full hx:min-w-0 hextra-max-content-width hx:px-6 hx:pt-4 hx:md:px-12">
|
||||
<main id="content" class="hx:w-full hx:min-w-0 hextra-max-content-width hx:px-6 hx:pt-4 hx:md:px-12">
|
||||
{{ partial "breadcrumb.html" (dict "page" . "enable" false) }}
|
||||
<br class="hx:mt-1.5 hx:text-sm" />
|
||||
{{ if .Title }}<h1 class="hx:text-center hx:mt-2 hx:text-4xl hx:font-bold hx:tracking-tight hx:text-slate-900 hx:dark:text-slate-100">{{ .Title }}</h1>{{ end }}
|
||||
@@ -21,7 +21,7 @@
|
||||
{{ end }}
|
||||
<p class="hx:opacity-80 hx:mt-4 hx:leading-7">{{- partial "utils/page-description" . -}}</p>
|
||||
<p class="hx:opacity-80 hx:mt-1 hx:leading-7">
|
||||
<a class="hx:text-[color:hsl(var(--primary-hue),100%,50%)] hx:underline hx:underline-offset-2 hx:decoration-from-font" href="{{ .RelPermalink }}">
|
||||
<a class="hx:text-[color:hsl(var(--primary-hue),100%,50%)] hx:underline hx:underline-offset-2 hx:decoration-from-font" href="{{ .RelPermalink }}" aria-label="{{ printf "%s %s" $readMore .Title }}">
|
||||
{{- $readMore -}}
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
{{ partial "sidebar.html" (dict "context" . "disableSidebar" true "displayPlaceholder" true) }}
|
||||
{{ partial "toc.html" . }}
|
||||
<article class="hx:w-full hx:break-words hx:flex hx:min-h-[calc(100vh-var(--navbar-height))] hx:min-w-0 hx:justify-center hx:pb-8 hx:pr-[calc(env(safe-area-inset-right)-1.5rem)]">
|
||||
<main class="hx:w-full hx:min-w-0 hextra-max-content-width hx:px-6 hx:pt-4 hx:md:px-12">
|
||||
<main id="content" class="hx:w-full hx:min-w-0 hextra-max-content-width hx:px-6 hx:pt-4 hx:md:px-12">
|
||||
{{ partial "breadcrumb.html" (dict "page" . "enable" true) }}
|
||||
{{ if .Title }}
|
||||
<div class="hx:flex hx:flex-col hx:sm:flex-row hx:items-start hx:sm:items-center hx:sm:justify-between hx:gap-4 hx:mt-2">
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
{{ partial "sidebar.html" (dict "context" .) }}
|
||||
{{ partial "toc.html" . }}
|
||||
<article class="hx:w-full hx:break-words hx:flex hx:min-h-[calc(100vh-var(--navbar-height))] hx:min-w-0 hx:justify-center hx:pb-8 hx:pr-[calc(env(safe-area-inset-right)-1.5rem)]">
|
||||
<main class="hx:w-full hx:min-w-0 hextra-max-content-width hx:px-6 hx:pt-4 hx:md:px-12">
|
||||
<main id="content" class="hx:w-full hx:min-w-0 hextra-max-content-width hx:px-6 hx:pt-4 hx:md:px-12">
|
||||
{{ partial "breadcrumb.html" (dict "page" . "enable" true) }}
|
||||
<div class="content">
|
||||
{{ if .Title }}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
{{ partial "sidebar.html" (dict "context" .) }}
|
||||
{{ partial "toc.html" . }}
|
||||
<article class="hx:w-full hx:break-words hx:flex hx:min-h-[calc(100vh-var(--navbar-height))] hx:min-w-0 hx:justify-center hx:pb-8 hx:pr-[calc(env(safe-area-inset-right)-1.5rem)]">
|
||||
<main class="hx:w-full hx:min-w-0 hextra-max-content-width hx:px-6 hx:pt-4 hx:md:px-12">
|
||||
<main id="content" class="hx:w-full hx:min-w-0 hextra-max-content-width hx:px-6 hx:pt-4 hx:md:px-12">
|
||||
{{ partial "breadcrumb.html" (dict "page" . "enable" true) }}
|
||||
<div class="content">
|
||||
{{ if .Title }}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
{{ partial "sidebar.html" (dict "context" . "disableSidebar" true "displayPlaceholder" true) }}
|
||||
{{ partial "toc.html" (dict "Params" (dict "toc" false)) }}
|
||||
<article class="hx:w-full hx:break-words hx:flex hx:min-h-[calc(100vh-var(--navbar-height))] hx:min-w-0 hx:justify-center hx:pb-8 hx:pr-[calc(env(safe-area-inset-right)-1.5rem)]">
|
||||
<main class="hx:w-full hx:min-w-0 hextra-max-content-width hx:px-6 hx:pt-4 hx:md:px-12">
|
||||
<main id="content" class="hx:w-full hx:min-w-0 hextra-max-content-width hx:px-6 hx:pt-4 hx:md:px-12">
|
||||
{{ if .Title }}<h1 class="hx:text-center hx:mt-2 hx:text-4xl hx:font-bold hx:tracking-tight hx:text-slate-900 hx:dark:text-slate-100">{{ .Title }}</h1>{{ end }}
|
||||
<div class="content">
|
||||
{{- with (index .Site.Data .Site.Language.Lang "termbase") -}}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{{ define "main" }}
|
||||
<div class='hx:mx-auto hx:flex hextra-max-page-width'>
|
||||
{{ partial "sidebar.html" (dict "context" . "disableSidebar" true) }}
|
||||
<div class="hx:w-full hx:break-words hx:min-h-[calc(100vh-var(--navbar-height))] hx:min-w-0 hx:pb-8 hx:pt-8 hx:md:pt-12 hx:pl-[max(env(safe-area-inset-left),1.5rem)] hx:pr-[max(env(safe-area-inset-left),1.5rem)]">
|
||||
<main id="content" class="hx:w-full hx:break-words hx:min-h-[calc(100vh-var(--navbar-height))] hx:min-w-0 hx:pb-8 hx:pt-8 hx:md:pt-12 hx:pl-[max(env(safe-area-inset-left),1.5rem)] hx:pr-[max(env(safe-area-inset-left),1.5rem)]">
|
||||
<div class="hx:flex hx:flex-col hx:items-start">
|
||||
{{ .Content }}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
{{ partial "sidebar.html" (dict "context" . "disableSidebar" true "displayPlaceholder" true) }}
|
||||
{{ partial "toc.html" . }}
|
||||
<article class="hx:w-full hx:break-words hx:flex hx:min-h-[calc(100vh-var(--navbar-height))] hx:min-w-0 hx:justify-center hx:pb-8 hx:pr-[calc(env(safe-area-inset-right)-1.5rem)]">
|
||||
<main class="hx:w-full hx:min-w-0 hextra-max-content-width hx:px-6 hx:pt-4 hx:md:px-12">
|
||||
<main id="content" class="hx:w-full hx:min-w-0 hextra-max-content-width hx:px-6 hx:pt-4 hx:md:px-12">
|
||||
{{ if .Title }}<h1 class="hx:text-center hx:mt-2 hx:text-4xl hx:font-bold hx:tracking-tight hx:text-slate-900 hx:dark:text-slate-100">{{ .Title }}</h1>{{ end }}
|
||||
<div class="content">
|
||||
{{ .Content }}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
{{ partial "sidebar.html" (dict "context" .) }}
|
||||
{{ partial "toc.html" . }}
|
||||
<article class="hx:w-full hx:break-words hx:flex hx:min-h-[calc(100vh-var(--navbar-height))] hx:min-w-0 hx:justify-center hx:pb-8 hx:pr-[calc(env(safe-area-inset-right)-1.5rem)]">
|
||||
<main class="hx:w-full hx:min-w-0 hextra-max-content-width hx:px-6 hx:pt-4 hx:md:px-12">
|
||||
<main id="content" class="hx:w-full hx:min-w-0 hextra-max-content-width hx:px-6 hx:pt-4 hx:md:px-12">
|
||||
{{ partial "breadcrumb.html" (dict "page" . "enable" false) }}
|
||||
<div class="content">
|
||||
{{ if .Title }}<h1>{{ .Title }}</h1>{{ end }}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
{{ partial "sidebar.html" (dict "context" . "disableSidebar" true "displayPlaceholder" true) }}
|
||||
{{ partial "toc.html" . }}
|
||||
<article class="hx:w-full hx:break-words hx:flex hx:min-h-[calc(100vh-var(--navbar-height))] hx:min-w-0 hx:justify-center hx:pb-8 hx:pr-[calc(env(safe-area-inset-right)-1.5rem)]">
|
||||
<main class="hx:w-full hx:min-w-0 hextra-max-content-width hx:px-6 hx:pt-4 hx:md:px-12">
|
||||
<main id="content" class="hx:w-full hx:min-w-0 hextra-max-content-width hx:px-6 hx:pt-4 hx:md:px-12">
|
||||
{{ partial "breadcrumb.html" (dict "page" . "enable" false) }}
|
||||
<br class="hx:mt-1.5 hx:text-sm" />
|
||||
{{ if .Title }}<h1 class="hx:text-center hx:mt-2 hx:text-4xl hx:font-bold hx:tracking-tight hx:text-slate-900 hx:dark:text-slate-100">{{ .Title }}</h1>{{ end }}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
{{ partial "sidebar.html" (dict "context" . "disableSidebar" true "displayPlaceholder" true) }}
|
||||
{{ partial "toc.html" (dict "Params" (dict "toc" false)) }}
|
||||
<article class="hx:w-full hx:break-words hx:flex hx:min-h-[calc(100vh-var(--navbar-height))] hx:min-w-0 hx:justify-center hx:pb-8 hx:pr-[calc(env(safe-area-inset-right)-1.5rem)]">
|
||||
<main class="hx:w-full hx:min-w-0 hextra-max-content-width hx:px-6 hx:pt-4 hx:md:px-12">
|
||||
<main id="content" class="hx:w-full hx:min-w-0 hextra-max-content-width hx:px-6 hx:pt-4 hx:md:px-12">
|
||||
<br class="hx:mt-1.5 hx:text-sm" />
|
||||
{{ if .Title }}<h1 class="hx:text-center hx:mt-2 hx:text-4xl hx:font-bold hx:tracking-tight hx:text-slate-900 hx:dark:text-slate-100">{{ .Title }}</h1>{{ end }}
|
||||
<div class="hx:mb-16"></div>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
{{ partial "sidebar.html" (dict "context" . "disableSidebar" true "displayPlaceholder" true) }}
|
||||
{{ partial "toc.html" (dict "Params" (dict "toc" false)) }}
|
||||
<article class="hx:w-full hx:break-words hx:flex hx:min-h-[calc(100vh-var(--navbar-height))] hx:min-w-0 hx:justify-center hx:pb-8 hx:pr-[calc(env(safe-area-inset-right)-1.5rem)]">
|
||||
<main class="hx:w-full hx:min-w-0 hextra-max-content-width hx:px-6 hx:pt-4 hx:md:px-12">
|
||||
<main id="content" class="hx:w-full hx:min-w-0 hextra-max-content-width hx:px-6 hx:pt-4 hx:md:px-12">
|
||||
<br class="hx:mt-1.5 hx:text-sm" />
|
||||
{{ if .Title }}<h1 class="hx:text-center hx:mt-2 hx:text-4xl hx:font-bold hx:tracking-tight hx:text-slate-900 hx:dark:text-slate-100">{{ .Title }}</h1>{{ end }}
|
||||
<div class="hx:mb-16"></div>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{{ define "main" }}
|
||||
<div class="hx:mx-auto hx:flex hx:max-w-[90rem]">
|
||||
{{ partial "sidebar.html" (dict "context" . "disableSidebar" true "displayPlaceholder" false) }}
|
||||
<article class="hx:w-full hx:break-words hx:min-h-[calc(100vh-var(--navbar-height))] hx:min-w-0 hx:pt-4 hx:pb-8 hx:pl-[max(env(safe-area-inset-left),1.5rem)] hx:pr-[max(env(safe-area-inset-left),1.5rem)]">
|
||||
<main id="content" class="hx:w-full hx:break-words hx:min-h-[calc(100vh-var(--navbar-height))] hx:min-w-0 hx:pt-4 hx:pb-8 hx:pl-[max(env(safe-area-inset-left),1.5rem)] hx:pr-[max(env(safe-area-inset-left),1.5rem)]">
|
||||
<br class="hx:mt-1.5 hx:text-sm" />
|
||||
{{ if .Title }}<h1 class="hx:text-center hx:mt-2 hx:text-4xl hx:font-bold hx:tracking-tight hx:text-slate-900 hx:dark:text-slate-100">{{ .Title }}</h1>{{ end }}
|
||||
<div class="content">
|
||||
{{ .Content }}
|
||||
</div>
|
||||
</article>
|
||||
</main>
|
||||
</div>
|
||||
{{ end }}
|
||||
1097
package-lock.json
generated
1097
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -3,13 +3,18 @@
|
||||
"dev:theme": "hugo server --logLevel=debug --config=hugo.yaml,../dev.toml --environment=theme --source=docs --themesDir=../.. -D -F --port 1313",
|
||||
"dev": "hugo server --source=docs --themesDir=../.. --disableFastRender -D --port 1313",
|
||||
"build:css": "npx postcss --config postcss.config.mjs --env production assets/css/styles.css -o assets/css/compiled/main.css",
|
||||
"build": "hugo --gc --minify --themesDir=../.. --source=docs"
|
||||
"build": "hugo --gc --minify --themesDir=../.. --source=docs",
|
||||
"test": "npx playwright test",
|
||||
"test:a11y": "npx playwright test tests/accessibility.spec.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@axe-core/playwright": "^4.10.1",
|
||||
"@playwright/test": "^1.49.1",
|
||||
"@tailwindcss/postcss": "^4.1.18",
|
||||
"postcss-cli": "^11.0.1",
|
||||
"prettier": "^3.8.0",
|
||||
"prettier-plugin-go-template": "^0.0.15",
|
||||
"serve": "^14.2.5",
|
||||
"tailwindcss": "^4.1.18"
|
||||
}
|
||||
}
|
||||
|
||||
21
playwright.config.ts
Normal file
21
playwright.config.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { defineConfig } from "@playwright/test";
|
||||
|
||||
const baseURL = process.env.BASE_URL || "http://localhost:1313";
|
||||
|
||||
export default defineConfig({
|
||||
testDir: "./tests",
|
||||
timeout: 60_000,
|
||||
retries: 0,
|
||||
reporter: [["list"], ["html"]],
|
||||
use: {
|
||||
baseURL,
|
||||
},
|
||||
webServer: process.env.BASE_URL
|
||||
? undefined
|
||||
: {
|
||||
command: "npx serve docs/public -l tcp://localhost:1313 --no-clipboard",
|
||||
url: baseURL,
|
||||
reuseExistingServer: !process.env.CI,
|
||||
timeout: 30_000,
|
||||
},
|
||||
});
|
||||
92
tests/accessibility.spec.ts
Normal file
92
tests/accessibility.spec.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import AxeBuilder from "@axe-core/playwright";
|
||||
|
||||
const WCAG_TAGS = ["wcag2a", "wcag2aa", "wcag22aa"];
|
||||
// TODO: Re-enable once known baseline issues are resolved and tracked.
|
||||
const DISABLED_RULES = ["color-contrast", "target-size"];
|
||||
|
||||
type Violation = Awaited<
|
||||
ReturnType<InstanceType<typeof AxeBuilder>["analyze"]>
|
||||
>["violations"][number];
|
||||
|
||||
function decodeXmlEntities(value: string): string {
|
||||
return value
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, "'");
|
||||
}
|
||||
|
||||
function parseLocUrlsFromSitemap(xml: string): string[] {
|
||||
const locRegex = /<loc>\s*([^<]+?)\s*<\/loc>/gi;
|
||||
const urls: string[] = [];
|
||||
let match: RegExpExecArray | null;
|
||||
|
||||
while ((match = locRegex.exec(xml)) !== null) {
|
||||
urls.push(decodeXmlEntities(match[1]));
|
||||
}
|
||||
|
||||
return urls;
|
||||
}
|
||||
|
||||
async function getEnglishPages(baseURL: string): Promise<string[]> {
|
||||
const sitemapUrl = `${baseURL}/en/sitemap.xml`;
|
||||
const response = await fetch(sitemapUrl);
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Failed to fetch sitemap (${response.status} ${response.statusText}) at ${sitemapUrl}`,
|
||||
);
|
||||
}
|
||||
|
||||
const xml = await response.text();
|
||||
const pages = parseLocUrlsFromSitemap(xml)
|
||||
.map((url) => {
|
||||
try {
|
||||
return new URL(url).pathname;
|
||||
} catch {
|
||||
return url;
|
||||
}
|
||||
});
|
||||
|
||||
if (pages.length === 0) {
|
||||
throw new Error(`Sitemap at ${sitemapUrl} returned no URLs.`);
|
||||
}
|
||||
|
||||
return pages;
|
||||
}
|
||||
|
||||
function formatViolation(v: Violation): string {
|
||||
return `• ${v.id} (${v.impact}) — ${v.nodes.length} element(s)\n ${v.help}\n ${v.helpUrl}`;
|
||||
}
|
||||
|
||||
test("all English pages pass axe-core WCAG AA", async ({ page, baseURL }) => {
|
||||
const pages = await getEnglishPages(baseURL!);
|
||||
const failures: string[] = [];
|
||||
|
||||
for (const path of pages) {
|
||||
await test.step(path, async () => {
|
||||
await page.goto(path, { waitUntil: "load" });
|
||||
|
||||
const results = await new AxeBuilder({ page })
|
||||
.withTags(WCAG_TAGS)
|
||||
.disableRules(DISABLED_RULES)
|
||||
.analyze();
|
||||
|
||||
if (results.violations.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
failures.push(
|
||||
`--- ${path} ---\n${results.violations
|
||||
.map(formatViolation)
|
||||
.join("\n\n")}`,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
expect(
|
||||
failures,
|
||||
`Accessibility violations found:\n\n${failures.join("\n\n")}`,
|
||||
).toHaveLength(0);
|
||||
});
|
||||
Reference in New Issue
Block a user