DOC Update website to `pydata-sphinx-theme`

This issue is a continuation of [#26809](https://github.com/scikit-learn/scikit-learn/pull/26809) and aims to migrate the scikit-learn website towards [`pydata-sphinx-theme`](https://pydata-sphinx-theme.readthedocs.io/en/stable/). As this is an ambitious goal, this issue is to track the steps of the migration. cc @lucyleeow who guided me to open this issue.

#### Quick links

- [The `new_web_theme` branch](https://github.com/scikit-learn/scikit-learn/tree/new_web_theme)
- [Tracker for upstream issues](https://github.com/scikit-learn/scikit-learn/issues/28084#issuecomment-1893103722)
- Live preview: https://scikit-learn.org/_pst_preview

#### TODO before merging into `main`

- `doc-min-dependencies` is bypassed in CI for now and we need to reactivate it before merging into `main`. Also, we need to make sure all dependencies are documented, in particular `sphinx-design` which was not added in the very first setup PR. See also [#28379](https://github.com/scikit-learn/scikit-learn/pull/28379).
- Remove the `new_web_theme` part in `.circleci/config.yml`.
- In `conf.py`, change the version switcher link to `https://scikit-learn.org/dev/_static/versions.json`.
- Remove `themes/` and update `exclude_patterns` in `conf.py`: these are only useful for the old theme.
- Pin higher versions of `sphinx`, `pydata-sphinx-theme`, and `sphinx-gallery`. See also [tracker for upstream issues](https://github.com/scikit-learn/scikit-learn/issues/28084#issuecomment-1893103722).

#### Tracking work towards the `new_web_theme` branch

- [x] https://github.com/scikit-learn/scikit-learn/pull/28132 
- [x] https://github.com/scikit-learn/scikit-learn/pull/28331
- [x] https://github.com/scikit-learn/scikit-learn/pull/28336
- [x] https://github.com/scikit-learn/scikit-learn/pull/28347
- [x] https://github.com/scikit-learn/scikit-learn/pull/28353
- [x] https://github.com/scikit-learn/scikit-learn/pull/28379
- [x] https://github.com/scikit-learn/scikit-learn/pull/28401
- [x] https://github.com/scikit-learn/scikit-learn/pull/28409
- [x] https://github.com/scikit-learn/scikit-learn/pull/28408
- [x] https://github.com/scikit-learn/scikit-learn/pull/28415
- [x] https://github.com/scikit-learn/scikit-learn/pull/28428
- [x] https://github.com/scikit-learn/scikit-learn/pull/28448
- [ ] https://github.com/scikit-learn/scikit-learn/pull/28478
- [x] https://github.com/scikit-learn/scikit-learn/pull/28511
- [x] https://github.com/scikit-learn/scikit-learn/pull/28512
- [x] https://github.com/scikit-learn/scikit-learn/pull/28623
- [x] https://github.com/scikit-learn/scikit-learn/pull/28630
- [ ] Fix admonitions (**Todo:** make e.g. `.. note:: something` into `.. admonition:: something` with `:class: note`)

#### Tracking work related by towards the `main` branch

- [x] https://github.com/scikit-learn/scikit-learn/pull/28387
- [x] https://github.com/scikit-learn/scikit-learn/pull/28376
- [x] https://github.com/scikit-learn/scikit-learn/pull/28315
- [x] https://github.com/scikit-learn/scikit-learn/pull/28272
- [x] https://github.com/scikit-learn/scikit-learn/pull/28271
- [x] https://github.com/scikit-learn/scikit-learn/pull/28255
- [x] https://github.com/scikit-learn/scikit-learn/pull/28134
- [x] https://github.com/scikit-learn/scikit-learn/pull/28107

COMMENT:
Here I provide a draft for the first PR (switching the theme)[^1]. It is more like a proof of concept for maintainers to see how complex the first PR might be to review. Please let me know if there are things you want me to clarify at this point.

<details>
<summary><code>git diff</code> statistics</summary>
<p></p>

```
 .gitignore                          |   2 +
 doc/Makefile                        |   1 +
 doc/conf.py                         | 132 ++++++++++++++++++++++++++++++------
 doc/css/.gitkeep                    |   0
 doc/js/scripts/details-permalink.js |  47 +++++++++++++
 doc/make.bat                        |  19 +++++-
 doc/scss/colors.scss                |  18 +++++
 doc/scss/custom.scss                |   2 +
 8 files changed, 198 insertions(+), 23 deletions(-)
```

</details>

<details>
<summary>Changes in <code>.gitignore</code></summary>
<p></p>

With my implementation we write `scss` files for clarity and use the `sphinxcontrib.sass` extension to convert them to `css` files that are directly usable, so we need to ignore contents in the `css` directory. Yet we need to keep that folder to left `sphinx` know that stuff inside needs to be copied by specifying the folder in `conf.py`.

```diff
diff --git a/.gitignore b/.gitignore
index 770f0b84f..7b4f61bea 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,6 +16,8 @@ MANIFEST
 doc/sg_execution_times.rst
 doc/_build/
 doc/auto_examples/
+doc/css/*
+!doc/css/.gitkeep
 doc/modules/generated/
 doc/datasets/generated/
 doc/min_dependency_table.rst
```

</details>

<details>
<summary>Changes in <code>doc/Makefile</code></summary>
<p></p>

Same reason as above.

```diff
diff --git a/doc/Makefile b/doc/Makefile
index 44f02585f..248c0694b 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -50,6 +50,7 @@ clean:
 	-rm -rf auto_examples/
 	-rm -rf generated/*
 	-rm -rf modules/generated/
+	-rm -rf css/styles/
 
 # Default to SPHINX_NUMJOBS=1 for full documentation build. Using
 # SPHINX_NUMJOBS!=1 may actually slow down the build, or cause weird issues in
```

</details>

<details>
<summary>Changes in <code>doc/make.bat</code></summary>
<p></p>

This is to be consistent with `Makefile` (it took me quite an amount of time to find that `make clean` is not really cleaning up all stuff on my Windows machine). Also for the same reason as above I added the cleanup of the `css/styles` directory as well.

```diff
diff --git a/doc/make.bat b/doc/make.bat
index b7e269a6a..1fbcb570e 100644
--- a/doc/make.bat
+++ b/doc/make.bat
@@ -29,8 +29,23 @@ if "%1" == "help" (
 )
 
 if "%1" == "clean" (
-	for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
-	del /q /s %BUILDDIR%\*
+	if exist %BUILDDIR%\ (
+		for /d %%i in (%BUILDDIR%\*) do rmdir /q /s "%%i"
+		del /q /s %BUILDDIR%\*
+	)
+	if exist auto_examples\ (
+		rmdir /q /s auto_examples
+	)
+	if exist generated\ (
+		for /d %%i in (generated\*) do rmdir /q /s "%%i"
+		del /q /s generated\*
+	)
+	if exist modules\generated\ (
+		rmdir /q /s modules\generated
+	)
+	if exist css\styles\ (
+		rmdir /q /s css\styles
+	)
 	goto end
 )
 
```

</details>

<details>
<summary>Changes in <code>doc/conf.py</code></summary>
<p></p>

For this large diff I will split it up and explain each part.

```diff
diff --git a/doc/conf.py b/doc/conf.py
index c5e87442a..b2fbc5e75 100644
--- a/doc/conf.py
+++ b/doc/conf.py
```

This is essentially just reorganizing a bit (moving stuff under `sphinxext/` together), and adding `sphinxcontrib.sass` and `sphinx_remove_toctrees`. The first one (`sphinxcontrib.sass`), as I have mentioned before, is to allow us to write `scss` for cleaner style sheet syntax. It is kind of annoying (at least for me) to repetitively write long selectors and be forced to use comments to separate different parts. `scss` allows flexible nested syntax that provide very clear structures. Yet if maintainers strongly disagree I can stick to pure `css` as well. The second one (`sphinx_remove_toctrees`), is for hiding the metadata routing section in the user guide from the toctree. I'm not sure if there is a better way to do this, but `sphinx_remove_toctree` works fine at least by my observation up to this moment, so I'm keeping it here.

```diff
@@ -55,14 +55,17 @@ extensions = [
     "sphinx.ext.intersphinx",
     "sphinx.ext.imgconverter",
     "sphinx_gallery.gen_gallery",
-    "sphinx_issues",
-    "add_toctree_functions",
     "sphinx-prompt",
     "sphinx_copybutton",
     "sphinxext.opengraph",
-    "doi_role",
-    "allow_nan_estimators",
     "matplotlib.sphinxext.plot_directive",
+    "sphinxcontrib.sass",
+    "sphinx_remove_toctrees",
+    # See sphinxext/
+    "add_toctree_functions",
+    "allow_nan_estimators",
+    "doi_role",
+    "sphinx_issues",
 ]
 
 # Specify how to identify the prompt when copying code snippets
```

Well, you know, I have got a Windows machine and I'm a Chinese, so I'm getting `UnicodeDecodeError: 'gbk' codec can't decode ...` all day long, that's why I'm specifying `source_encoding` just in case.

```diff
@@ -122,7 +125,7 @@ autosummary_generate = True
 source_suffix = ".rst"
 
 # The encoding of source files.
-# source_encoding = 'utf-8'
+source_encoding = "utf-8"
 
 # The main toctree document.
 root_doc = "contents"
```

`pydata-sphinx-theme` has specific configuration options for `pygments` in both light and dark modes, and will override this option, so I'm preferring the theme-specific configuration.

```diff
@@ -176,9 +179,6 @@ add_function_parentheses = False
 # output. They are ignored by default.
 # show_authors = False
 
-# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = "sphinx"
-
 # A list of ignored prefixes for module index sorting.
 # modindex_common_prefix = []
 
```

I think I have written sufficient comments for the following theme-specific configuration part. I've included every option explicitly (except those that are deprecated or will be deprecated), even if we can use default values (because personally I prefer explicit over implicit, and we will not be affected in case `pydata-sphinx-theme` changes some default configurations). Also, due to switching to a non-customized theme, `html_theme_path` should no longer be needed (though I'm not removing contents under `theme/` for now because there maybe something useful to take from, and we can remove it when finalizing the whole theme switching process).

Still maybe I should explain a bit about how I dealt with the configurations that are removed. I replaced `legacy_google_analytics` and `analytics` with the new `analytics` option, which I think is mostly equivalent but has not validated. As for `mathjax_path` I'm really not sure how to deal with it. I think `pydata-sphinx-theme` is not really caring about this? Needs further discussion. `link_to_live_contributing_page` is replaced by `is_devrelease` in `html_context` which you will see later[^2].

```diff
@@ -187,20 +187,95 @@ pygments_style = "sphinx"
 
 # The theme to use for HTML and HTML Help pages.  Major themes that come with
 # Sphinx are currently 'default' and 'sphinxdoc'.
-html_theme = "scikit-learn-modern"
+html_theme = "pydata_sphinx_theme"
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
-# documentation.
+# documentation. For pydata-sphinx-theme, see:
+# https://pydata-sphinx-theme.readthedocs.io/en/stable/user_guide/layout.html#references
 html_theme_options = {
-    "legacy_google_analytics": True,
-    "analytics": True,
-    "mathjax_path": mathjax_path,
-    "link_to_live_contributing_page": not parsed_version.is_devrelease,
+    # -- General configuration ------------------------------------------------
+    "sidebar_includehidden": True,
+    "use_edit_page_button": True,
+    "external_links": [],
+    "icon_links_label": "Icon Links",
+    "icon_links": [
+        {
+            "name": "GitHub",
+            "url": "https://github.com/scikit-learn/scikit-learn",
+            "icon": "fa-brands fa-square-github",
+            "type": "fontawesome",
+        },
+    ],
+    "analytics": {
+        "plausible_analytics_domain": "scikit-learn.org",
+        "plausible_analytics_url": "https://views.scientific-python.org/js/script.js",
+    },
+    # If "prev-next" is included in article_footer_items, then setting show_prev_next
+    # to True would repeat prev and next links, as of pydata-sphinx-theme 0.14.0. See
+    # https://github.com/pydata/pydata-sphinx-theme/blob/b731dc230bc26a3d1d1bb039c56c977a9b3d25d8/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/layout.html#L118-L129
+    "show_prev_next": False,
+    "search_bar_text": "Search the docs ...",
+    "navigation_with_keys": False,
+    "collapse_navigation": False,
+    "navigation_depth": 2,
+    "show_nav_level": 1,
+    "show_toc_level": 1,
+    "navbar_align": "left",
+    "header_links_before_dropdown": 5,
+    "header_dropdown_text": "More",
+    # The switcher requires a JSON file with the list of documentation versions, which
+    # is generated by the script build_tools/circle/list_versions.py. For the switcher
+    # to be displayed, one adds "version-switcher" somewhere in the page layout. See
+    # https://pydata-sphinx-theme.readthedocs.io/en/stable/user_guide/version-dropdown.html
+    "switcher": {
+        # TODO: Change to (maybe) https://scikit-learn.org/dev/_static/versions.json
+        # The current URL is hosted on my personal website, and is for testing purpose.
+        # My ideal logic is here: in CircleCI when running build_doc.sh we generate the
+        # versions.json file under doc/, and this file is added in html_extra_path to be
+        # copied into the build directory, uploaded somewhere under scikit-learn.org,
+        # and that "somewhere" is the "json_url" here.
+        "json_url": (
+            "https://charlie-xiao.github.io/assets/json/sklearn-versions-test.json"
+        ),
+        "version_match": release,
+    },
+    # check_switcher may be set to False if docbuild pipeline fails. See
+    # https://pydata-sphinx-theme.readthedocs.io/en/stable/user_guide/version-dropdown.html#configure-switcher-json-url
+    "check_switcher": True,
+    "pygment_light_style": "a11y-high-contrast-light",
+    "pygment_dark_style": "a11y-high-contrast-dark",
+    "logo": {
+        "alt_text": "scikit-learn homepage",
+        "image_relative": "logos/scikit-learn-logo-small.png",
+        "image_light": "logos/scikit-learn-logo-small.png",
+        "image_dark": "logos/scikit-learn-logo-small.png",
+    },
+    "surface_warnings": True,
+    # -- Template placement in theme layouts ----------------------------------
+    "navbar_start": ["navbar-logo"],
+    # Note that the alignment of navbar_center is controlled by navbar_align
+    "navbar_center": ["navbar-nav"],
+    "navbar_end": ["theme-switcher", "navbar-icon-links", "version-switcher"],
+    # navbar_persistent is persistent right (even when on mobiles)
+    "navbar_persistent": ["search-button"],
+    "article_header_start": ["breadcrumbs"],
+    "article_header_end": [],
+    "article_footer_items": ["prev-next"],
+    "content_footer_items": [],
+    # Use html_sidebars that map page patterns to list of sidebar templates
+    "primary_sidebar_end": [],
+    "footer_start": ["copyright"],
+    "footer_center": [],
+    "footer_end": [],
+    # Use :html_theme.sidebar_secondary.remove: for file-wide removal
+    "secondary_sidebar_items": ["page-toc", "sourcelink"],
+    "show_version_warning_banner": True,
+    "announcement": [],
 }
 
 # Add any paths that contain custom themes here, relative to this directory.
-html_theme_path = ["themes"]
+# html_theme_path = ["themes"]
 
 
 # The name for this set of Sphinx documents.  If None, it defaults to
```

`pydata-sphinx-theme` has provided more detailed configurations for the logo (as above), so `html_logo` is no longer needed.

```diff
@@ -210,10 +285,6 @@ html_theme_path = ["themes"]
 # A shorter title for the navigation bar.  Default is the same as html_title.
 html_short_title = "scikit-learn"
 
-# The name of an image file (relative to this directory) to place at the top
-# of the sidebar.
-html_logo = "logos/scikit-learn-logo-small.png"
-
 # The name of an image file (within the static path) to use as favicon of the
 # docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
 # pixels large.
```

This is just adding two static directories so that `sphinx` knows to copy files under them. `css` is just for style sheets (generated from `scss` files by the `sphinxcontrib.sass`, for which the configuration will be seen below), and `js` is just for JS scripts.

```diff
@@ -222,7 +293,7 @@ html_favicon = "logos/favicon.ico"
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ["images"]
+html_static_path = ["images", "css", "js"]
 
 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
 # using the given strftime format.
```

Introducing additional JS scripts and CSS style sheets, and configuring the `sphinxcontrib.sass` extension. For `html_js_files`, the `details-permalink.js` script was originally under the `theme/` directory so automatically included, but now we need to add it manually. For `html_css_files`, I put `scss` source files under `scss/` and convert them to files with the same name under `css/styles/`. Currently I'm creating `colors.scss` and `custom.scss` because they are global style sheets. I did have more style sheets in my current attempt, e.g., `index.scss` for the landing page, `install.scss` for the OS-dependent installation guide styling, etc., but they are page-specific and will be postponed to further PRs. If needed, this is the [homepage of `sphinxcontrib.scss`](https://github.com/attakei-lab/sphinxcontrib-sass). It does not seem very popular but at least works fine for me.

```diff
@@ -235,6 +306,18 @@ html_static_path = ["images"]
 # template names.
 html_additional_pages = {"index": "index.html"}
 
+# Additional js files
+html_js_files = ["scripts/details-permalink.js"]
+
+# Additional css files, which are compiled from scss files using sphinxcontrib-sass
+html_css_file_names = [
+    "colors",
+    "custom",
+]
+sass_src_dir, sass_out_dir = "scss", "css/styles"
+sass_targets = {f"{name}.scss": f"{name}.css" for name in html_css_file_names}
+html_css_files = [f"styles/{name}.css" for name in html_css_file_names]
+
 # If false, no module index is generated.
 html_domain_indices = False
 
```

`is_devrelease` is the converse of `link_to_live_contributing_page`, which is used to determine whether to link `development` and `contributing` to `dev/` or local[^2]. It was originally in the theme-specific configuration part above.

```diff
@@ -308,12 +391,15 @@ html_context["redirects"] = redirects
 for old_link in redirects:
     html_additional_pages[old_link] = "redirects.html"
 
+# See https://github.com/scikit-learn/scikit-learn/pull/22550
+html_context["is_devrelease"] = parsed_version.is_devrelease
+
 # Not showing the search summary makes the search page load faster.
 html_show_search_summary = True
 
 
 # The "summary-anchor" IDs will be overwritten via JavaScript to be unique.
-# See `doc/theme/scikit-learn-modern/static/js/details-permalink.js`.
+# See `doc/js/scripts/details-permalink.js`.
 rst_prolog = """
 .. |details-start| raw:: html
 
```

This is the configuration for the `sphinx-remove-toctrees` extension. This is intended to hide the metadata routing section from the user guide toctree, and as aforementioned I'm not sure if there is a better way but this one works fine. It is also recommended by `pydata-sphinx-theme`[^3], though not exactly this usecase. If needed this is the [homepage of `sphinx-remove-toctrees`](https://github.com/executablebooks/sphinx-remove-toctrees).

```diff
@@ -799,6 +885,10 @@ linkcheck_ignore = [
     "consistently-create-same-random-numpy-array/5837352#comment6712034_5837352",
 ]
 
+# Config for sphinx-remove-toctrees
+
+remove_from_toctrees = ["metadata_routing.rst"]
+
 # Use a browser-like user agent to avoid some "403 Client Error: Forbidden for
 # url" errors. This is taken from the variable navigator.userAgent inside a
 # browser console.
```

</details>

<details>
<summary>Creating <code>doc/css/.gitkeep</code></summary>
<p></p>

Nothing special but to keep the `css/` directory there because it is specified in `html_static_path`.

```diff
diff --git a/doc/css/.gitkeep b/doc/css/.gitkeep
new file mode 100644
index 000000000..e69de29bb
```

</details>

<details>
<summary>Creating <code>doc/js/scripts/details-permalink.js</code></summary>
<p></p>

Nothing special but copying `details-permalink.js` from under the `theme/` directory to this place. The original `details-permalink.js` file is not deleted because I want to do the cleanup only when finalizing the whole theme switching process.

```diff
diff --git a/doc/js/scripts/details-permalink.js b/doc/js/scripts/details-permalink.js
new file mode 100644
index 000000000..62392e983
--- /dev/null
+++ b/doc/js/scripts/details-permalink.js
@@ -0,0 +1,47 @@
+// Function to create permalink into <details> elements to be able to link them
+// The assumption is that such a block will be defined as follows:
+//     <details id="summary-anchor">
+//     <summary class="btn btn-light">
+//     Some title
+//     <span class="tooltiptext">Click for more details</span>
+//     <a class="headerlink" href="#summary-anchor" title="Permalink to this heading">露</a>
+//     </summary>
+//     <div class="card">
+//     Some details
+//     </div>
+//     </details>
+// We seek to replace `#summary-anchor` with a unique identifier based on the
+// summary text.
+// This syntax is defined in `doc/conf.py` in the `rst_prolog` variable.
+function updateIdAndHrefBasedOnSummaryText() {
+    var allDetailsElements = document.querySelectorAll('details');
+    // Counter to store the duplicated summary text to add it as a suffix in the
+    // anchor ID
+    var anchorIDCounters = {};
+
+    allDetailsElements.forEach(function (detailsElement) {
+        // Get the <summary> element within the current <details>
+        var summaryElement = detailsElement.querySelector('summary');
+
+        // The ID uses the first line, lowercased, and spaces replaced with dashes
+        var anchorID = summaryElement.textContent.trim().split("\n")[0].replace(/\s+/g, '-').toLowerCase();
+
+        // Suffix the anchor ID with a counter if it already exists
+        if (anchorIDCounters[anchorID]) {
+            anchorIDCounters[anchorID] += 1;
+            anchorID = anchorID + '-' + anchorIDCounters[anchorID];
+        } else {
+            anchorIDCounters[anchorID] = 1;
+        }
+
+        detailsElement.setAttribute('id', anchorID);
+
+        var anchorElement = summaryElement.querySelector('a.headerlink');
+        anchorElement.setAttribute('href', '#' + anchorID);
+    });
+}
+
+// Add an event listener to execute the function when the page is loaded
+document.addEventListener('DOMContentLoaded', function () {
+    updateIdAndHrefBasedOnSummaryText();
+});
```
</details>

<details>
<summary>Creating <code>doc/scss/colors.scss</code></summary>
<p></p>

Defining some colors. This is a global style sheet so I'm putting it here. These colors are the ones I have used up to this moment, subject to change. Some are vendored from the original style sheets, others are created with my meager artistic skills.

```diff
diff --git a/doc/scss/colors.scss b/doc/scss/colors.scss
new file mode 100644
index 000000000..d496fce6b
--- /dev/null
+++ b/doc/scss/colors.scss
@@ -0,0 +1,18 @@
+:root {
+  --sk-cyan-darker: #2a6280;
+  --sk-cyan-dark: #30799c;
+  --sk-cyan: #3499cd;
+
+  --sk-orange-darkest: #451b00;
+  --sk-orange-dark: #e07200;
+  --sk-orange: #ff9c34;
+  --sk-orange-light: #ffb260;
+  --sk-orange-lighter: #fff3cd;
+  --sk-orange-lightest: #fff3d3;
+
+  --sk-text-dark: #222832;
+  --sk-text-light: #ced6dd;
+
+  --sk-background-dim-light: #f8f8f8;
+  --sk-background-dim-dark: #222222;
+}
```

</details>

<details>
<summary>Creating <code>doc/scss/custom.scss</code></summary>
<p></p>

As described in the comment, this is a global style sheet so created in advance.

```diff
diff --git a/doc/scss/custom.scss b/doc/scss/custom.scss
new file mode 100644
index 000000000..2eef3a1b1
--- /dev/null
+++ b/doc/scss/custom.scss
@@ -0,0 +1,2 @@
+/** This is a general styling sheet.
+  * It is mainly used to override the default styling of pydata-sphinx-theme. */
```

</details>

[^1]: The reason why I demonstrated it in this way instead of making an actual PR is that (1) the `new_web_theme` branch is kind of outdated and I assume that maintainers will sync it before starting this ambitious theme switching journey, and (2) I claimed that I will wait for more maintainers' opinions before starting the first PR.

[^2]: [#22550](https://github.com/scikit-learn/scikit-learn/pull/22550)

[^3]: [PyData Theme - Build Performance and Size - Selectively Remove Pages From Your Sidebar](https://pydata-sphinx-theme.readthedocs.io/en/stable/user_guide/performance.html#selectively-remove-pages-from-your-sidebar)

Labels: Documentation