Skip to content

Commit ee2d127

Browse files
authored
✨ Add navbar_end part in navbar UI (#802)
1 parent 9ecc788 commit ee2d127

9 files changed

Lines changed: 81 additions & 3 deletions

File tree

.changeset/witty-dryers-buy.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@myst-theme/site": patch
3+
"@myst-theme/book": patch
4+
---
5+
6+
✨ Add navbar_end part in navbar UI

docs/_site/navbar_end.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{button}`MyST Docs <https://mystmd.org>` {button}`GitHub <https://github.com/jupyter-book/myst-theme>`

docs/myst.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ project:
1818
📢 **Announcement:** This is a test banner for the MyST Theme! [Learn more about MyST](/reference).
1919
footer: _site/footer.md
2020
primary_sidebar_footer: _site/primary_sidebar_footer.md
21+
navbar_end: _site/navbar_end.md
2122
toc:
2223
- file: index.md
2324
- file: ui.md

docs/ui.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ This page documents site-level UI components that appear on every page. use the
77
Many of the UI areas below allow users to insert custom content or interface elements using **parts**.
88
These are documented below, and in each case they use the same configuration pattern through `site.parts` or `project.parts` in `myst.yml`.
99

10+
:::{note}
11+
Parts are only supported in the **book** theme. The article theme does not yet support parts configuration.
12+
:::
13+
1014
## Navigation bar
1115

1216
### Site logo
@@ -94,6 +98,28 @@ site:
9498
- If the `.md` file it points to is empty, the footer will not be visible
9599
- If not configured, falls back to the default "Made with MyST" footer
96100

101+
## Navbar End
102+
103+
Display custom content at the end of the top navigation bar, after the theme toggle button.
104+
This is useful for adding icon links, badges, or call-to-action buttons to your navbar.
105+
106+
### Configuration
107+
108+
Create a markdown file with your navbar content and add it to `myst.yml`:
109+
110+
```yaml
111+
site:
112+
parts:
113+
navbar_end: _site/navbar_end.md
114+
```
115+
116+
### Behavior
117+
118+
- Renders inline at the end of the navbar (after the theme toggle and action buttons)
119+
- Supports any MyST markdown content (links, images, formatting, etc.)
120+
- Content is rendered inline, so keep it short - a few links or icons work best
121+
- On narrow screens, the content moves into the primary sidebar menu
122+
97123
## Hiding Elements
98124

99125
Control the visibility of various page elements. All options can be set site-wide or per-page.

packages/site/src/components/Navigation/Navigation.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { PrimarySidebar } from './PrimarySidebar.js';
33
import type { Heading } from '@myst-theme/common';
44
import { getProjectHeadings } from '@myst-theme/common';
55
import type { SiteManifest } from 'myst-config';
6+
import type { GenericParent } from 'myst-common';
67

78
/**
89
* PrimaryNavigation will load nav links and headers from the site manifest and display
@@ -15,13 +16,15 @@ export const PrimaryNavigation = ({
1516
hide_toc,
1617
mobileOnly,
1718
footer,
19+
navbarEnd,
1820
}: {
1921
children?: React.ReactNode;
2022
projectSlug?: string;
2123
sidebarRef?: React.RefObject<HTMLDivElement>;
2224
hide_toc?: boolean;
2325
mobileOnly?: boolean;
2426
footer?: React.ReactNode;
27+
navbarEnd?: GenericParent;
2528
}) => {
2629
const config = useSiteManifest();
2730
if (!config) return null;
@@ -41,6 +44,7 @@ export const PrimaryNavigation = ({
4144
nav={nav}
4245
headings={headings}
4346
footer={footer}
47+
navbarEnd={navbarEnd}
4448
/>
4549
);
4650
};
@@ -63,6 +67,7 @@ export const ConfigurablePrimaryNavigation = ({
6367
nav,
6468
headings,
6569
footer,
70+
navbarEnd,
6671
}: {
6772
children?: React.ReactNode;
6873
sidebarRef?: React.RefObject<HTMLDivElement>;
@@ -71,6 +76,7 @@ export const ConfigurablePrimaryNavigation = ({
7176
nav?: SiteManifest['nav'];
7277
headings?: Heading[];
7378
footer?: React.ReactNode;
79+
navbarEnd?: GenericParent;
7480
}) => {
7581
const [open, setOpen] = useNavOpen();
7682
const top = useThemeTop();
@@ -107,6 +113,7 @@ export const ConfigurablePrimaryNavigation = ({
107113
nav={nav}
108114
headings={headings}
109115
footer={footer}
116+
navbarEnd={navbarEnd}
110117
hide_toc={hide_toc}
111118
mobileOnly={mobileOnly}
112119
/>

packages/site/src/components/Navigation/PrimarySidebar.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import React, { useEffect, useRef } from 'react';
22
import classNames from 'classnames';
33
import { useNavigation } from '@remix-run/react';
4+
import type { GenericParent } from 'myst-common';
5+
import { MyST } from 'myst-to-react';
46
import {
57
useNavOpen,
68
useSiteManifest,
@@ -149,6 +151,7 @@ export const PrimarySidebar = ({
149151
sidebarRef,
150152
nav,
151153
footer,
154+
navbarEnd,
152155
headings,
153156
hide_toc,
154157
mobileOnly,
@@ -157,6 +160,7 @@ export const PrimarySidebar = ({
157160
nav?: SiteManifest['nav'];
158161
headings?: Heading[];
159162
footer?: React.ReactNode;
163+
navbarEnd?: GenericParent;
160164
hide_toc?: boolean;
161165
mobileOnly?: boolean;
162166
}) => {
@@ -222,6 +226,16 @@ export const PrimarySidebar = ({
222226
<SidebarNav nav={nav} />
223227
</nav>
224228
)}
229+
{navbarEnd && (
230+
<div
231+
className={classNames(
232+
'article myst-primary-sidebar-navbar-end xl:hidden p-2 my-1 flex flex-wrap gap-2 [&_p]:contents',
233+
sidebarSectionInsetClass,
234+
)}
235+
>
236+
<MyST ast={navbarEnd} />
237+
</div>
238+
)}
225239
{nav && headings && <div className="my-3 border-b-2 lg:hidden" />}
226240
{headings && (
227241
<nav

packages/site/src/components/Navigation/TopNav.tsx

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import classNames from 'classnames';
33
import { Menu, Transition } from '@headlessui/react';
44
import { ChevronDownIcon, Bars3Icon as MenuIcon } from '@heroicons/react/24/solid';
55
import type { SiteManifest, SiteNavItem } from 'myst-config';
6+
import type { GenericParent } from 'myst-common';
7+
import { MyST } from 'myst-to-react';
68
import { ThemeButton } from './ThemeButton.js';
79
import { Search } from './Search.js';
810
import {
@@ -113,7 +115,15 @@ export function NavItems({ nav }: { nav?: SiteManifest['nav'] }) {
113115
);
114116
}
115117

116-
export function TopNav({ hideToc, hideSearch }: { hideToc?: boolean; hideSearch?: boolean }) {
118+
export function TopNav({
119+
hideToc,
120+
hideSearch,
121+
navbarEnd,
122+
}: {
123+
hideToc?: boolean;
124+
hideSearch?: boolean;
125+
navbarEnd?: GenericParent;
126+
}) {
117127
const [open, setOpen] = useNavOpen();
118128
const config = useSiteManifest();
119129
const { title, nav, actions } = config ?? {};
@@ -152,8 +162,17 @@ export function TopNav({ hideToc, hideSearch }: { hideToc?: boolean; hideSearch?
152162
<div className="flex items-center flex-grow w-auto">
153163
<NavItems nav={nav} />
154164
<div className="flex-grow block"></div>
165+
{/* Search bar */}
155166
{!hideSearch && <Search />}
156-
<ThemeButton />
167+
{/* Light/Dark theme button */}
168+
<ThemeButton className="w-10 h-10 ml-3" />
169+
{/* Custom part at end of navbar. It is `hidden` up until xl size since it will be in the sidebar drawer up to that point */}
170+
{navbarEnd && (
171+
<div className="article myst-navbar-end hidden xl:flex items-center ml-3 [&>*]:m-0">
172+
<MyST ast={navbarEnd} />
173+
</div>
174+
)}
175+
{/* Mobile pop-up for page actions */}
157176
<div className="block sm:hidden">
158177
<ActionMenu actions={actions} />
159178
</div>

themes/book/app/routes/$.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,12 @@ function ArticlePageAndNavigationInternal({
100100
<TabStateProvider>
101101
{projectParts?.banner && <Banner content={projectParts.banner.mdast} />}
102102
</TabStateProvider>
103-
<TopNav hideToc={hide_toc} hideSearch={hideSearch} />
103+
<TopNav hideToc={hide_toc} hideSearch={hideSearch} navbarEnd={projectParts?.navbar_end?.mdast} />
104104
<PrimaryNavigation
105105
sidebarRef={toc}
106106
hide_toc={hide_toc}
107107
footer={<SidebarFooter content={projectParts?.primary_sidebar_footer?.mdast} />}
108+
navbarEnd={projectParts?.navbar_end?.mdast}
108109
projectSlug={projectSlug}
109110
/>
110111
<TabStateProvider>

themes/book/template.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ parts:
8585
- id: primary_sidebar_footer
8686
description: The primary sidebar footer, to replace Made with MyST
8787
required: false
88+
- id: navbar_end
89+
description: Custom content displayed at the end of the top navbar
90+
required: false
8891
build:
8992
install: npm ci --ignore-scripts
9093
start: npm run start

0 commit comments

Comments
 (0)