Skip to content

Commit

Permalink
Merge branch 'develop' into 2278-keybindings
Browse files Browse the repository at this point in the history
# Conflicts:
#	src/core/event/index.js
  • Loading branch information
jhildenbiddle committed Nov 30, 2023
2 parents fa90428 + 50b84f7 commit 4461aca
Show file tree
Hide file tree
Showing 10 changed files with 285 additions and 52 deletions.
33 changes: 33 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -874,6 +874,39 @@ window.$docsify = {
}
```

## skipLink

- Type: `Boolean|String|Object`
- Default: `'Skip to main content'`

Determines if/how the site's [skip navigation link](https://webaim.org/techniques/skipnav/) will be rendered.

```js
// Render skip link for all routes (default)
window.$docsify = {
skipLink: 'Skip to main content',
};
```

```js
// Render localized skip links based on route paths
window.$docsify = {
skipLink: {
'/es/': 'Saltar al contenido principal',
'/de-de/': 'Ga naar de hoofdinhoud',
'/ru-ru/': 'Перейти к основному содержанию',
'/zh-cn/': '跳到主要内容',
},
};
```

```js
// Do not render skip link
window.$docsify = {
skipLink: false,
};
```

## subMaxLevel

- Type: `Number`
Expand Down
6 changes: 6 additions & 0 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@
},
pathNamespaces: ['/es', '/de-de', '/ru-ru', '/zh-cn'],
},
skipLink: {
'/es/': 'Saltar al contenido principal',
'/de-de/': 'Ga naar de hoofdinhoud',
'/ru-ru/': 'Перейти к основному содержанию',
'/zh-cn/': '跳到主要内容',
},
vueComponents: {
'button-counter': {
template: /* html */ `<button @click="count += 1">You clicked me {{ count }} times</button>`,
Expand Down
6 changes: 6 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@
},
pathNamespaces: ['/es', '/de-de', '/ru-ru', '/zh-cn'],
},
skipLink: {
'/es/': 'Saltar al contenido principal',
'/de-de/': 'Ga naar de hoofdinhoud',
'/ru-ru/': 'Перейти к основному содержанию',
'/zh-cn/': '跳到主要内容',
},
vueComponents: {
'button-counter': {
template: /* html */ `<button @click="count += 1">You clicked me {{ count }} times</button>`,
Expand Down
52 changes: 46 additions & 6 deletions src/core/event/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,27 @@ import config from '../config.js';
export function Events(Base) {
return class Events extends Base {
$resetEvents(source) {
const { auto2top } = this.config;
const { auto2top, loadNavbar } = this.config;
const { path, query } = this.route;

// If 'history', rely on the browser's scroll auto-restoration when going back or forward
// Note: Scroll position set by browser on forward/back (i.e. "history")
if (source !== 'history') {
// Scroll to ID if specified
if (this.route.query.id) {
this.#scrollIntoView(this.route.path, this.route.query.id);
if (query.id) {
this.#scrollIntoView(path, query.id, true);
}
// Scroll to top if a link was clicked and auto2top is enabled
if (source === 'navigate') {
else if (source === 'navigate') {
auto2top && this.#scroll2Top(auto2top);
}
}

if (this.config.loadNavbar) {
// Move focus to content
if (query.id || source === 'navigate') {
this.focusContent();
}

if (loadNavbar) {
this.__getAndActive(this.router, 'nav');
}
}
Expand All @@ -37,9 +43,13 @@ export function Events(Base) {
const { coverpage, keyBindings } = this.config;
const keyModifiers = ['alt', 'ctrl', 'meta', 'shift'];

// Bind skip link
this.#skipLink('#skip-to-content');

// Bind toggle button
this.#btn('button.sidebar-toggle', this.router);
this.#collapse('.sidebar', this.router);

// Bind sticky effect
if (coverpage) {
!isMobile && on('scroll', this.__sticky);
Expand Down Expand Up @@ -120,6 +130,22 @@ export function Events(Base) {
#enableScrollEvent = true;
#coverHeight = 0;

#skipLink(el) {
el = dom.getNode(el);

if (el === null || el === undefined) {
return;
}

dom.on(el, 'click', evt => {
const target = dom.getNode('#main');

evt.preventDefault();
target && target.focus();
this.#scrollTo(target);
});
}

#scrollTo(el, offset = 0) {
if (this.#scroller) {
this.#scroller.stop();
Expand All @@ -142,6 +168,20 @@ export function Events(Base) {
.begin();
}

focusContent() {
const { query } = this.route;
const focusEl = query.id
? // Heading ID
dom.find(`#${query.id}`)
: // First heading
dom.find('#main :where(h1, h2, h3, h4, h5, h6)') ||
// Content container
dom.find('#main');

// Move focus to content area
focusEl && focusEl.focus();
}

#highlight(path) {
if (!this.#enableScrollEvent) {
return;
Expand Down
6 changes: 5 additions & 1 deletion src/core/render/compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,11 @@ export class Compiler {
nextToc.slug = url;
_self.toc.push(nextToc);

return `<h${level} id="${slug}"><a href="${url}" data-id="${slug}" class="anchor"><span>${str}</span></a></h${level}>`;
// Note: tabindex="-1" allows programmatically focusing on heading
// elements after navigation. This is preferred over focusing on the link
// within the heading because it matches the focus behavior of screen
// readers when navigating page content.
return `<h${level} id="${slug}" tabindex="-1"><a href="${url}" data-id="${slug}" class="anchor"><span>${str}</span></a></h${level}>`;
};

origin.code = highlightCodeCompiler({ renderer });
Expand Down
61 changes: 45 additions & 16 deletions src/core/render/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,34 @@ export function Render(Base) {
el.setAttribute('href', nameLink[match]);
}
}

