Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/themes.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ Display expand/collapse icons on page links in the sidebar.
<label>
<input class="toggle" type="checkbox" value="sidebar-chevron-left" data-class data-group="sidebar-chevron"> Preview <code>sidebar-chevron-left</code>
</label>
<br>
<label>
<input class="toggle" type="checkbox" value="sidebar-chevron-root-hidden" data-class> Hide root-level chevrons with <code>sidebar-chevron-root-hidden</code>
</label>

<!-- prettier-ignore -->
```html
Expand All @@ -128,6 +132,14 @@ Display expand/collapse icons on page links in the sidebar.
<body class="sidebar-chevron-left">
```

To hide chevrons on all root-level page links and group titles while retaining
chevrons on nested page links, add the `sidebar-chevron-root-hidden` class:

<!-- prettier-ignore -->
```html
<body class="sidebar-chevron-right sidebar-chevron-root-hidden">
```

To prevent chevrons from displaying for specific page links, add a `no-chevron` class as follows:

```md
Expand Down
4 changes: 2 additions & 2 deletions src/core/event/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ export function Events(Base) {
dom.on(sidebarElm, 'click', (/** @type {MouseEvent} */ { target }) => {
const groupTitle = /** @type {HTMLElement | null} */ (
/** @type {HTMLElement} */ (target).closest(
'.group-title[role="button"]',
'.group-toggle[role="button"]',
)
);

Expand All @@ -277,7 +277,7 @@ export function Events(Base) {
dom.on(sidebarElm, 'keydown', (/** @type {KeyboardEvent} */ event) => {
const groupTitle = /** @type {HTMLElement | null} */ (
/** @type {HTMLElement} */ (event.target).closest(
'.group-title[role="button"]',
'.group-toggle[role="button"]',
)
);

Expand Down
42 changes: 37 additions & 5 deletions src/core/render/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,32 @@ export function Render(Base) {
});
}

/**
* Normalize links in loose Markdown lists from `<li><p><a>` to
* `<li><a>` so sidebar behavior and styling do not depend on list
* tightness.
*
* @param {Element} sidebarNavEl
*/
#normalizeSidebarPageLinks(sidebarNavEl) {
dom.findAll(sidebarNavEl, 'li > p').forEach(paragraph => {
const link = paragraph.firstElementChild;
const onlyContainsLink = [...paragraph.childNodes].every(
node =>
node === link || (node.nodeType === 3 && !node.textContent?.trim()),
);

if (
!paragraph.attributes.length &&
paragraph.children.length === 1 &&
link?.tagName === 'A' &&
onlyContainsLink
) {
paragraph.replaceWith(link);
}
});
}

#executeScript() {
const script = dom
.findAll('.markdown-section>script')
Expand Down Expand Up @@ -320,7 +346,7 @@ export function Render(Base) {
dom
.findAll(
sidebarNavEl,
'li.group > .group-title[role="button"][data-group-id]',
'li.group > .group-toggle[role="button"][data-group-id]',
)
.map(elm => [
elm.getAttribute('data-group-id'),
Expand All @@ -329,6 +355,7 @@ export function Render(Base) {
);

dom.setHTML('.sidebar-nav', this.compiler.sidebar(text, maxLevel));
this.#normalizeSidebarPageLinks(sidebarNavEl);

sidebarToggleEl.setAttribute('aria-expanded', String(!isMobile()));

Expand Down Expand Up @@ -358,18 +385,18 @@ export function Render(Base) {
// Mark page links and groups
const pageLinks = dom.findAll(
sidebarNavEl,
'a:is(li > a, li > p > a):not(.section-link, [target="_blank"])',
'li > a:not(.section-link, [target="_blank"])',
);
const pageLinkGroups = dom
// NOTE: Using filter() method as a replacement for :has() selector. It
// would be preferable to use only 'li:not(:has(> a, > p > a))' selector
// would be preferable to use only 'li:not(:has(> a))' selector
// but the :has() selector is not supported by our Jest test environment
// See: https://github.com/jsdom/jsdom/issues/3506#issuecomment-1769782333
.findAll(sidebarEl, 'li')
.filter(
elm =>
elm.querySelector(':scope > ul') &&
!elm.querySelectorAll(':scope > a, :scope > p > a').length,
!elm.querySelector(':scope > a'),
);

pageLinks.forEach(elm => {
Expand All @@ -382,6 +409,10 @@ export function Render(Base) {
let groupTitle = [...elm.children].find(
child => child.tagName === 'P' && !child.querySelector('a'),
);
// Preserve the original styling behavior: only text-only paragraphs
// produced by Markdown receive the group-title class.
const styledGroupTitle =
groupTitle && !groupTitle.children.length ? groupTitle : null;

if (!groupTitle) {
const sublist = [...elm.children].find(
Expand All @@ -405,7 +436,7 @@ export function Render(Base) {
}
}

groupTitle?.classList.add('group-title');
styledGroupTitle?.classList.add('group-title');

const rootList = elm.parentElement;

Expand All @@ -415,6 +446,7 @@ export function Render(Base) {
sidebarGroupStates.get(groupId) ?? collapseSidebarGroups;

elm.classList.toggle('collapse', isCollapsed);
groupTitle.classList.add('group-toggle');
groupTitle.setAttribute('data-group-id', groupId);
groupTitle.setAttribute('role', 'button');
groupTitle.setAttribute('tabindex', '0');
Expand Down
19 changes: 14 additions & 5 deletions src/themes/shared/_classes.css
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,11 @@
}

body[class*='sidebar-chevron'] {
.sidebar-nav :is(a.page-link, p.group-title[role='button']).no-chevron {
.sidebar-nav :is(a.page-link, p.group-toggle).no-chevron {
background: none;
}

.sidebar-nav p.group-title[role='button'] {
.sidebar-nav p.group-toggle {
background: var(--sidebar-pagelink-bg);

&[aria-expanded='true'] {
Expand All @@ -94,6 +94,15 @@ body[class*='sidebar-chevron'] {
}
}

body.sidebar-chevron-root-hidden {
.sidebar-nav > ul > li {
> a.page-link,
> p.group-toggle[aria-expanded] {
background: none;
}
}
}

/* Left */
/* -------------------------------------------------------------------------- */
:root:has(body.sidebar-chevron-left) {
Expand All @@ -105,7 +114,7 @@ body.sidebar-chevron-left {
--_inset: 18px;

li {
:is(a.page-link, p.group-title[role='button']) {
:is(a.page-link, p.group-toggle) {
padding-left: var(--_inset);
}

Expand All @@ -128,12 +137,12 @@ body.sidebar-chevron-left {

body.sidebar-chevron-right {
.sidebar-nav {
p.group-title[role='button'] {
p.group-toggle {
margin-right: 0;
}

li {
:is(a, p.group-title[role='button']) {
:is(a, p.group-toggle) {
padding-right: calc(var(--_sidebar-inset) + 15px);
}
}
Expand Down
16 changes: 8 additions & 8 deletions src/themes/shared/_sidebar.css
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,15 @@
color: var(--sidebar-group-title-color);
font-size: var(--sidebar-group-title-font-size);
font-weight: var(--sidebar-group-title-font-weight);
}

&[role='button'] {
cursor: pointer;
user-select: none;
&.group-toggle {
cursor: pointer;
user-select: none;

&:focus-visible {
outline: 2px solid currentColor;
outline-offset: 2px;
}
&:focus-visible {
outline: 2px solid currentColor;
outline-offset: 2px;
}
}
}
Expand All @@ -99,7 +99,7 @@
}

&.collapse {
> :not(a, p:has(> a.page-link)):not(.group-title) {
> :not(a, .group-toggle) {
display: none;
}
}
Expand Down
Loading