@myst-theme/site 0.17.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myst-theme/site",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "type": "module",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -21,11 +21,11 @@
21
21
  "dependencies": {
22
22
  "@headlessui/react": "^1.7.15",
23
23
  "@heroicons/react": "^2.0.18",
24
- "@myst-theme/common": "^0.17.0",
25
- "@myst-theme/diagrams": "^0.17.0",
26
- "@myst-theme/frontmatter": "^0.17.0",
27
- "@myst-theme/providers": "^0.17.0",
28
- "@myst-theme/search": "^0.17.0",
24
+ "@myst-theme/common": "^0.18.0",
25
+ "@myst-theme/diagrams": "^0.18.0",
26
+ "@myst-theme/frontmatter": "^0.18.0",
27
+ "@myst-theme/providers": "^0.18.0",
28
+ "@myst-theme/search": "^0.18.0",
29
29
  "@radix-ui/react-collapsible": "^1.0.3",
30
30
  "@radix-ui/react-dialog": "^1.0.3",
31
31
  "@radix-ui/react-visually-hidden": "^1.1.0",
@@ -33,9 +33,9 @@
33
33
  "lodash.throttle": "^4.1.1",
34
34
  "myst-common": "^1.8.1",
35
35
  "myst-config": "^1.7.9",
36
- "myst-demo": "^0.17.0",
36
+ "myst-demo": "^0.18.0",
37
37
  "myst-spec-ext": "^1.8.1",
38
- "myst-to-react": "^0.17.0",
38
+ "myst-to-react": "^0.18.0",
39
39
  "nbtx": "^0.2.3",
40
40
  "node-cache": "^5.1.2",
41
41
  "node-fetch": "^2.6.11",
@@ -1,5 +1,6 @@
1
1
  import type { GenericParent } from 'myst-common';
2
2
  import { MyST, HashLink } from 'myst-to-react';
3
+ import classNames from 'classnames';
3
4
 
4
5
  export function Abstract({
5
6
  content,
@@ -14,13 +15,13 @@ export function Abstract({
14
15
  }) {
15
16
  if (!content) return null;
16
17
  return (
17
- <div className={className}>
18
- <h2 id={id} className="mb-3 text-base font-semibold group">
18
+ <div className={classNames('myst-abstract', className)}>
19
+ <h2 id={id} className="myst-abstract-title mb-3 text-base font-semibold group">
19
20
  {title}
20
21
  <HashLink id={id} title={`Link to ${title}`} hover className="ml-2" />
21
22
  </h2>
22
- <div className="px-6 py-1 mb-3 rounded-sm bg-slate-50 dark:bg-slate-800">
23
- <MyST ast={content} className="col-body" />
23
+ <div className="myst-abstract-box px-6 py-1 mb-3 rounded-sm bg-slate-50 dark:bg-slate-800">
24
+ <MyST ast={content} className="myst-abstract-content col-body" />
24
25
  </div>
25
26
  </div>
26
27
  );
@@ -13,7 +13,7 @@ export function BackmatterParts({
13
13
  innerClassName?: string;
14
14
  }) {
15
15
  return (
16
- <div className={containerClassName}>
16
+ <div className={classNames('myst-backmatter-parts', containerClassName)}>
17
17
  <Backmatter
18
18
  className={innerClassName}
19
19
  title="Acknowledgments"
@@ -43,15 +43,20 @@ export function Backmatter({
43
43
  }) {
44
44
  if (!content) return null;
45
45
  return (
46
- <div className={classNames('flex flex-col w-full md:flex-row group/backmatter', className)}>
46
+ <div
47
+ className={classNames(
48
+ 'myst-backmatter flex flex-col w-full md:flex-row group/backmatter',
49
+ className,
50
+ )}
51
+ >
47
52
  <h2
48
53
  id={id}
49
- className="mt-5 text-base font-semibold group md:w-[200px] self-start md:flex-none opacity-90 group-hover/backmatter:opacity-100"
54
+ className="myst-backmatter-title mt-5 text-base font-semibold group md:w-[200px] self-start md:flex-none opacity-90 group-hover/backmatter:opacity-100"
50
55
  >
51
56
  {title}
52
57
  <HashLink id={id} title={`Link to ${title}`} hover className="ml-2" />
53
58
  </h2>
54
- <div className="grow opacity-90 group-hover/backmatter:opacity-100 col-screen">
59
+ <div className="myst-backmatter-content grow opacity-90 group-hover/backmatter:opacity-100 col-screen">
55
60
  <MyST ast={getChildren(content)} />
56
61
  </div>
57
62
  </div>
@@ -22,25 +22,30 @@ export function Bibliography({
22
22
  return (
23
23
  <section
24
24
  id="references"
25
- className={classNames(grid, 'subgrid-gap col-screen', containerClassName)}
25
+ className={classNames(
26
+ 'myst-bibliography',
27
+ grid,
28
+ 'subgrid-gap col-screen',
29
+ containerClassName,
30
+ )}
26
31
  >
27
32
  <div className={innerClassName}>
28
33
  {!!hideLongBibliography && filtered.length > hideLongBibliography && (
29
34
  <button
30
35
  onClick={() => setHidden(!hidden)}
31
- className="float-right p-1 px-2 text-xs border rounded hover:border-blue-500 dark:hover:border-blue-400"
36
+ className="myst-bibliography-toggle float-right p-1 px-2 text-xs border rounded hover:border-blue-500 dark:hover:border-blue-400"
32
37
  >
33
38
  {hidden ? 'Show All' : 'Collapse'}
34
39
  </button>
35
40
  )}
36
- <header className="text-lg font-semibold text-stone-900 dark:text-white group">
41
+ <header className="myst-bibliography-header text-lg font-semibold text-stone-900 dark:text-white group">
37
42
  References
38
43
  <HashLink id="references" title="Link to References" hover className="ml-2" />
39
44
  </header>
40
45
  </div>
41
46
  <div
42
47
  className={classNames(
43
- 'pl-3 mb-8 text-xs text-stone-500 dark:text-stone-300',
48
+ 'myst-bibliography-list pl-3 mb-8 text-xs text-stone-500 dark:text-stone-300',
44
49
  innerClassName,
45
50
  )}
46
51
  >
@@ -50,7 +55,7 @@ export function Bibliography({
50
55
  return (
51
56
  <li
52
57
  key={label}
53
- className="break-words"
58
+ className="myst-bibliography-item break-words"
54
59
  id={`cite-${label}`}
55
60
  dangerouslySetInnerHTML={{ __html: html || '' }}
56
61
  />
@@ -60,7 +65,7 @@ export function Bibliography({
60
65
  <li className="text-center list-none">
61
66
  <button
62
67
  onClick={() => setHidden(!hidden)}
63
- className="p-2 border rounded hover:border-blue-500 dark:hover:border-blue-400"
68
+ className="myst-bibliography-toggle p-2 border rounded hover:border-blue-500 dark:hover:border-blue-400"
64
69
  >
65
70
  {hidden ? `Show all ${filtered.length} references` : 'Collapse references'}
66
71
  </button>
@@ -1,6 +1,7 @@
1
1
  import type { SourceFileKind } from 'myst-spec-ext';
2
2
  import type { GenericParent } from 'myst-common';
3
3
  import { MyST, Block as MystBlock } from 'myst-to-react';
4
+ import classNames from 'classnames';
4
5
 
5
6
  /**
6
7
  * @deprecated This component is not maintained, in favor of the generic `MyST` component
@@ -13,7 +14,7 @@ export function ContentBlocks({
13
14
  pageKind?: SourceFileKind;
14
15
  className?: string;
15
16
  }) {
16
- return <MyST ast={mdast} className={className} />;
17
+ return <MyST ast={mdast} className={classNames('myst-content-blocks', className)} />;
17
18
  }
18
19
 
19
20
  /** @deprecated use `import { Block } from 'myst-to-react';` */
@@ -35,15 +35,16 @@ type Props = {
35
35
  * scrollIntoView is used to ensure that when a user clicks on an item, it will smoothly scroll.
36
36
  */
37
37
  const Headings = ({ headings, activeId }: Props) => (
38
- <ul className="text-sm leading-6 text-slate-400">
38
+ <ul className="myst-outline-list text-sm leading-6 text-slate-400">
39
39
  {headings.map((heading) => (
40
40
  <li
41
41
  key={heading.id}
42
- className={classNames('border-l-2 hover:border-l-blue-500', {
42
+ className={classNames('myst-outline-item border-l-2 hover:border-l-blue-500', {
43
43
  'text-blue-600': heading.id === activeId,
44
44
  'border-l-gray-300 dark:border-l-gray-50': heading.id !== activeId,
45
45
  'border-l-blue-500': heading.id === activeId,
46
46
  'bg-blue-50 dark:bg-slate-800': heading.id === activeId,
47
+ 'myst-outline-item-active': heading.id === activeId,
47
48
  })}
48
49
  >
49
50
  <a
@@ -393,24 +394,27 @@ export const DocumentOutline = ({
393
394
  }
394
395
 
395
396
  return (
396
- <Collapsible.Root open={open} onOpenChange={setOpen}>
397
+ <Collapsible.Root open={open} onOpenChange={setOpen} className="myst-outline-section">
397
398
  <nav
398
399
  ref={outlineRef}
399
400
  aria-label="Document Outline"
400
401
  className={classNames(
401
- 'not-prose overflow-y-auto',
402
+ 'myst-outline not-prose overflow-y-auto',
402
403
  'transition-opacity duration-700', // Animation on load
403
404
  className,
404
405
  )}
405
406
  style={{
406
407
  top: top,
407
- maxHeight: `calc(100vh - ${top + 20}px)`,
408
+ maxHeight: `calc(100vh - ${top + 100}px)`,
408
409
  }}
409
410
  >
410
- <div className="flex flex-row gap-2 mb-4 text-sm leading-6 uppercase rounded-lg text-slate-900 dark:text-slate-100">
411
+ <div className="myst-outline-header flex flex-row gap-2 mb-4 text-sm leading-6 uppercase rounded-lg text-slate-900 dark:text-slate-100">
411
412
  {title}
412
413
  <Collapsible.Trigger asChild>
413
- <button className="self-center flex-none rounded-md group hover:bg-slate-300/30 focus:outline outline-blue-200 outline-2">
414
+ <button
415
+ className="myst-outline-collapsible self-center flex-none rounded-md group hover:bg-slate-300/30 focus:outline outline-blue-200 outline-2"
416
+ aria-label="Open Contents"
417
+ >
414
418
  <ChevronRightIcon
415
419
  className="transition-transform duration-300 group-data-[state=open]:rotate-90 text-text-slate-700 dark:text-slate-100"
416
420
  height="1.5rem"
@@ -436,7 +440,7 @@ export function SupportingDocuments() {
436
440
  if (!pages || pages.length === 0) return null;
437
441
  return (
438
442
  <>
439
- <div className="my-4 text-sm leading-6 uppercase text-slate-900 dark:text-slate-100">
443
+ <div className="myst-supporting-documents my-4 text-sm leading-6 uppercase text-slate-900 dark:text-slate-100">
440
444
  Supporting Documents
441
445
  </div>
442
446
  <ul className="flex flex-col gap-2 pl-0 text-sm leading-6 list-none text-slate-700 dark:text-slate-300">
@@ -15,7 +15,10 @@ export const FooterLink = ({
15
15
  return (
16
16
  <Link
17
17
  prefetch="intent"
18
- className="flex-1 block p-4 font-normal text-gray-600 no-underline border border-gray-200 rounded shadow-sm group hover:border-blue-600 dark:hover:border-blue-400 hover:text-blue-600 dark:hover:text-blue-400 dark:text-gray-100 dark:border-gray-500 hover:shadow-lg dark:shadow-neutral-700"
18
+ className={classNames(
19
+ 'myst-footer-link flex-1 block p-4 font-normal text-gray-600 no-underline border border-gray-200 rounded shadow-sm group hover:border-blue-600 dark:hover:border-blue-400 hover:text-blue-600 dark:hover:text-blue-400 dark:text-gray-100 dark:border-gray-500 hover:shadow-lg dark:shadow-neutral-700',
20
+ { 'myst-footer-link-prev': right, 'myst-footer-link-next': !right },
21
+ )}
19
22
  to={withBaseurl(url, baseurl)}
20
23
  >
21
24
  <div className="flex h-full align-middle">
@@ -23,18 +26,20 @@ export const FooterLink = ({
23
26
  <ArrowLeftIcon
24
27
  width="1.5rem"
25
28
  height="1.5rem"
26
- className="self-center transition-transform group-hover:-translate-x-1 shrink-0"
29
+ className="myst-footer-link-icon self-center transition-transform group-hover:-translate-x-1 shrink-0"
27
30
  />
28
31
  )}
29
32
  <div className={classNames('flex-grow', { 'text-right': right })}>
30
- <div className="text-xs text-gray-500 dark:text-gray-400">{group || ' '}</div>
33
+ <div className="myst-footer-link-group text-xs text-gray-500 dark:text-gray-400">
34
+ {group || ' '}
35
+ </div>
31
36
  {short_title || title}
32
37
  </div>
33
38
  {!right && (
34
39
  <ArrowRightIcon
35
40
  width="1.5rem"
36
41
  height="1.5rem"
37
- className="self-center transition-transform group-hover:translate-x-1 shrink-0"
42
+ className="myst-footer-link-icon self-center transition-transform group-hover:translate-x-1 shrink-0"
38
43
  />
39
44
  )}
40
45
  </div>
@@ -45,7 +50,7 @@ export const FooterLink = ({
45
50
  export function FooterLinksBlock({ links }: { links?: FooterLinks }) {
46
51
  if (!links || (!links.navigation?.prev && !links.navigation?.next)) return null;
47
52
  return (
48
- <div className="flex pt-10 mb-10 space-x-4">
53
+ <div className="myst-footer-links flex pt-10 mb-10 space-x-4">
49
54
  {links.navigation?.prev && <FooterLink {...links.navigation?.prev} right />}
50
55
  {links.navigation?.next && <FooterLink {...links.navigation?.next} />}
51
56
  </div>
@@ -20,29 +20,33 @@ export function Footnotes({
20
20
  return (
21
21
  <section
22
22
  id="footnotes"
23
- className={classNames(grid, 'subgrid-gap col-screen', containerClassName)}
23
+ className={classNames('myst-footnotes', grid, 'subgrid-gap col-screen', containerClassName)}
24
24
  >
25
25
  <div className={innerClassName}>
26
- <header className="text-lg font-semibold text-stone-900 dark:text-white group">
26
+ <header className="myst-footnotes-header text-lg font-semibold text-stone-900 dark:text-white group">
27
27
  Footnotes
28
28
  <HashLink id="footnotes" title="Link to Footnotes" hover className="ml-2" />
29
29
  </header>
30
30
  </div>
31
31
  <div
32
32
  className={classNames(
33
- 'pl-3 mb-8 text-xs text-stone-500 dark:text-stone-300',
33
+ 'myst-footnotes-list pl-3 mb-8 text-xs text-stone-500 dark:text-stone-300',
34
34
  innerClassName,
35
35
  )}
36
36
  >
37
37
  <ol>
38
38
  {defs.map((fn) => {
39
39
  return (
40
- <li key={(fn as GenericNode).key} id={`fn-${fn.identifier}`} className="group">
40
+ <li
41
+ key={(fn as GenericNode).key}
42
+ id={`fn-${fn.identifier}`}
43
+ className="myst-footnotes-item group"
44
+ >
41
45
  <div className="flex flex-row">
42
- <div className="break-words grow">
46
+ <div className="myst-footnote-content break-words grow">
43
47
  <MyST ast={fn.children} />
44
48
  </div>
45
- <div className="flex flex-col grow-0">
49
+ <div className="myst-footnote-backlinks flex flex-col grow-0">
46
50
  {refs
47
51
  .filter((ref) => ref.identifier === fn.identifier)
48
52
  .map((ref) => (
@@ -1,6 +1,7 @@
1
1
  import { type KnownParts } from '../utils.js';
2
2
  import { Abstract } from './Abstract.js';
3
3
  import { Keywords } from './Keywords.js';
4
+ import classNames from 'classnames';
4
5
 
5
6
  export function FrontmatterParts({
6
7
  parts,
@@ -17,21 +18,28 @@ export function FrontmatterParts({
17
18
  }) {
18
19
  if (!parts.abstract && !parts.keypoints && !parts.summary) return null;
19
20
  return (
20
- <div className={containerClassName}>
21
- <Abstract className={innerClassName} content={parts.abstract} />
21
+ <div className={classNames('myst-fm-parts', containerClassName)}>
22
22
  <Abstract
23
- className={innerClassName}
23
+ className={classNames('myst-fm-section', innerClassName)}
24
+ content={parts.abstract}
25
+ />
26
+ <Abstract
27
+ className={classNames('myst-fm-section', innerClassName)}
24
28
  content={parts.keypoints}
25
29
  title="Key Points"
26
30
  id="keypoints"
27
31
  />
28
32
  <Abstract
29
- className={innerClassName}
33
+ className={classNames('myst-fm-section', innerClassName)}
30
34
  content={parts.summary}
31
35
  title="Plain Language Summary"
32
36
  id="summary"
33
37
  />
34
- <Keywords className={innerClassName} keywords={keywords} hideKeywords={hideKeywords} />
38
+ <Keywords
39
+ className={classNames('myst-fm-section', innerClassName)}
40
+ keywords={keywords}
41
+ hideKeywords={hideKeywords}
42
+ />
35
43
  </div>
36
44
  );
37
45
  }
@@ -15,11 +15,13 @@ export function ArticleHeader({
15
15
  children,
16
16
  toggleTheme,
17
17
  className,
18
+ hideAuthors,
18
19
  }: {
19
20
  frontmatter: PageFrontmatterWithDownloads;
20
21
  children?: React.ReactNode;
21
22
  toggleTheme?: boolean;
22
23
  className?: string;
24
+ hideAuthors?: boolean;
23
25
  }) {
24
26
  const grid = useGridSystemProvider();
25
27
  const { subject, venue, volume, issue, ...rest } = frontmatter ?? {};
@@ -32,12 +34,12 @@ export function ArticleHeader({
32
34
  'col-page-left': grid === 'article-grid',
33
35
  };
34
36
  return (
35
- <header className="relative col-screen">
37
+ <header className="myst-article-header relative col-screen">
36
38
  {frontmatter?.banner && (
37
39
  // This is the banner contained in a full-bleed div
38
40
  <div
39
41
  className={classNames(
40
- 'absolute',
42
+ 'myst-article-header-background absolute',
41
43
  grid,
42
44
  'subgrid-gap col-screen bg-no-repeat bg-cover bg-top w-full h-full -z-10 pointer-events-none',
43
45
  )}
@@ -48,7 +50,7 @@ export function ArticleHeader({
48
50
  )}
49
51
  <div
50
52
  className={classNames(
51
- 'w-full relative col-screen article',
53
+ 'myst-article-header-content w-full relative col-screen article',
52
54
  grid,
53
55
  'subgrid-gap',
54
56
  {
@@ -59,7 +61,7 @@ export function ArticleHeader({
59
61
  )}
60
62
  >
61
63
  <div
62
- className={classNames(positionBackground, {
64
+ className={classNames('myst-article-header-banner', positionBackground, {
63
65
  'shadow-2xl bg-white/80 dark:bg-black/80 backdrop-blur': frontmatter?.banner,
64
66
  })}
65
67
  >
@@ -83,16 +85,19 @@ export function ArticleHeader({
83
85
  <OpenAccessBadge open_access={frontmatter?.open_access} />
84
86
  <GitHubLink github={frontmatter?.github} />
85
87
  </div>
86
- {toggleTheme && <ThemeButton className="inline-block w-5 h-5 mt-0.5 ml-1" />}
88
+ {toggleTheme && (
89
+ <ThemeButton className="myst-article-header-theme-button inline-block w-5 h-5 mt-0.5 ml-1" />
90
+ )}
87
91
  </div>
88
92
  <div className="flex flex-col mb-10 md:flex-row">
89
93
  <FrontmatterBlock
90
94
  frontmatter={rest}
91
95
  authorStyle="list"
92
- className={classNames('flex-grow', {
96
+ className={classNames('myst-article-header-fm flex-grow', {
93
97
  'pt-6 px-6': frontmatter?.banner,
94
98
  ...positionFrontmatter,
95
99
  })}
100
+ hideAuthors={hideAuthors}
96
101
  hideBadges
97
102
  hideExports
98
103
  />
@@ -12,18 +12,20 @@ export function Keywords({
12
12
  }) {
13
13
  if (hideKeywords || !keywords || keywords.length === 0) return null;
14
14
  return (
15
- <div className={classNames('mb-10 group', className)}>
16
- <span className="mr-2 font-semibold">Keywords:</span>
17
- {keywords.map((k, i) => (
18
- <span
19
- key={k}
20
- className={classNames({
21
- "after:content-[','] after:mr-1": i < keywords.length - 1,
22
- })}
23
- >
24
- {k}
25
- </span>
26
- ))}
15
+ <div className={classNames('myst-keywords mb-10 group', className)}>
16
+ <span className="myst-keywords-label mr-2 font-semibold">Keywords:</span>
17
+ <span className="myst-keywords-list">
18
+ {keywords.map((k, i) => (
19
+ <span
20
+ key={k}
21
+ className={classNames('myst-keywords-item', {
22
+ "after:content-[','] after:mr-1": i < keywords.length - 1,
23
+ })}
24
+ >
25
+ {k}
26
+ </span>
27
+ ))}
28
+ </span>
27
29
  <HashLink id="keywords" title="Link to Keywords" hover className="ml-2" />
28
30
  </div>
29
31
  );
@@ -7,9 +7,9 @@ import type { SiteManifest } from 'myst-config';
7
7
  export function ActionMenu({ actions }: { actions?: SiteManifest['actions'] }) {
8
8
  if (!actions || actions.length === 0) return null;
9
9
  return (
10
- <Menu as="div" className="relative">
10
+ <Menu as="div" className="myst-action-menu relative">
11
11
  <div>
12
- <Menu.Button className="flex text-sm bg-transparent rounded-full focus:outline-none">
12
+ <Menu.Button className="myst-action-menu-button flex text-sm bg-transparent rounded-full focus:outline-none">
13
13
  <span className="sr-only">Open Menu</span>
14
14
  <div className="flex items-center text-stone-200 hover:text-white">
15
15
  <EllipsisVerticalIcon width="2rem" height="2rem" className="p-1" />
@@ -25,13 +25,14 @@ export function ActionMenu({ actions }: { actions?: SiteManifest['actions'] }) {
25
25
  leaveFrom="transform opacity-100 scale-100"
26
26
  leaveTo="transform opacity-0 scale-95"
27
27
  >
28
- <Menu.Items className="absolute right-0 w-48 py-1 mt-2 origin-top-right bg-white rounded-sm shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
28
+ <Menu.Items className="myst-action-menu-dropdown absolute right-0 w-48 py-1 mt-2 origin-top-right bg-white rounded-sm shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
29
29
  {actions?.map((action) => (
30
30
  <Menu.Item key={action.url}>
31
31
  {({ active }) => (
32
32
  <a
33
33
  href={action.url}
34
34
  className={classNames(
35
+ 'myst-action-menu-item',
35
36
  active ? 'bg-gray-100' : '',
36
37
  'block px-4 py-2 text-sm text-gray-700',
37
38
  )}
@@ -19,13 +19,13 @@ export function HomeLink({
19
19
  const nothingSet = !logo && !logoText;
20
20
  return (
21
21
  <Link
22
- className="flex items-center ml-3 dark:text-white w-fit md:ml-5 xl:ml-7"
22
+ className="myst-home-link flex items-center ml-3 dark:text-white w-fit md:ml-5 xl:ml-7"
23
23
  to={url ? url : withBaseurl('/', baseurl)}
24
24
  prefetch="intent"
25
25
  >
26
26
  {logo && (
27
27
  <div
28
- className={classNames('p-1 mr-3', {
28
+ className={classNames('myst-home-link-logo p-1 mr-3', {
29
29
  'dark:bg-white dark:rounded': !logoDark,
30
30
  })}
31
31
  >
@@ -48,9 +48,9 @@ export function LoadingBar() {
48
48
  return (
49
49
  <div
50
50
  className={classNames(
51
- 'w-screen h-[2px] bg-blue-500 absolute left-0 bottom-0 transition-transform',
51
+ 'myst-loading-bar w-screen h-[2px] bg-blue-500 absolute left-0 bottom-0 transition-transform',
52
52
  {
53
- 'animate-load scale-x-40': isLoading,
53
+ 'myst-loading-bar-progress animate-load scale-x-40': isLoading,
54
54
  'scale-x-100': !isLoading,
55
55
  },
56
56
  )}
@@ -92,7 +92,7 @@ export const ConfigurablePrimaryNavigation = ({
92
92
  <>
93
93
  {open && !mobileOnly && headings && (
94
94
  <div
95
- className="fixed inset-0 z-30 bg-black opacity-50"
95
+ className="myst-navigation-overlay fixed inset-0 z-30 bg-black opacity-50"
96
96
  style={{ marginTop: top }}
97
97
  onClick={() => setOpen(false)}
98
98
  ></div>
@@ -7,6 +7,9 @@ import {
7
7
  useGridSystemProvider,
8
8
  useThemeTop,
9
9
  useIsWide,
10
+ useBaseurl,
11
+ withBaseurl,
12
+ useBannerState,
10
13
  } from '@myst-theme/providers';
11
14
  import type { Heading } from '@myst-theme/common';
12
15
  import { Toc } from './TableOfContentsItems.js';
@@ -16,12 +19,14 @@ import * as Collapsible from '@radix-ui/react-collapsible';
16
19
  import { ChevronRightIcon } from '@heroicons/react/24/solid';
17
20
 
18
21
  export function SidebarNavItem({ item }: { item: SiteNavItem }) {
22
+ const baseurl = useBaseurl();
19
23
  if (!item.children?.length) {
20
24
  return (
21
25
  <ExternalOrInternalLink
22
26
  nav
23
- to={item.url ?? ''}
27
+ to={withBaseurl(item.url, baseurl) ?? ''}
24
28
  className={classNames(
29
+ 'myst-primary-sidebar-item-short',
25
30
  'p-2 my-1 rounded-lg',
26
31
  'hover:bg-slate-300/30',
27
32
  'block break-words focus:outline outline-blue-200 outline-2 rounded',
@@ -36,38 +41,40 @@ export function SidebarNavItem({ item }: { item: SiteNavItem }) {
36
41
  <Collapsible.Root className="w-full" open={open} onOpenChange={setOpen}>
37
42
  <div
38
43
  className={classNames(
44
+ 'myst-primary-sidebar-item',
39
45
  'flex flex-row w-full gap-2 px-2 my-1 text-left rounded-lg outline-none',
40
46
  'hover:bg-slate-300/30',
41
47
  )}
42
48
  >
43
49
  <ExternalOrInternalLink
44
50
  nav
45
- to={item.url ?? ''}
46
- className={classNames('py-2 grow', {})}
51
+ to={withBaseurl(item.url, baseurl) ?? ''}
52
+ className={classNames('myst-primary-sidebar-item-title py-2 grow', {})}
47
53
  onClick={() => setOpen(!open)}
48
54
  >
49
55
  {item.title}
50
56
  </ExternalOrInternalLink>
51
57
  <Collapsible.Trigger asChild>
52
58
  <button
53
- className="self-center flex-none rounded-md group hover:bg-slate-300/30 focus:outline outline-blue-200 outline-2"
59
+ className="myst-primary-sidebar-item-child self-center flex-none rounded-md group hover:bg-slate-300/30 focus:outline outline-blue-200 outline-2"
54
60
  aria-label="Open Folder"
55
61
  >
56
62
  <ChevronRightIcon
57
- className="transition-transform duration-300 group-data-[state=open]:rotate-90 text-text-slate-700 dark:text-slate-100"
63
+ className="myst-primary-sidebar-item-icon transition-transform duration-300 group-data-[state=open]:rotate-90 text-text-slate-700 dark:text-slate-100"
58
64
  height="1.5rem"
59
65
  width="1.5rem"
60
66
  />
61
67
  </button>
62
68
  </Collapsible.Trigger>
63
69
  </div>
64
- <Collapsible.Content className="pl-3 pr-[2px] collapsible-content">
70
+ <Collapsible.Content className="myst-primary-sidebar-item-content pl-3 pr-[2px] collapsible-content">
65
71
  {item.children.map((action) => (
66
72
  <ExternalOrInternalLink
67
73
  nav
68
74
  key={action.url}
69
- to={action.url || ''}
75
+ to={withBaseurl(action.url, baseurl) || ''}
70
76
  className={classNames(
77
+ 'myst-primary-sidebar-item-link',
71
78
  'p-2 my-1 rounded-lg',
72
79
  'hover:bg-slate-300/30',
73
80
  'block break-words focus:outline outline-blue-200 outline-2 rounded',
@@ -97,15 +104,18 @@ export function useSidebarHeight<T extends HTMLElement = HTMLElement>(top = 0, i
97
104
  const toc = useRef<HTMLDivElement>(null);
98
105
  const transitionState = useNavigation().state;
99
106
  const wide = useIsWide();
107
+ const { bannerState } = useBannerState();
108
+ const totalTop = top + bannerState.height;
109
+
100
110
  const setHeight = () => {
101
111
  if (!container.current || !toc.current) return;
102
112
  const height = container.current.offsetHeight - window.scrollY;
103
113
  const div = toc.current.firstChild as HTMLDivElement;
104
114
  if (div)
105
115
  div.style.height = wide
106
- ? `min(calc(100vh - ${top}px), ${height + inset}px)`
107
- : `calc(100vh - ${top}px)`;
108
- if (div) div.style.height = `min(calc(100vh - ${top}px), ${height + inset}px)`;
116
+ ? `min(calc(100vh - ${totalTop}px), ${height + inset}px)`
117
+ : `calc(100vh - ${totalTop}px)`;
118
+ if (div) div.style.height = `min(calc(100vh - ${totalTop}px), ${height + inset}px)`;
109
119
  const nav = toc.current.querySelector('nav');
110
120
  if (nav) nav.style.opacity = height > 150 ? '1' : '0';
111
121
  };
@@ -117,7 +127,7 @@ export function useSidebarHeight<T extends HTMLElement = HTMLElement>(top = 0, i
117
127
  return () => {
118
128
  window.removeEventListener('scroll', handleScroll);
119
129
  };
120
- }, [container, toc, transitionState, wide]);
130
+ }, [container, toc, transitionState, wide, totalTop]);
121
131
  return { container, toc };
122
132
  }
123
133
 
@@ -137,6 +147,7 @@ export const PrimarySidebar = ({
137
147
  mobileOnly?: boolean;
138
148
  }) => {
139
149
  const top = useThemeTop();
150
+ const { bannerState } = useBannerState();
140
151
  const grid = useGridSystemProvider();
141
152
  const footerRef = useRef<HTMLDivElement>(null);
142
153
  const [open] = useNavOpen();
@@ -155,16 +166,18 @@ export const PrimarySidebar = ({
155
166
  <div
156
167
  ref={sidebarRef as any}
157
168
  className={classNames(
169
+ 'myst-primary-sidebar',
158
170
  'fixed',
159
171
  `xl:${grid}`, // for example, xl:article-grid
160
172
  'grid-gap xl:w-screen xl:pointer-events-none overflow-auto max-xl:min-w-[300px]',
161
173
  { 'lg:hidden': nav && hide_toc },
162
174
  { hidden: !open, 'z-30': open, 'z-10': !open },
163
175
  )}
164
- style={{ top }}
176
+ style={{ top: top + bannerState.height }}
165
177
  >
166
178
  <div
167
179
  className={classNames(
180
+ 'myst-primary-sidebar-pointer',
168
181
  'pointer-events-auto',
169
182
  'xl:col-margin-left flex-col',
170
183
  'overflow-hidden',
@@ -177,11 +190,11 @@ export const PrimarySidebar = ({
177
190
  },
178
191
  )}
179
192
  >
180
- <div className="flex-grow py-6 overflow-y-auto primary-scrollbar">
193
+ <div className="myst-primary-sidebar-nav flex-grow py-6 overflow-y-auto primary-scrollbar">
181
194
  {nav && (
182
195
  <nav
183
196
  aria-label="Navigation"
184
- className="overflow-y-hidden transition-opacity ml-3 xl:ml-0 mr-3 max-w-[350px] lg:hidden"
197
+ className="myst-primary-sidebar-topnav overflow-y-hidden transition-opacity ml-3 xl:ml-0 mr-3 max-w-[350px] lg:hidden"
185
198
  >
186
199
  <SidebarNav nav={nav} />
187
200
  </nav>
@@ -190,7 +203,7 @@ export const PrimarySidebar = ({
190
203
  {headings && (
191
204
  <nav
192
205
  aria-label="Table of Contents"
193
- className="flex-grow overflow-y-hidden transition-opacity ml-3 xl:ml-0 mr-3 max-w-[350px]"
206
+ className="myst-primary-sidebar-toc flex-grow overflow-y-hidden transition-opacity ml-3 xl:ml-0 mr-3 max-w-[350px]"
194
207
  >
195
208
  <Toc headings={headings} />
196
209
  </nav>
@@ -198,7 +211,7 @@ export const PrimarySidebar = ({
198
211
  </div>
199
212
  {footer && (
200
213
  <div
201
- className="flex-none py-6 transition-all duration-700 translate-y-6 opacity-0"
214
+ className="myst-primary-sidebar-footer flex-none py-6 transition-all duration-700 translate-y-6 opacity-0"
202
215
  ref={footerRef}
203
216
  >
204
217
  {footer}
@@ -174,7 +174,7 @@ function SearchShortcut() {
174
174
  return (
175
175
  <div
176
176
  aria-hidden
177
- className="items-center hidden mx-1 font-mono text-sm text-gray-400 sm:flex gap-x-1"
177
+ className="myst-search-shortcut items-center hidden mx-1 font-mono text-sm text-gray-600 sm:flex gap-x-1"
178
178
  >
179
179
  <kbd
180
180
  className={classNames(
@@ -224,7 +224,7 @@ function SearchResultItem({
224
224
 
225
225
  // Render the icon
226
226
  const iconProps = useMemo(() => {
227
- return { className: 'inline-block w-6 mx-2 shrink-0' };
227
+ return { className: 'myst-search-result-icon inline-block w-6 mx-2 shrink-0' };
228
228
  }, []);
229
229
  const iconRenderer = createElement(
230
230
  type === 'lvl1' ? DocumentIcon : type === 'content' ? Bars3BottomLeftIcon : HashtagIcon,
@@ -241,7 +241,7 @@ function SearchResultItem({
241
241
  text={title}
242
242
  matches={matches}
243
243
  limit={type === 'content' ? charLimit : undefined}
244
- className="text-sm"
244
+ className="myst-search-result-highlight text-sm"
245
245
  />
246
246
  );
247
247
 
@@ -338,7 +338,7 @@ function SearchResults({
338
338
  [onHoverSelect],
339
339
  );
340
340
  return (
341
- <div className="mt-4 overflow-y-scroll">
341
+ <div className="myst-search-results mt-4 overflow-y-scroll">
342
342
  {searchResults.length ? (
343
343
  <ul
344
344
  // Accessiblity:
@@ -363,7 +363,7 @@ function SearchResults({
363
363
  // Indicate whether this is selected
364
364
  aria-selected={selectedIndex === index}
365
365
  // Allow for nested-highlighting
366
- className="group"
366
+ className="myst-search-result-item group"
367
367
  // Trigger selection on movement, so that scrolling doesn't trigger handler
368
368
  onMouseMove={handleMouseMove}
369
369
  >
@@ -372,7 +372,7 @@ function SearchResults({
372
372
  ))}
373
373
  </ul>
374
374
  ) : (
375
- <span>No results found.</span>
375
+ <span className="myst-search-no-results">No results found.</span>
376
376
  )}
377
377
  </div>
378
378
  );
@@ -506,8 +506,9 @@ function SearchForm({
506
506
  <>
507
507
  <form onSubmit={onSubmit}>
508
508
  <div className="relative flex w-full h-10 flow-row gap-x-1 ">
509
- <label id={searchListID} htmlFor={searchInputID}>
510
- <MagnifyingGlassIcon className="absolute text-gray-400 inset-y-0 start-0 h-10 w-10 p-2.5 aspect-square flex items-center pointer-events-none" />
509
+ <label id={searchLabelID} htmlFor={searchInputID}>
510
+ <MagnifyingGlassIcon className="absolute text-gray-600 inset-y-0 start-0 h-10 w-10 p-2.5 aspect-square flex items-center pointer-events-none" />
511
+ <span className="hidden">Search query</span>
511
512
  </label>
512
513
  <input
513
514
  autoComplete="off"
@@ -515,7 +516,7 @@ function SearchForm({
515
516
  disabled={!enabled}
516
517
  autoCapitalize="false"
517
518
  className={classNames(
518
- 'block flex-grow p-2 ps-10 placeholder-gray-400',
519
+ 'myst-search-input block flex-grow p-2 ps-10 placeholder-gray-400',
519
520
  'border border-gray-300 dark:border-gray-600',
520
521
  'rounded-lg bg-gray-50 dark:bg-gray-700',
521
522
  'focus:ring-blue-500 dark:focus:ring-blue-500',
@@ -540,7 +541,7 @@ function SearchForm({
540
541
  </div>
541
542
  </form>
542
543
  {!enabled && (
543
- <div className="mx-2 mt-4 text-sm text-gray-500">
544
+ <div className="myst-search-no-results mx-2 mt-4 text-sm text-gray-500">
544
545
  Search is not enabled for this site. :(
545
546
  </div>
546
547
  )}
@@ -560,12 +561,13 @@ const SearchPlaceholderButton = forwardRef<
560
561
  <button
561
562
  {...props}
562
563
  className={classNames(
564
+ 'myst-search-bar',
563
565
  className,
564
- 'flex items-center h-10 aspect-square sm:w-64 text-left text-gray-400',
566
+ 'flex items-center h-10 aspect-square sm:w-64 text-left text-gray-600',
565
567
  'border border-gray-300 dark:border-gray-600',
566
568
  'rounded-lg bg-gray-50 dark:bg-gray-700',
567
569
  {
568
- 'hover:ring-blue-500': !disabled,
570
+ 'myst-search-bar-disabled hover:ring-blue-500': !disabled,
569
571
  'dark:hover:ring-blue-500': !disabled,
570
572
  'hover:border-blue-500': !disabled,
571
573
  'dark:hover:border-blue-500': !disabled,
@@ -575,7 +577,7 @@ const SearchPlaceholderButton = forwardRef<
575
577
  ref={ref}
576
578
  >
577
579
  <MagnifyingGlassIcon className="p-2.5 h-10 w-10 aspect-square" />
578
- <span className="hidden sm:block grow">Search</span>
580
+ <span className="myst-search-text-placeholder hidden sm:block grow">Search</span>
579
581
  <SearchShortcut />
580
582
  </button>
581
583
  );
@@ -630,7 +632,7 @@ export function Search({ debounceTime = 500, charLimit = 64 }: SearchProps) {
630
632
  <Dialog.Portal>
631
633
  <Dialog.Overlay className="fixed inset-0 bg-[#656c85cc] z-[1000]" />
632
634
  <Dialog.Content
633
- className="fixed flex flex-col top-0 bg-white dark:bg-stone-900 z-[1001] h-screen w-screen sm:left-1/2 sm:-translate-x-1/2 sm:w-[90vw] sm:max-w-screen-sm sm:h-auto sm:max-h-[var(--content-max-height)] sm:top-[var(--content-top)] sm:rounded-md p-4 text-gray-900 dark:text-white"
635
+ className="myst-search-dialog fixed flex flex-col top-0 bg-white dark:bg-stone-900 z-[1001] h-screen w-screen sm:left-1/2 sm:-translate-x-1/2 sm:w-[90vw] sm:max-w-screen-sm sm:h-auto sm:max-h-[var(--content-max-height)] sm:top-[var(--content-top)] sm:rounded-md p-4 text-gray-900 dark:text-white"
634
636
  // Store state as CSS variables so that we can set the style with tailwind variants
635
637
  style={
636
638
  {
@@ -43,8 +43,12 @@ function nestToc(toc: Heading[]): NestedHeading[] {
43
43
 
44
44
  function pathnameMatchesHeading(pathname: string, heading: Heading, baseurl?: string) {
45
45
  const headingPath = withBaseurl(heading.path, baseurl);
46
- if (pathname && headingPath === `${pathname}/index`) return true;
47
- return headingPath === pathname;
46
+ // In static html builds, pathname ends up with an unwanted trailing slash
47
+ // and then won't match the heading's slashless path. So first normalize the
48
+ // given path by removing any trailing slash.
49
+ const normedPath = pathname.endsWith('/') ? pathname.slice(0, -1) : pathname;
50
+ if (normedPath && headingPath === `${normedPath}/index`) return true;
51
+ return headingPath === normedPath;
48
52
  }
49
53
 
50
54
  function childrenOpen(headings: NestedHeading[], pathname: string, baseurl?: string): string[] {
@@ -61,7 +65,7 @@ function childrenOpen(headings: NestedHeading[], pathname: string, baseurl?: str
61
65
  export const Toc = ({ headings }: { headings: Heading[] }) => {
62
66
  const nested = nestToc(headings);
63
67
  return (
64
- <div className="w-full px-1 dark:text-white">
68
+ <div className="myst-toc w-full px-1 dark:text-white">
65
69
  {nested.map((item) => (
66
70
  <NestedToc heading={item} key={item.id} />
67
71
  ))}
@@ -89,7 +93,7 @@ function LinkItem({
89
93
  <Link
90
94
  title={`${heading.enumerator ? `${heading.enumerator} ` : ''}${heading.title}`}
91
95
  className={classNames(
92
- 'block break-words focus:outline outline-blue-200 outline-2 rounded',
96
+ 'myst-toc-heading block break-words focus:outline outline-blue-200 outline-2 rounded',
93
97
  className,
94
98
  )}
95
99
  to={heading.url}
@@ -155,8 +159,8 @@ const NestedToc = ({ heading }: { heading: NestedHeading }) => {
155
159
  if (!heading.children || heading.children.length === 0) {
156
160
  return (
157
161
  <LinkItem
158
- className={classNames('p-2 my-1 rounded-lg', {
159
- 'bg-blue-300/30': exact,
162
+ className={classNames('myst-toc-item p-2 my-1 rounded-lg', {
163
+ 'myst-toc-item-exact bg-blue-300/30': exact,
160
164
  'hover:bg-slate-300/30': !exact,
161
165
  'font-bold': heading.level === 'index',
162
166
  })}
@@ -168,9 +172,9 @@ const NestedToc = ({ heading }: { heading: NestedHeading }) => {
168
172
  <Collapsible.Root className="w-full" open={open} onOpenChange={setOpen}>
169
173
  <div
170
174
  className={classNames(
171
- 'flex flex-row w-full gap-2 px-2 my-1 text-left rounded-lg outline-none',
175
+ 'myst-toc-item flex flex-row w-full gap-2 px-2 my-1 text-left rounded-lg outline-none',
172
176
  {
173
- 'bg-blue-300/30': exact,
177
+ 'myst-toc-item-exact bg-blue-300/30': exact,
174
178
  'hover:bg-slate-300/30': !exact,
175
179
  },
176
180
  )}
@@ -8,15 +8,15 @@ export function ThemeButton({ className = 'w-8 h-8 mx-3' }: { className?: string
8
8
  return (
9
9
  <button
10
10
  className={classNames(
11
- 'theme rounded-full aspect-square border border-stone-700 dark:border-white hover:bg-neutral-100 border-solid overflow-hidden text-stone-700 dark:text-white hover:text-stone-500 dark:hover:text-neutral-800',
11
+ 'myst-theme-button theme rounded-full aspect-square border border-stone-700 dark:border-white hover:bg-neutral-100 border-solid overflow-hidden text-stone-700 dark:text-white hover:text-stone-500 dark:hover:text-neutral-800',
12
12
  className,
13
13
  )}
14
14
  title={`Toggle theme between light and dark mode`}
15
15
  aria-label={`Toggle theme between light and dark mode`}
16
16
  onClick={nextTheme}
17
17
  >
18
- <MoonIcon className="h-full w-full p-0.5 hidden dark:block" />
19
- <SunIcon className="h-full w-full p-0.5 dark:hidden" />
18
+ <MoonIcon className="myst-theme-moon-icon h-full w-full p-0.5 hidden dark:block" />
19
+ <SunIcon className="myst-theme-sun-icon h-full w-full p-0.5 dark:hidden" />
20
20
  </button>
21
21
  );
22
22
  }
@@ -5,7 +5,13 @@ import { ChevronDownIcon, Bars3Icon as MenuIcon } from '@heroicons/react/24/soli
5
5
  import type { SiteManifest, SiteNavItem } from 'myst-config';
6
6
  import { ThemeButton } from './ThemeButton.js';
7
7
  import { Search } from './Search.js';
8
- import { useNavLinkProvider, useNavOpen, useSiteManifest } from '@myst-theme/providers';
8
+ import {
9
+ useBaseurl,
10
+ useNavLinkProvider,
11
+ useNavOpen,
12
+ useSiteManifest,
13
+ withBaseurl,
14
+ } from '@myst-theme/providers';
9
15
  import { LoadingBar } from './Loading.js';
10
16
  import { HomeLink } from './HomeLink.js';
11
17
  import { ActionMenu } from './ActionMenu.js';
@@ -15,12 +21,13 @@ export const DEFAULT_NAV_HEIGHT = 60;
15
21
 
16
22
  export function NavItem({ item }: { item: SiteNavItem }) {
17
23
  const NavLink = useNavLinkProvider();
24
+ const baseurl = useBaseurl();
18
25
  if (!('children' in item)) {
19
26
  return (
20
- <div className="relative inline-block mx-2 grow-0">
27
+ <div className="myst-top-nav-item relative inline-block mx-2 grow-0">
21
28
  <ExternalOrInternalLink
22
29
  nav
23
- to={item.url ?? ''}
30
+ to={withBaseurl(item.url, baseurl) ?? ''}
24
31
  className={({ isActive }) =>
25
32
  classNames(
26
33
  'inline-flex items-center justify-center w-full mx-2 py-1 text-md font-medium dark:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75',
@@ -36,7 +43,7 @@ export function NavItem({ item }: { item: SiteNavItem }) {
36
43
  );
37
44
  }
38
45
  return (
39
- <Menu as="div" className="relative inline-block mx-2 grow-0">
46
+ <Menu as="div" className="myst-top-nav-dropdown relative inline-block mx-2 grow-0">
40
47
  <div className="inline-block">
41
48
  <Menu.Button className="inline-flex items-center justify-center w-full py-1 mx-2 font-medium rounded-md text-md text-stone-900 dark:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75">
42
49
  <span>{item.title}</span>
@@ -56,14 +63,14 @@ export function NavItem({ item }: { item: SiteNavItem }) {
56
63
  leaveFrom="transform opacity-100 scale-100"
57
64
  leaveTo="transform opacity-0 scale-95"
58
65
  >
59
- <Menu.Items className="absolute w-48 py-1 mt-2 origin-top-left bg-white rounded-sm shadow-lg left-4 ring-1 ring-black ring-opacity-5 focus:outline-none">
66
+ <Menu.Items className="myst-top-nav-dropdown-items absolute w-48 py-1 mt-2 origin-top-left bg-white rounded-sm shadow-lg left-4 ring-1 ring-black ring-opacity-5 focus:outline-none">
60
67
  {item.children?.map((action) => (
61
68
  <Menu.Item key={action.url}>
62
69
  {/* This is really ugly, BUT, the action needs to be defined HERE or the click away doesn't work for some reason */}
63
70
  {action.url?.startsWith('http') ? (
64
71
  <a
65
72
  href={action.url || ''}
66
- className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-black"
73
+ className="myst-top-nav-dropdown-item block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-black"
67
74
  target="_blank"
68
75
  rel="noopener noreferrer"
69
76
  >
@@ -74,7 +81,7 @@ export function NavItem({ item }: { item: SiteNavItem }) {
74
81
  to={action.url || ''}
75
82
  className={({ isActive }) =>
76
83
  classNames(
77
- ' block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-black ',
84
+ 'myst-top-nav-dropdown-item block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-black',
78
85
  {
79
86
  'text-black font-bold': isActive,
80
87
  },
@@ -109,8 +116,8 @@ export function TopNav({ hideToc, hideSearch }: { hideToc?: boolean; hideSearch?
109
116
  const { title, nav, actions } = config ?? {};
110
117
  const { logo, logo_dark, logo_text, logo_url } = config?.options ?? {};
111
118
  return (
112
- <div className="bg-white/80 backdrop-blur dark:bg-stone-900/80 shadow dark:shadow-stone-700 p-3 md:px-8 sticky w-screen top-0 z-30 h-[60px]">
113
- <nav className="flex items-center justify-between flex-nowrap max-w-[1440px] mx-auto">
119
+ <div className="myst-top-nav bg-white/80 backdrop-blur dark:bg-stone-900/80 shadow dark:shadow-stone-700 p-3 md:px-8 sticky w-screen top-0 z-30 h-[60px]">
120
+ <nav className="myst-top-nav-bar flex items-center justify-between flex-nowrap max-w-[1440px] mx-auto">
114
121
  <div className="flex flex-row xl:min-w-[19.5rem] mr-2 sm:mr-7 justify-start items-center shrink-0">
115
122
  {
116
123
  <div
@@ -120,7 +127,7 @@ export function TopNav({ hideToc, hideSearch }: { hideToc?: boolean; hideSearch?
120
127
  })}
121
128
  >
122
129
  <button
123
- className="flex items-center border-stone-400 text-stone-800 hover:text-stone-900 dark:text-stone-200 hover:dark:text-stone-100"
130
+ className="myst-top-nav-menu-button flex items-center border-stone-400 text-stone-800 hover:text-stone-900 dark:text-stone-200 hover:dark:text-stone-100"
124
131
  onClick={() => {
125
132
  setOpen(!open);
126
133
  }}
@@ -31,13 +31,13 @@ export function SkipToArticle({
31
31
  const articleHandler = useCallback(() => makeSkipClickHandler(art), [article]);
32
32
  return (
33
33
  <div
34
- className="fixed top-1 left-1 h-[0px] w-[0px] focus-within:z-40 focus-within:h-auto focus-within:w-auto bg-white overflow-hidden focus-within:p-2 focus-within:ring-1"
34
+ className="myst-skip-to-article fixed top-1 left-1 h-[0px] w-[0px] focus-within:z-40 focus-within:h-auto focus-within:w-auto bg-white overflow-hidden focus-within:p-2 focus-within:ring-1"
35
35
  aria-label="skip to content options"
36
36
  >
37
37
  {frontmatter && (
38
38
  <a
39
39
  href={`#${fm}`}
40
- className="block px-2 py-1 text-black underline"
40
+ className="myst-skip-to-link block px-2 py-1 text-black underline"
41
41
  onClick={frontmatterHandler}
42
42
  >
43
43
  Skip to article frontmatter
@@ -46,7 +46,7 @@ export function SkipToArticle({
46
46
  {article && (
47
47
  <a
48
48
  href={`#${art}`}
49
- className="block px-2 py-1 text-black underline"
49
+ className="myst-skip-to-link block px-2 py-1 text-black underline"
50
50
  onClick={articleHandler}
51
51
  >
52
52
  Skip to article content
@@ -62,14 +62,14 @@ export function SkipToArticle({
62
62
  export const SkipTo = React.memo(({ targets }: { targets: { id: string; title: string }[] }) => {
63
63
  return (
64
64
  <div
65
- className="fixed top-1 left-1 h-[0px] w-[0px] focus-within:z-40 focus-within:h-auto focus-within:w-auto bg-white overflow-hidden focus-within:p-2 focus-within:ring-1"
65
+ className="myst-skip-to-article fixed top-1 left-1 h-[0px] w-[0px] focus-within:z-40 focus-within:h-auto focus-within:w-auto bg-white overflow-hidden focus-within:p-2 focus-within:ring-1"
66
66
  aria-label="skip to content options"
67
67
  >
68
68
  {targets.map(({ id, title }) => (
69
69
  <a
70
70
  key={id}
71
71
  href={`#${id}`}
72
- className="block px-2 py-1 text-black underline"
72
+ className="myst-skip-to-link block px-2 py-1 text-black underline"
73
73
  onClick={makeSkipClickHandler(id)}
74
74
  >
75
75
  {title}
@@ -1,9 +1,9 @@
1
1
  export function Spinner({ size }: { size: number }) {
2
2
  return (
3
- <div role="status">
3
+ <div role="status" className="myst-spinner">
4
4
  <svg
5
5
  aria-hidden="true"
6
- className={`w-[${size}px] h-[${size}px] mr-2 text-gray-200 animate-spin dark:text-gray-600 fill-green-600`}
6
+ className={`myst-spinner-icon w-[${size}px] h-[${size}px] mr-2 text-gray-200 animate-spin dark:text-gray-600 fill-green-600`}
7
7
  viewBox="0 0 100 101"
8
8
  fill="none"
9
9
  xmlns="http://www.w3.org/2000/svg"
@@ -168,11 +168,11 @@ export function AppErrorBoundary() {
168
168
  const error = useRouteError();
169
169
  return (
170
170
  <Document theme={Theme.light}>
171
- <article className="article">
172
- <main className="article-grid subgrid-gap col-screen">
171
+ <main className="article-grid subgrid-gap col-screen">
172
+ <article className="article">
173
173
  {isRouteErrorResponse(error) ? <Error404 /> : <ErrorUnhandled error={error as any} />}
174
- </main>
175
- </article>
174
+ </article>
175
+ </main>
176
176
  </Document>
177
177
  );
178
178
  }