#renderSkipLink(vm) {
const { skipLink } = vm.config;

if (skipLink !== false) {
const el = dom.getNode('#skip-to-content');

let skipLinkText =
typeof skipLink === 'string' ? skipLink : 'Skip to main content';

if (skipLink?.constructor === Object) {
const matchingPath = Object.keys(skipLink).find(path =>
vm.route.path.startsWith(path.startsWith('/') ? path : `/${path}`)
);
const matchingText = matchingPath && skipLink[matchingPath];

skipLinkText = matchingText || skipLinkText;
}

if (el) {
el.innerHTML = skipLinkText;
} else {
const html = `<button id="skip-to-content">${skipLinkText}</button>`;
dom.body.insertAdjacentHTML('afterbegin', html);
}
}
}

_renderTo(el, content, replace) {
const node = dom.getNode(el);
if (node) {
Expand Down Expand Up @@ -396,6 +424,9 @@ export function Render(Base) {
_updateRender() {
// Render name link
this.#renderNameLink(this);

// Render skip link
this.#renderSkipLink(this);
}

initRender() {
Expand All @@ -409,14 +440,10 @@ export function Render(Base) {
}

const id = config.el || '#app';
const navEl = dom.find('nav') || dom.create('nav');

const el = dom.find(id);
let html = '';
let navAppendToTarget = dom.body;

if (el) {
navEl.setAttribute('aria-label', 'secondary');
let html = '';

if (config.repo) {
html += tpl.corner(config.repo, config.cornerExternalLinkTarget);
Expand All @@ -437,25 +464,27 @@ export function Render(Base) {
}

html += tpl.main(config);

// Render main app
this._renderTo(el, html, true);
} else {
this.rendered = true;
}

if (config.mergeNavbar && isMobile) {
navAppendToTarget = dom.find('.sidebar');
} else {
navEl.classList.add('app-nav');

if (!config.repo) {
navEl.classList.add('no-badge');
}
}

// Add nav
if (config.loadNavbar) {
dom.before(navAppendToTarget, navEl);
const navEl = dom.find('nav') || dom.create('nav');
const isMergedSidebar = config.mergeNavbar && isMobile;

navEl.setAttribute('aria-label', 'secondary');

if (isMergedSidebar) {
dom.find('.sidebar').prepend(navEl);
} else {
dom.body.prepend(navEl);
navEl.classList.add('app-nav');
navEl.classList.toggle('no-badge', !config.repo);
}
}

if (config.themeColor) {
Expand Down
2 changes: 1 addition & 1 deletion src/core/render/tpl.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export function main(config) {
return /* html */ `
<main role="presentation">${aside}
<section class="content">
<article class="markdown-section" id="main" role="main"><!--main--></article>
<article id="main" class="markdown-section" role="main" tabindex="-1"><!--main--></article>
</section>
</main>
`;
Expand Down
32 changes: 32 additions & 0 deletions src/themes/basic/_layout.styl
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,38 @@ li input[type='checkbox']
margin 0 0.2em 0.25em 0
vertical-align middle

[tabindex="-1"]:focus
outline none !important

/* skip link */
#skip-to-content
appearance none
display block
position fixed
z-index 2147483647
top 0
left 50%
padding 0.5rem 1.5rem
border 0
border-radius: 100vw
background-color $color-primary
background-color var(--theme-color, $color-primary)
color $color-bg
color var(--theme-bg, $color-bg)
opacity 0
font-size inherit
text-decoration none
transform translate(-50%, -100%)
transition-property opacity, transform
transition-duration 0s, 0.2s
transition-delay 0.2s, 0s

&:focus
opacity 1
transform translate(-50%, 0.75rem)
transition-duration 0s, 0.2s
transition-delay 0s, 0s

/* navbar */
.app-nav
margin 25px 60px 0 0
Expand Down
2 changes: 1 addition & 1 deletion test/integration/__snapshots__/docs.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ exports[`Docs Site coverpage renders and is unchanged 1`] = `
)
\\">
<div class=\\"mask\\"></div>
<div class=\\"cover-main\\"><p><img src=\\"http://127.0.0.1:3001/_media/icon.svg\\" data-origin=\\"_media/icon.svg\\" alt=\\"logo\\"></p><h1 id=\\"docsify-4130\\"><a href=\\"#/?id=docsify-4130\\" data-id=\\"docsify-4130\\" class=\\"anchor\\"><span>docsify <small>4.13.0</small></span></a></h1><blockquote>
<div class=\\"cover-main\\"><p><img src=\\"http://127.0.0.1:3001/_media/icon.svg\\" data-origin=\\"_media/icon.svg\\" alt=\\"logo\\"></p><h1 id=\\"docsify-4130\\" tabindex=\\"-1\\"><a href=\\"#/?id=docsify-4130\\" data-id=\\"docsify-4130\\" class=\\"anchor\\"><span>docsify <small>4.13.0</small></span></a></h1><blockquote>
<p>A magical documentation site generator.</p></blockquote>
<ul><li>Simple and lightweight</li><li>No statically built html files</li><li>Multiple themes</li></ul><p><a href=\\"https://github.com/docsifyjs/docsify/\\" target=\\"_blank\\" rel=\\"noopener\\">GitHub</a>
<a href=\\"#/?id=docsify\\">Getting Started</a></p></div>
Expand Down
Loading

0 comments on commit 4461aca

Please sign in to comment.