diff --git a/docs/Makefile b/docs/Makefile
index 9cd3086b6..abf9cc069 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -9,7 +9,13 @@ SPHINXPROJ = UltraPlot
SOURCEDIR = .
BUILDDIR = _build
-.PHONY: help clean Makefile
+.PHONY: help clean html html-exec Makefile
+
+html:
+ @UPLT_DOCS_EXECUTE=$${UPLT_DOCS_EXECUTE:-always} $(SPHINXBUILD) -b html "$(SOURCEDIR)" "$(BUILDDIR)/html" -E -a $(SPHINXOPTS)
+
+html-exec:
+ @UPLT_DOCS_EXECUTE=always $(SPHINXBUILD) -b html "$(SOURCEDIR)" "$(BUILDDIR)/html" -E -a $(SPHINXOPTS)
# Put it first so that "make" without argument is like "make help".
help:
diff --git a/docs/_static/custom.css b/docs/_static/custom.css
index 145657869..000c2a324 100644
--- a/docs/_static/custom.css
+++ b/docs/_static/custom.css
@@ -1,8 +1,310 @@
+:root {
+ /* Core surfaces */
+ --uplt-color-panel-bg: #ffffff; /* page bg (light) */
+ --uplt-color-sidebar-bg: #f4f4f4; /* TOC + notebook cell bg (light) */
+ --uplt-color-card-bg: #f4f4f4; /* used by .card-img-top background */
+ --uplt-color-white: #ffffff;
+
+ /* Borders & shadows */
+ --uplt-color-border-muted: #e1e4e5;
+ --uplt-color-button-border: #c5c5c5;
+ --uplt-color-shadow: rgba(0, 0, 0, 0.1);
+
+ /* Text */
+ --uplt-color-text-main: #404040;
+ --uplt-color-text-strong: #333333;
+ --uplt-color-text-secondary: #555555;
+ --uplt-color-text-muted: #606060;
+
+ /* Accent */
+ --uplt-color-accent: #0f766e;
+ --uplt-color-accent-hover: rgba(15, 118, 110, 0.1);
+ --uplt-color-accent-active: rgba(15, 118, 110, 0.15);
+ --uplt-color-accent-grad-start: rgba(15, 118, 110, 0.1);
+ --uplt-color-accent-grad-end: rgba(15, 118, 110, 0.02);
+ --uplt-color-accent-shadow-strong: rgba(15, 118, 110, 0.2);
+ --uplt-color-accent-shadow-soft: rgba(15, 118, 110, 0.1);
+
+ /* Scrollbar */
+ --uplt-color-scrollbar-track: #f1f1f1;
+ --uplt-color-scrollbar-thumb: #cdcdcd;
+ --uplt-color-scrollbar-thumb-hover: #9e9e9e;
+
+ --uplt-color-code-bg: var(--uplt-color-sidebar-bg); /* same as page */
+ --uplt-color-code-fg: #6a6a6a; /* gray code text (light) */
+ --code-block-background: var(--uplt-color-code-bg);
+ --sy-c-link: var(--uplt-color-accent);
+ --sy-c-link-hover: #0b5f59;
+ --uplt-color-toc-bg: #e9e9e9;
+}
+
+.sy-main .yue a,
+.globaltoc a,
+.localtoc a,
+.sy-breadcrumbs a {
+ color: var(--sy-c-link);
+}
+
+.sy-main .yue a:hover,
+.globaltoc a:hover,
+.localtoc a:hover,
+.sy-breadcrumbs a:hover {
+ color: var(--sy-c-link-hover);
+}
+
+.sy-main .yue a:not(.headerlink) {
+ border-bottom-color: transparent !important;
+ text-decoration: none !important;
+}
+
+.sy-main .yue a:not(.headerlink):hover {
+ border-bottom-color: transparent !important;
+ text-decoration: underline !important;
+ text-decoration-color: var(--sy-c-link-hover);
+ text-decoration-thickness: 0.08em;
+ text-underline-offset: 0.14em;
+}
+
+.sy-head .sy-head-links {
+ justify-content: flex-start !important;
+ column-gap: 1.8rem !important;
+ padding-left: 1.25rem !important;
+ padding-right: 1.25rem !important;
+}
+
+.sy-head .sy-head-brand {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.2rem 0.04rem 0.2rem;
+ line-height: 1.25;
+}
+
+.sy-head .sy-head-brand strong {
+ font-size: 0.74rem;
+ font-weight: 700;
+ letter-spacing: 0.065em;
+ text-transform: uppercase;
+ line-height: 1.25;
+}
+
+@media (min-width: 768px) {
+ .sy-head .sy-head-links > ul {
+ display: flex !important;
+ align-items: center;
+ justify-content: flex-start;
+ column-gap: 2.8rem !important;
+ margin: 0 !important;
+ padding: 0 !important;
+ text-align: left;
+ }
+
+ .sy-head .sy-head-links > ul > li.link {
+ margin: 0 !important;
+ padding: 0 !important;
+ }
+}
+
+.sy-head .sy-head-links a {
+ border: 0 !important;
+ border-bottom: 2px solid transparent !important;
+ border-radius: 0;
+ padding: 0.2rem 0.04rem 0.2rem;
+ line-height: 1.25;
+ font-size: 0.74rem;
+ font-weight: 600;
+ letter-spacing: 0.065em;
+ text-transform: uppercase;
+ color: var(--uplt-color-text-main);
+ background: transparent !important;
+ text-decoration: none !important;
+ transition:
+ border-bottom-color 0.2s ease,
+ color 0.2s ease,
+ opacity 0.2s ease;
+}
+
+.sy-head .sy-head-links a:hover {
+ border-bottom-color: rgba(15, 118, 110, 0.35) !important;
+ color: var(--uplt-color-accent);
+ opacity: 1;
+}
+
+.sy-head .sy-head-links a[href="#"],
+.sy-head .sy-head-links a[aria-current="page"] {
+ color: var(--uplt-color-accent) !important;
+ border-bottom-color: var(--uplt-color-accent) !important;
+ opacity: 1;
+}
+
+@media (min-width: 768px) {
+ .sy-head,
+ .sy-breadcrumbs,
+ .sy-lside {
+ transition:
+ opacity 0.24s ease,
+ transform 0.24s ease;
+ will-change: opacity, transform;
+ }
+
+ html.uplt-chrome-hidden .sy-head,
+ html.uplt-chrome-hidden .sy-breadcrumbs {
+ opacity: 0;
+ transform: translateY(-14px);
+ pointer-events: none;
+ }
+
+ html.uplt-chrome-hidden .sy-lside {
+ opacity: 0;
+ transform: translateX(-14px);
+ pointer-events: none;
+ }
+}
+
+/* Content heading hierarchy */
+.sy-main .yue h1 {
+ font-size: clamp(2rem, 2.6vw, 2.5rem);
+ line-height: 1.12;
+ font-weight: 740;
+ letter-spacing: -0.018em;
+ margin: 0 0 1.1rem;
+ padding-bottom: 0.38rem;
+ display: grid;
+ grid-template-columns: auto 1fr;
+ align-items: center;
+ column-gap: 0.7rem;
+ row-gap: 0.25rem;
+ color: var(--sy-c-heading);
+}
+
+.sy-main .yue h1::before {
+ content: "";
+ grid-row: 1;
+ grid-column: 1;
+ width: 0.5rem;
+ height: 1.05em;
+ border-radius: 999px;
+ background: linear-gradient(180deg, var(--uplt-color-accent) 0%, #0a5f58 100%);
+ box-shadow: 0 0 0 1px var(--uplt-color-accent-shadow-soft);
+}
+
+.sy-main .yue h1::after {
+ content: "";
+ display: block;
+ grid-row: 2;
+ grid-column: 2;
+ width: clamp(2.8rem, 8vw, 4.2rem);
+ height: 0.2rem;
+ border-radius: 999px;
+ background: linear-gradient(90deg, var(--uplt-color-accent) 0%, #0a5f58 100%);
+}
+
+.sy-main .yue h2 {
+ font-size: clamp(1.35rem, 1.8vw, 1.65rem);
+ line-height: 1.25;
+ font-weight: 650;
+ margin: 2.2rem 0 0.8rem;
+ padding-bottom: 0.35rem;
+ border-bottom: 1px solid var(--sy-c-divider);
+ box-shadow: inset 0 -2px 0 0 var(--uplt-color-accent-hover);
+ color: var(--sy-c-heading);
+}
+
+.sy-main .yue h3 {
+ font-size: 1.08rem;
+ line-height: 1.3;
+ font-weight: 620;
+ margin: 1.45rem 0 0.5rem;
+ padding-left: 0.55rem;
+ border-left: 3px solid var(--uplt-color-accent);
+ color: var(--sy-c-heading);
+}
+
+.sy-main .yue h4,
+.sy-main .yue h5,
+.sy-main .yue h6 {
+ font-size: 0.98rem;
+ font-weight: 600;
+ margin: 1.1rem 0 0.35rem;
+ color: var(--sy-c-text);
+}
+
+html.dark .sy-head .sy-head-links a,
+html.dark-theme .sy-head .sy-head-links a,
+[data-color-mode="dark"] .sy-head .sy-head-links a {
+ color: #dbe6e5;
+ opacity: 0.96;
+}
+
+html.dark .sy-head .sy-head-links a:hover,
+html.dark-theme .sy-head .sy-head-links a:hover,
+[data-color-mode="dark"] .sy-head .sy-head-links a:hover {
+ color: #66d0c6;
+ border-bottom-color: rgba(102, 208, 198, 0.55) !important;
+}
+
+html.dark .sy-head .sy-head-links a[href="#"],
+html.dark-theme .sy-head .sy-head-links a[href="#"],
+[data-color-mode="dark"] .sy-head .sy-head-links a[href="#"],
+html.dark .sy-head .sy-head-links a[aria-current="page"],
+html.dark-theme .sy-head .sy-head-links a[aria-current="page"],
+[data-color-mode="dark"] .sy-head .sy-head-links a[aria-current="page"] {
+ color: #8be0d9 !important;
+ border-bottom-color: #8be0d9 !important;
+}
+
+@media screen and (max-width: 1200px) {
+ .sy-head .sy-head-links {
+ column-gap: 3.8rem !important;
+ padding-left: 1rem !important;
+ padding-right: 1rem !important;
+ }
+}
+
+.yue :not(pre) > code,
+.yue code.docutils.literal.notranslate,
+.yue code.docutils.literal.notranslate .pre {
+ color: var(--sy-c-link);
+ background-color: var(--uplt-color-accent-hover);
+ border: 1px solid var(--uplt-color-border-muted);
+ border-radius: 0.2rem;
+ padding: 0.06rem 0.28rem;
+}
+
+html.dark,
+html.dark-theme,
+[data-color-mode="dark"] {
+ --uplt-color-accent: #1aa89a;
+ --uplt-color-accent-hover: rgba(26, 168, 154, 0.14);
+ --uplt-color-accent-active: rgba(26, 168, 154, 0.22);
+ --uplt-color-accent-grad-start: rgba(26, 168, 154, 0.16);
+ --uplt-color-accent-grad-end: rgba(26, 168, 154, 0.04);
+ --uplt-color-accent-shadow-strong: rgba(26, 168, 154, 0.26);
+ --uplt-color-accent-shadow-soft: rgba(26, 168, 154, 0.14);
+ --sy-c-link: #58d5c9;
+ --sy-c-link-hover: #84e8df;
+ --uplt-color-panel-bg: #202020;
+ --code-block-background: #141414;
+ --syntax-dark-background: #141414;
+ --syntax-dark-highlight: #2a2f2f;
+ --uplt-color-toc-bg: #171717;
+}
+
+@media (prefers-color-scheme: dark) {
+ html:not(.light):not(.light-theme):not([data-color-mode="light"]) {
+ --uplt-color-panel-bg: #202020;
+ --code-block-background: #141414;
+ --syntax-dark-background: #141414;
+ --syntax-dark-highlight: #2a2f2f;
+ --uplt-color-toc-bg: #171717;
+ }
+}
+
.grid-item-card .card-img-top {
height: 100%;
object-fit: cover;
width: 100%;
- background-color: slategrey;
+ background-color: var(--uplt-color-card-bg);
}
/* Make all cards with this class use flexbox for vertical layout */
@@ -10,6 +312,24 @@
display: flex !important;
flex-direction: column !important;
height: 100% !important;
+ border: 1px solid var(--uplt-color-border-muted) !important;
+ border-radius: 0.8rem !important;
+ background: linear-gradient(
+ 180deg,
+ var(--uplt-color-white) 0%,
+ var(--uplt-color-sidebar-bg) 100%
+ ) !important;
+ box-shadow: 0 4px 14px var(--uplt-color-shadow);
+ transition:
+ transform 0.18s ease,
+ box-shadow 0.18s ease,
+ border-color 0.18s ease;
+}
+
+.card-with-bottom-text:hover {
+ transform: translateY(-2px);
+ border-color: var(--uplt-color-accent) !important;
+ box-shadow: 0 10px 22px var(--uplt-color-accent-shadow-soft);
}
/* Style the card content areas */
@@ -17,12 +337,41 @@
display: flex !important;
flex-direction: column !important;
flex-grow: 1 !important;
+ gap: 0.25rem;
+ padding: 0.85rem 1rem 1rem !important;
+}
+
+.card-with-bottom-text .sd-card-header {
+ background: linear-gradient(
+ 135deg,
+ var(--uplt-color-accent) 0%,
+ #0a5f58 100%
+ ) !important;
+ color: #ffffff !important;
+ border-bottom: 0 !important;
+ border-top-left-radius: 0.8rem !important;
+ border-top-right-radius: 0.8rem !important;
+ padding: 0.72rem 1rem !important;
+}
+
+.card-with-bottom-text .sd-card-header .sd-card-text,
+.card-with-bottom-text .sd-card-header strong {
+ color: #ffffff !important;
+}
+
+.card-with-bottom-text .sd-card-title {
+ margin-bottom: 0.35rem;
+ font-weight: 650;
+ letter-spacing: -0.01em;
}
/* Make images not grow or shrink */
.card-with-bottom-text img {
flex-shrink: 0 !important;
margin-bottom: 0.5rem !important;
+ border-radius: 0.45rem;
+ border: 1px solid var(--uplt-color-border-muted);
+ background: var(--uplt-color-card-bg);
}
/* Push the last paragraph to the bottom */
@@ -32,6 +381,19 @@
text-align: center !important;
}
+html.dark .card-with-bottom-text,
+html.dark-theme .card-with-bottom-text,
+[data-color-mode="dark"] .card-with-bottom-text {
+ background: linear-gradient(180deg, #252525 0%, #1f1f1f 100%) !important;
+ box-shadow: 0 7px 20px rgba(0, 0, 0, 0.3);
+}
+
+html.dark .card-with-bottom-text .sd-card-header,
+html.dark-theme .card-with-bottom-text .sd-card-header,
+[data-color-mode="dark"] .card-with-bottom-text .sd-card-header {
+ background: linear-gradient(135deg, #178f84 0%, #0f6f67 100%) !important;
+}
+
.img-container img {
object-fit: cover;
width: 100%;
@@ -43,13 +405,13 @@
justify-content: space-between;
align-items: center;
padding: 12px 15px;
- border-bottom: 1px solid #e1e4e5;
+ border-bottom: 1px solid var(--uplt-color-border-muted);
}
.right-toc-title {
font-weight: 600;
font-size: 1.1em;
- color: #2980b9;
+ color: var(--uplt-color-accent);
}
.right-toc-buttons {
@@ -60,7 +422,7 @@
.right-toc-toggle-btn {
background: none;
border: none;
- color: #2980b9;
+ color: var(--uplt-color-accent);
font-size: 16px;
cursor: pointer;
width: 24px;
@@ -74,7 +436,7 @@
}
.right-toc-toggle-btn:hover {
- background-color: rgba(41, 128, 185, 0.1);
+ background-color: var(--uplt-color-accent-hover);
}
.right-toc-content {
@@ -93,16 +455,16 @@
display: block;
padding: 5px 0;
text-decoration: none;
- color: #404040;
+ color: var(--uplt-color-text-main);
border-radius: 4px;
transition: all 0.2s ease;
margin-bottom: 3px;
}
.right-toc-link:hover {
- background-color: rgba(41, 128, 185, 0.1);
+ background-color: var(--uplt-color-accent-hover);
padding-left: 5px;
- color: #2980b9;
+ color: var(--uplt-color-accent);
}
.right-toc-level-h1 {
@@ -118,13 +480,13 @@
.right-toc-level-h3 {
padding-left: 2.4em;
font-size: 0.9em;
- color: #606060;
+ color: var(--uplt-color-text-muted);
}
.right-toc-subtoggle {
background: none;
border: none;
- color: #2980b9;
+ color: var(--uplt-color-accent);
cursor: pointer;
font-size: 0.9em;
margin-right: 0.3em;
@@ -139,8 +501,8 @@
/* Active TOC item highlighting */
.right-toc-link.active {
- background-color: rgba(41, 128, 185, 0.15);
- color: #2980b9;
+ background-color: var(--uplt-color-accent-active);
+ color: var(--uplt-color-accent);
font-weight: 500;
padding-left: 5px;
}
@@ -162,17 +524,17 @@
}
.right-toc-content::-webkit-scrollbar-track {
- background: #f1f1f1;
+ background: var(--uplt-color-scrollbar-track);
border-radius: 10px;
}
.right-toc-content::-webkit-scrollbar-thumb {
- background: #cdcdcd;
+ background: var(--uplt-color-scrollbar-thumb);
border-radius: 10px;
}
.right-toc-content::-webkit-scrollbar-thumb:hover {
- background: #9e9e9e;
+ background: var(--uplt-color-scrollbar-thumb-hover);
}
.toc-wrapper {
@@ -192,11 +554,11 @@
width: 280px;
left: 1125px;
font-size: 0.9em;
- background-color: #f8f9fa;
+ background-color: var(--uplt-color-panel-bg);
z-index: 100;
border-radius: 6px;
- box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
- border-left: 3px solid #2980b9;
+ box-shadow: 0 4px 10px var(--uplt-color-shadow);
+ border-left: 3px solid var(--uplt-color-accent);
transition: all 0.3s ease;
max-height: calc(100vh - 150px);
}
@@ -207,12 +569,12 @@
border-radius: 16px;
background: linear-gradient(
135deg,
- rgba(41, 128, 185, 0.08),
- rgba(41, 128, 185, 0.02)
+ var(--uplt-color-accent-grad-start),
+ var(--uplt-color-accent-grad-end)
);
box-shadow:
- 0 10px 24px rgba(41, 128, 185, 0.18),
- 0 2px 6px rgba(41, 128, 185, 0.08);
+ 0 10px 24px var(--uplt-color-accent-shadow-strong),
+ 0 2px 6px var(--uplt-color-accent-shadow-soft);
}
.gallery-filter-bar {
@@ -223,9 +585,9 @@
}
.gallery-filter-button {
- border: 1px solid #c5c5c5;
- background-color: #ffffff;
- color: #333333;
+ border: 1px solid var(--uplt-color-button-border);
+ background-color: var(--uplt-color-white);
+ color: var(--uplt-color-text-strong);
padding: 0.35rem 0.85rem;
border-radius: 999px;
font-size: 0.9em;
@@ -237,9 +599,9 @@
}
.gallery-filter-button.is-active {
- background-color: #2980b9;
- border-color: #2980b9;
- color: #ffffff;
+ background-color: var(--uplt-color-accent);
+ border-color: var(--uplt-color-accent);
+ color: var(--uplt-color-white);
}
.gallery-section-hidden {
@@ -332,14 +694,58 @@ body.wy-body-for-nav
font-weight: bold;
display: block;
margin: 1.5em 0 0.5em 0;
- border-bottom: 2px solid #2980b9;
+ border-bottom: 2px solid var(--uplt-color-accent);
padding-bottom: 0.3em;
- color: #2980b9;
+ color: var(--uplt-color-accent);
}
.gallery-section-description {
margin: 0 0 1em 0;
- color: #555;
+ color: var(--uplt-color-text-secondary);
+}
+
+/* Gallery example pages: collapsible source code */
+.yue details.uplt-code-details {
+ margin-top: 0.9rem;
+ border: 1px solid var(--uplt-color-border-muted);
+ border-radius: 0.35rem;
+ background: var(--uplt-color-panel-bg);
+}
+
+.yue details.uplt-code-details > summary.uplt-code-summary {
+ list-style: none;
+ cursor: pointer;
+ user-select: none;
+ padding: 0.45rem 0.7rem;
+ font-size: 0.8rem;
+ font-weight: 600;
+ letter-spacing: 0.03em;
+ color: var(--sy-c-link);
+}
+
+.yue details.uplt-code-details > summary.uplt-code-summary::-webkit-details-marker {
+ display: none;
+}
+
+.yue details.uplt-code-details[open] > summary.uplt-code-summary {
+ border-bottom: 1px solid var(--uplt-color-border-muted);
+}
+
+.yue details.uplt-code-details > .highlight-Python {
+ margin: 0;
+ border: 0;
+ border-radius: 0 0 0.35rem 0.35rem;
+}
+
+.yue details.uplt-code-details > .nbinput.docutils.container {
+ margin: 0;
+ border: 0;
+ border-radius: 0 0 0.35rem 0.35rem;
+}
+
+.yue details.uplt-code-details > .nbinput.docutils.container div.input_area {
+ border-radius: 0 0 0.35rem 0.35rem;
+ border-top: 0;
}
/* Responsive adjustments */
@@ -365,3 +771,288 @@ body.wy-body-for-nav
height: auto;
display: block;
}
+
+/* Shibuya: unify sidebar and notebook cell backgrounds */
+.sy-lside,
+.sy-lside-inner,
+.sy-rside,
+.sy-rside-inner,
+.sy-scrollbar {
+ background-color: var(--uplt-color-toc-bg);
+}
+
+.yue div.nbinput.container > div.input_area,
+.yue div.nboutput.container > div.output_area,
+.yue .highlight,
+.yue .highlight pre {
+ background-color: var(--code-block-background) !important;
+}
+
+/* Shibuya right TOC: collapse sub-H1 headings under each H1 section */
+.sy-rside .localtoc {
+ margin-left: 0.55rem;
+ border: 1px solid var(--uplt-color-border-muted);
+ border-radius: 0.5rem;
+ padding: 0.7rem 0.75rem;
+ background: var(--uplt-color-panel-bg);
+ box-shadow: 0 1px 6px var(--uplt-color-shadow);
+}
+
+.sy-rside .sy-rside-inner > div:empty {
+ display: none;
+}
+
+.sy-rside .localtoc > h3 {
+ color: var(--sy-c-light);
+ font-family: var(--sy-f-heading);
+ font-size: 0.86rem;
+ font-weight: 500;
+ letter-spacing: 0.4px;
+ text-transform: uppercase;
+ margin: 0 0 0.5rem 0;
+ padding: 0 0 0.45rem 0;
+ border-bottom: 1px solid var(--sy-c-divider);
+}
+
+.sy-rside .localtoc > .uplt-toc-head {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 0.45rem;
+ margin: 0 0 0.5rem 0;
+ padding: 0 0 0.45rem 0;
+ border-bottom: 1px solid var(--sy-c-divider);
+}
+
+.sy-rside .localtoc > .uplt-toc-head > h3 {
+ color: var(--sy-c-light);
+ font-family: var(--sy-f-heading);
+ font-size: 0.86rem;
+ font-weight: 500;
+ letter-spacing: 0.4px;
+ text-transform: uppercase;
+ margin: 0;
+ padding: 0;
+ border: 0;
+}
+
+.sy-rside .localtoc > ul li > a {
+ display: block;
+ padding: 0.08rem 0.2rem 0.08rem 0.45rem;
+ border-radius: 0.2rem;
+}
+
+.sy-rside .localtoc > ul > li.uplt-toc-collapsible {
+ position: relative;
+ padding-left: 1.2rem;
+}
+
+.sy-rside .localtoc > .uplt-toc-controls {
+ display: flex;
+ gap: 0.35rem;
+ margin: 0 0 0.75rem 0;
+}
+
+.sy-rside .localtoc > .uplt-code-controls {
+ display: grid;
+ grid-template-columns: 1fr;
+ row-gap: 0.35rem;
+ margin: 0.85rem 0 0 0;
+ padding-top: 0.55rem;
+ border-top: 1px solid var(--sy-c-divider);
+}
+
+.sy-rside .localtoc > .uplt-code-controls .uplt-code-btn {
+ width: 100%;
+ justify-self: stretch;
+ text-align: left;
+}
+
+.sy-rside .localtoc .uplt-toc-btn {
+ border: 1px solid var(--sy-c-border);
+ background: var(--uplt-color-panel-bg);
+ color: var(--sy-c-text);
+ border-radius: 6px;
+ padding: 0.16rem 0.5rem;
+ font-size: 0.73rem;
+ font-weight: 600;
+ letter-spacing: 0.01em;
+ line-height: 1.25;
+ cursor: pointer;
+ box-shadow: 0 1px 2px var(--uplt-color-shadow);
+ transition:
+ border-color 0.2s ease,
+ color 0.2s ease,
+ background-color 0.2s ease,
+ box-shadow 0.2s ease;
+}
+
+.sy-rside .localtoc > .uplt-toc-head .uplt-toc-btn-hide {
+ padding: 0.14rem 0.42rem;
+}
+
+.sy-rside .localtoc .uplt-toc-btn:hover {
+ border-color: var(--sy-c-link);
+ color: var(--sy-c-link);
+ background: var(--sy-c-surface);
+ box-shadow: 0 1px 3px var(--uplt-color-shadow);
+}
+
+.sy-rside .localtoc .uplt-toc-btn:focus-visible {
+ outline: 2px solid var(--sy-c-link);
+ outline-offset: 1px;
+}
+
+.uplt-rside-show {
+ position: fixed;
+ right: 1rem;
+ top: 5.5rem;
+ z-index: 50;
+ border: 1px solid var(--sy-c-border);
+ background: var(--uplt-color-panel-bg);
+ color: var(--sy-c-text);
+ border-radius: 6px;
+ padding: 0.24rem 0.68rem;
+ font-size: 0.73rem;
+ font-weight: 600;
+ cursor: pointer;
+ display: none;
+ box-shadow: 0 2px 10px var(--uplt-color-shadow);
+}
+
+.uplt-rside-hidden .uplt-rside-show {
+ display: inline-flex;
+ align-items: center;
+}
+
+.uplt-rside-hidden .sy-rside {
+ display: none;
+}
+
+.uplt-rside-hidden .rside-overlay {
+ display: none;
+}
+
+.sy-rside .localtoc > ul > li > button.uplt-toc-toggle {
+ position: absolute;
+ left: -0.1rem;
+ top: 0.12rem;
+ width: 1.2rem;
+ height: 1.2rem;
+ border-radius: 3px;
+ border: none;
+ background: transparent;
+ color: var(--sy-c-light);
+ cursor: pointer;
+ font-size: 0;
+ line-height: 1;
+ padding: 0;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ transition: background-color 0.2s ease;
+}
+
+.sy-rside .localtoc > ul > li > button.uplt-toc-toggle::before {
+ content: "▸";
+ font-size: 2.02rem;
+ transform: rotate(0deg);
+ transition: transform 0.2s ease;
+}
+
+.sy-rside
+ .localtoc
+ > ul
+ > li
+ > button.uplt-toc-toggle[aria-expanded="true"]::before {
+ transform: rotate(90deg);
+}
+
+.sy-rside .localtoc > ul > li > button.uplt-toc-toggle:hover {
+ color: var(--sy-c-link);
+ background: var(--sy-c-surface);
+}
+
+.globaltoc > ul a.current,
+.localtoc > ul li.active > a {
+ color: var(--sy-c-link) !important;
+}
+
+.globaltoc > ul a:hover,
+.localtoc > ul li > a:hover {
+ color: var(--sy-c-link-hover) !important;
+}
+
+/* Left TOC: subtle colored section markers */
+.globaltoc li.toctree-l1 {
+ border-left: 3px solid var(--uplt-color-border-muted);
+ padding-left: 0.45rem;
+ border-radius: 0.2rem;
+}
+
+.globaltoc li.toctree-l1:nth-of-type(6n + 1) {
+ border-left-color: #7fb3ad;
+}
+
+.globaltoc li.toctree-l1:nth-of-type(6n + 2) {
+ border-left-color: #8fb6cc;
+}
+
+.globaltoc li.toctree-l1:nth-of-type(6n + 3) {
+ border-left-color: #b4b6d8;
+}
+
+.globaltoc li.toctree-l1:nth-of-type(6n + 4) {
+ border-left-color: #c0b7ce;
+}
+
+.globaltoc li.toctree-l1:nth-of-type(6n + 5) {
+ border-left-color: #b7c8a7;
+}
+
+.globaltoc li.toctree-l1:nth-of-type(6n + 6) {
+ border-left-color: #d2b8a4;
+}
+
+/* API pages: increase visual separation for summary and details blocks */
+.sy-main .yue [id^="api-"] p.rubric {
+ margin-top: 1.25rem;
+ margin-bottom: 0.45rem;
+ padding: 0.35rem 0.6rem;
+ border-left: 3px solid var(--uplt-color-accent);
+ background: linear-gradient(
+ 90deg,
+ var(--uplt-color-accent-grad-start),
+ var(--uplt-color-accent-grad-end)
+ );
+ border-radius: 0.2rem;
+}
+
+.sy-main .yue [id^="api-"] dl.py {
+ margin-top: 0.85rem;
+ padding: 0.6rem 0.8rem;
+ border: 1px solid var(--uplt-color-border-muted);
+ border-radius: 0.35rem;
+ background: var(--uplt-color-panel-bg);
+}
+
+.sy-main .yue [id^="api-"] dl.py.attribute,
+.sy-main .yue [id^="api-"] dl.py.data {
+ border-left: 3px solid #2f7a4a;
+}
+
+.sy-main .yue [id^="api-"] dl.py.method,
+.sy-main .yue [id^="api-"] dl.py.function {
+ border-left: 3px solid #1f6d9c;
+}
+
+.sy-main .yue [id^="api-"] dl.py.class,
+.sy-main .yue [id^="api-"] dl.py.exception {
+ border-left: 3px solid #7a4a1f;
+}
+
+.sy-main .yue [id^="api-"] dl.py dt {
+ padding: 0.25rem 0.35rem;
+ border-radius: 0.2rem;
+ background: var(--uplt-color-sidebar-bg);
+}
diff --git a/docs/_static/custom.js b/docs/_static/custom.js
index bca643396..4bd28d2d2 100644
--- a/docs/_static/custom.js
+++ b/docs/_static/custom.js
@@ -1,4 +1,345 @@
+function getDirectChildByTag(el, tagName) {
+ return (
+ Array.from(el.children).find((child) => child.tagName === tagName) || null
+ );
+}
+
+function getDirectToggleButton(item) {
+ return (
+ Array.from(item.children).find(
+ (child) =>
+ child.tagName === "BUTTON" &&
+ child.classList.contains("uplt-toc-toggle"),
+ ) || null
+ );
+}
+
+function setTocItemExpanded(item, expanded) {
+ const childList = getDirectChildByTag(item, "UL");
+ const toggle = getDirectToggleButton(item);
+ if (!childList || !toggle) return;
+ childList.hidden = !expanded;
+ childList.style.display = expanded ? "" : "none";
+ toggle.setAttribute("aria-expanded", expanded ? "true" : "false");
+ toggle.classList.toggle("is-expanded", expanded);
+ toggle.textContent = "";
+}
+
+function localtocHasMeaningfulEntries(localtoc) {
+ const links = Array.from(localtoc.querySelectorAll("a.reference.internal"));
+ return links.some((link) => {
+ const href = (link.getAttribute("href") || "").trim();
+ const text = (link.textContent || "").trim();
+ return text && href && href !== "#";
+ });
+}
+
+function getCodeDetailsBlocks() {
+ return Array.from(document.querySelectorAll("details.uplt-code-details"));
+}
+
+function initScrollChromeFade() {
+ const topBar = document.querySelector(".sy-head");
+ const leftBar = document.querySelector(".sy-lside");
+ if (!topBar && !leftBar) return;
+
+ let lastY = window.scrollY || 0;
+ let ticking = false;
+ const minDelta = 6;
+ const revealThreshold = 96;
+
+ const setHidden = (hidden) => {
+ document.documentElement.classList.toggle("uplt-chrome-hidden", hidden);
+ };
+
+ const update = () => {
+ const y = window.scrollY || 0;
+ const delta = y - lastY;
+ const expanded = (document.body.getAttribute("data-expanded") || "").trim();
+ const isMobileMenuOpen =
+ expanded.includes("head-nav") ||
+ expanded.includes("lside") ||
+ expanded.includes("rside");
+
+ if (window.innerWidth < 768 || isMobileMenuOpen || y < revealThreshold) {
+ setHidden(false);
+ } else if (delta > minDelta) {
+ setHidden(true);
+ } else if (delta < -minDelta) {
+ setHidden(false);
+ }
+
+ lastY = y;
+ ticking = false;
+ };
+
+ window.addEventListener(
+ "scroll",
+ () => {
+ if (!ticking) {
+ window.requestAnimationFrame(update);
+ ticking = true;
+ }
+ },
+ { passive: true },
+ );
+ window.addEventListener("resize", update, { passive: true });
+ update();
+}
+
+function syncRightTocCodeButtons(localtoc) {
+ if (!localtoc) return;
+ const blocks = getCodeDetailsBlocks();
+ let codeControls =
+ Array.from(localtoc.children).find(
+ (child) =>
+ child.classList && child.classList.contains("uplt-code-controls"),
+ ) || null;
+ if (!blocks.length) {
+ if (codeControls) {
+ codeControls.remove();
+ }
+ return;
+ }
+
+ if (!codeControls) {
+ codeControls = document.createElement("div");
+ codeControls.className = "uplt-code-controls";
+ localtoc.appendChild(codeControls);
+ }
+
+ let collapseCodeBtn = codeControls.querySelector(".uplt-code-collapse");
+ if (!collapseCodeBtn) {
+ collapseCodeBtn = document.createElement("button");
+ collapseCodeBtn.type = "button";
+ collapseCodeBtn.className = "uplt-toc-btn uplt-code-btn uplt-code-collapse";
+ collapseCodeBtn.addEventListener("click", function () {
+ const codeBlocks = getCodeDetailsBlocks();
+ const allCollapsed = codeBlocks.length > 0 && codeBlocks.every((block) => !block.open);
+ if (allCollapsed) {
+ codeBlocks.forEach((block) => {
+ block.open = true;
+ });
+ } else {
+ codeBlocks.forEach((block) => {
+ block.open = false;
+ });
+ }
+ updateCodeButtonLabels();
+ });
+ codeControls.appendChild(collapseCodeBtn);
+ }
+
+ const updateCodeButtonLabels = () => {
+ const codeBlocks = getCodeDetailsBlocks();
+ const allCollapsed = codeBlocks.length > 0 && codeBlocks.every((block) => !block.open);
+ collapseCodeBtn.textContent = allCollapsed ? "Show all code" : "Collapse code";
+ };
+
+ blocks.forEach((block) => {
+ if (block.dataset.upltCodeSync !== "1") {
+ block.addEventListener("toggle", updateCodeButtonLabels);
+ block.dataset.upltCodeSync = "1";
+ }
+ });
+ updateCodeButtonLabels();
+}
+
+function initShibuyaRightToc() {
+ const shibuyaRightToc = document.querySelector(".sy-rside");
+ if (!shibuyaRightToc) return;
+ const path = window.location.pathname || "";
+ const isGalleryIndexPage =
+ /\/gallery\/?$/.test(path) ||
+ /\/gallery\/index(?:_new)?\.html$/.test(path);
+ const forceHideRightToc =
+ document.body.classList.contains("no-right-toc") ||
+ isGalleryIndexPage ||
+ !!document.querySelector(".sphx-glr-thumbcontainer") ||
+ !!document.querySelector(".sphx-glr-thumbnails");
+ if (forceHideRightToc) {
+ shibuyaRightToc.style.display = "none";
+ const overlay = document.querySelector(".rside-overlay");
+ if (overlay) overlay.style.display = "none";
+ return;
+ }
+
+ const localtoc = shibuyaRightToc.querySelector(".localtoc");
+ if (!localtoc) return;
+
+ const overlay = document.querySelector(".rside-overlay");
+ if (!localtocHasMeaningfulEntries(localtoc)) {
+ shibuyaRightToc.style.display = "none";
+ if (overlay) overlay.style.display = "none";
+ return;
+ }
+ shibuyaRightToc.style.display = "";
+ if (overlay) overlay.style.display = "";
+
+ const storageKey = "uplt.rside.hidden";
+ const setRightTocHidden = (hidden) => {
+ document.body.classList.toggle("uplt-rside-hidden", hidden);
+ try {
+ localStorage.setItem(storageKey, hidden ? "1" : "0");
+ } catch (_err) {
+ // Ignore storage errors in private/incognito environments.
+ }
+ };
+
+ if (!document.body.dataset.upltRsideStateInit) {
+ let restoreHidden = false;
+ try {
+ restoreHidden = localStorage.getItem(storageKey) === "1";
+ } catch (_err) {
+ restoreHidden = false;
+ }
+ setRightTocHidden(restoreHidden);
+ document.body.dataset.upltRsideStateInit = "1";
+ }
+
+ let showBtn = document.querySelector(".uplt-rside-show");
+ if (!showBtn) {
+ showBtn = document.createElement("button");
+ showBtn.type = "button";
+ showBtn.className = "uplt-rside-show";
+ showBtn.textContent = "Show contents";
+ showBtn.setAttribute("aria-label", "Show right table of contents");
+ showBtn.addEventListener("click", function () {
+ setRightTocHidden(false);
+ });
+ document.body.appendChild(showBtn);
+ }
+
+ const topList = getDirectChildByTag(localtoc, "UL");
+ if (!topList) return;
+
+ let headRow =
+ Array.from(localtoc.children).find(
+ (child) => child.classList && child.classList.contains("uplt-toc-head"),
+ ) || null;
+ const directHeading = getDirectChildByTag(localtoc, "H3");
+ if (!headRow && directHeading) {
+ headRow = document.createElement("div");
+ headRow.className = "uplt-toc-head";
+ localtoc.insertBefore(headRow, directHeading);
+ headRow.appendChild(directHeading);
+ }
+ if (headRow) {
+ let hideBtn = headRow.querySelector(".uplt-toc-btn-hide");
+ if (!hideBtn) {
+ hideBtn = document.createElement("button");
+ hideBtn.type = "button";
+ hideBtn.className = "uplt-toc-btn uplt-toc-btn-hide";
+ hideBtn.textContent = "Hide";
+ hideBtn.addEventListener("click", function () {
+ setRightTocHidden(true);
+ });
+ headRow.appendChild(hideBtn);
+ }
+ }
+
+ const topItems = Array.from(topList.children).filter(
+ (node) => node.tagName === "LI",
+ );
+ const collapsibleItems = [];
+ const currentHash = (window.location.hash || "").trim();
+
+ topItems.forEach((item) => {
+ const link =
+ Array.from(item.children).find(
+ (child) =>
+ child.tagName === "A" &&
+ child.classList.contains("reference") &&
+ child.classList.contains("internal"),
+ ) || null;
+ const childList = getDirectChildByTag(item, "UL");
+ if (!link || !childList) return;
+
+ item.classList.add("uplt-toc-collapsible");
+ let toggle = getDirectToggleButton(item);
+ if (!toggle) {
+ toggle = document.createElement("button");
+ toggle.type = "button";
+ toggle.className = "uplt-toc-toggle";
+ toggle.setAttribute("aria-label", "Toggle section");
+ toggle.textContent = "";
+ toggle.addEventListener("click", function () {
+ const expanded = toggle.getAttribute("aria-expanded") === "true";
+ setTocItemExpanded(item, !expanded);
+ });
+ item.insertBefore(toggle, link);
+ }
+
+ const hashInChildren =
+ currentHash &&
+ Array.from(childList.querySelectorAll("a.reference.internal")).some(
+ (a) => (a.getAttribute("href") || "").trim() === currentHash,
+ );
+ const hashOnTop = currentHash && (link.getAttribute("href") || "") === currentHash;
+ if (!toggle.hasAttribute("aria-expanded")) {
+ setTocItemExpanded(item, !!(hashOnTop || hashInChildren));
+ } else if (hashOnTop || hashInChildren) {
+ setTocItemExpanded(item, true);
+ }
+
+ collapsibleItems.push(item);
+ });
+
+ let controls =
+ Array.from(localtoc.children).find(
+ (child) =>
+ child.classList && child.classList.contains("uplt-toc-controls"),
+ ) || null;
+ if (!collapsibleItems.length) {
+ if (controls) controls.remove();
+ syncRightTocCodeButtons(localtoc);
+ return;
+ }
+
+ if (!controls) {
+ controls = document.createElement("div");
+ controls.className = "uplt-toc-controls";
+ localtoc.insertBefore(controls, topList);
+ }
+
+ let collapseBtn = controls.querySelector(".uplt-toc-btn-collapse");
+ if (!collapseBtn) {
+ collapseBtn = document.createElement("button");
+ collapseBtn.type = "button";
+ collapseBtn.className = "uplt-toc-btn uplt-toc-btn-collapse";
+ collapseBtn.textContent = "Collapse";
+ collapseBtn.addEventListener("click", function () {
+ collapsibleItems.forEach((item) => setTocItemExpanded(item, false));
+ });
+ controls.appendChild(collapseBtn);
+ }
+
+ let expandBtn = controls.querySelector(".uplt-toc-btn-expand");
+ if (!expandBtn) {
+ expandBtn = document.createElement("button");
+ expandBtn.type = "button";
+ expandBtn.className = "uplt-toc-btn uplt-toc-btn-expand";
+ expandBtn.textContent = "Expand";
+ expandBtn.addEventListener("click", function () {
+ collapsibleItems.forEach((item) => setTocItemExpanded(item, true));
+ });
+ controls.appendChild(expandBtn);
+ }
+
+ syncRightTocCodeButtons(localtoc);
+}
+
document.addEventListener("DOMContentLoaded", function () {
+ initScrollChromeFade();
+
+ if (document.querySelector(".sphx-glr-thumbcontainer")) {
+ document.body.classList.add("no-right-toc");
+ }
+
+ // Shibuya theme: right TOC controls and collapsible sub-sections.
+ initShibuyaRightToc();
+ window.addEventListener("hashchange", initShibuyaRightToc);
+
// Check if current page has opted out of the TOC
if (document.body.classList.contains("no-right-toc")) {
return;
@@ -206,6 +547,44 @@ document.addEventListener("DOMContentLoaded", function () {
});
document.addEventListener("DOMContentLoaded", function () {
+ const wrapWithCodeToggle = (block) => {
+ if (!block || !block.parentNode) return;
+ if (block.closest("details.uplt-code-details")) return;
+ const details = document.createElement("details");
+ details.className = "uplt-code-details";
+ const summary = document.createElement("summary");
+ summary.className = "uplt-code-summary";
+ summary.textContent = "Show code";
+ details.appendChild(summary);
+ block.parentNode.insertBefore(details, block);
+ details.appendChild(block);
+ details.open = false;
+ details.addEventListener("toggle", function () {
+ summary.textContent = details.open ? "Hide code" : "Show code";
+ });
+ };
+
+ // Gallery example pages: collapse source code blocks by default.
+ const galleryExampleCodeBlocks = Array.from(
+ document.querySelectorAll(
+ "section.sphx-glr-example-title div.highlight-Python.notranslate",
+ ),
+ );
+ galleryExampleCodeBlocks.forEach((block) => {
+ wrapWithCodeToggle(block);
+ });
+
+ // Notebook-style tutorial pages: collapse input code cells by default.
+ const notebookInputBlocks = Array.from(
+ document.querySelectorAll("div.nbinput.docutils.container"),
+ );
+ notebookInputBlocks.forEach((block) => {
+ wrapWithCodeToggle(block);
+ });
+
+ // Re-sync right TOC controls now that code wrappers exist.
+ initShibuyaRightToc();
+
const navLinks = document.querySelectorAll(
".wy-menu-vertical a.reference.internal",
);
diff --git a/docs/conf.py b/docs/conf.py
index 66f4edff6..5fcc100af 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -57,11 +57,41 @@ def __getattr__(self, name):
# Build what's news page from github releases
from subprocess import run
-run([sys.executable, "_scripts/fetch_releases.py"], check=False)
+FAST_PREVIEW = os.environ.get("UPLT_DOCS_FAST_PREVIEW", "").strip().lower() in {
+ "1",
+ "true",
+ "yes",
+ "on",
+}
+if not FAST_PREVIEW:
+ run([sys.executable, "_scripts/fetch_releases.py"], check=False)
+
+# Docs theme selector. Default to Shibuya, but keep env override for A/B checks.
+DOCS_THEME = os.environ.get("UPLT_DOCS_THEME", "shibuya").strip().lower()
+if DOCS_THEME in {"ultratheme", "rtd", "sphinx_rtd_light_dark"}:
+ DOCS_THEME = "sphinx_rtd_light_dark"
+else:
+ DOCS_THEME = "shibuya"
+if DOCS_THEME == "shibuya":
+ try:
+ import shibuya # noqa: F401
+ except Exception:
+ print("Shibuya theme not installed; falling back to sphinx_rtd_light_dark.")
+ DOCS_THEME = "sphinx_rtd_light_dark"
# Update path for sphinx-automodapi and sphinxext extension
sys.path.append(os.path.abspath("."))
sys.path.insert(0, os.path.abspath(".."))
+_ultratheme_path = os.path.abspath("../UltraTheme")
+if os.path.isdir(_ultratheme_path):
+ sys.path.insert(0, _ultratheme_path)
+
+try:
+ import ultraplot_theme # noqa: F401
+
+ HAVE_ULTRAPLOT_THEME_EXT = True
+except Exception:
+ HAVE_ULTRAPLOT_THEME_EXT = False
# Ensure whats_new exists during local builds without GitHub fetch.
whats_new_path = Path(__file__).parent / "whats_new.rst"
@@ -194,13 +224,17 @@ def _reset_ultraplot(gallery_conf, fname):
"sphinx.ext.autosummary", # autosummary directive
"sphinxext.custom_roles", # local extension
"sphinx_automodapi.automodapi", # fork of automodapi
- "sphinx_rtd_light_dark", # use custom theme
- "sphinx_sitemap",
"sphinx_copybutton", # add copy button to code
"_ext.notoc",
"nbsphinx", # parse rst books
"sphinx_gallery.gen_gallery",
]
+if not FAST_PREVIEW:
+ extensions.append("sphinx_sitemap")
+if HAVE_ULTRAPLOT_THEME_EXT:
+ extensions.append("ultraplot_theme")
+elif DOCS_THEME == "sphinx_rtd_light_dark":
+ extensions.append("sphinx_rtd_light_dark")
autosectionlabel_prefix_document = True
@@ -306,6 +340,8 @@ def _reset_ultraplot(gallery_conf, fname):
"pint": ("https://pint.readthedocs.io/en/stable/", None),
"networkx": ("https://networkx.org/documentation/stable/", None),
}
+if FAST_PREVIEW:
+ intersphinx_mapping = {}
# Fix duplicate class member documentation from autosummary + numpydoc
@@ -359,7 +395,11 @@ def _reset_ultraplot(gallery_conf, fname):
# Add jupytext support to nbsphinx
nbsphinx_custom_formats = {".py": ["jupytext.reads", {"fmt": "py:percent"}]}
-nbsphinx_execute = "auto"
+# Control notebook execution from env for predictable local/CI builds.
+# Use values: auto, always, never.
+nbsphinx_execute = os.environ.get("UPLT_DOCS_EXECUTE", "auto").strip().lower()
+if nbsphinx_execute not in {"auto", "always", "never"}:
+ nbsphinx_execute = "auto"
# Suppress warnings in nbsphinx kernels without injecting visible cells.
os.environ["PYTHONWARNINGS"] = "ignore"
@@ -387,8 +427,9 @@ def _reset_ultraplot(gallery_conf, fname):
}
# The name of the Pygments (syntax highlighting) style to use.
-# The light-dark theme toggler overloads this, but set default anyway
-pygments_style = "none"
+# Use non-purple-forward palettes for clearer code contrast in both modes.
+pygments_style = "friendly"
+pygments_dark_style = "native"
html_baseurl = "https://ultraplot.readthedocs.io/stable"
sitemap_url_scheme = "{link}"
@@ -405,20 +446,45 @@ def _reset_ultraplot(gallery_conf, fname):
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
-# Use modified RTD theme with overrides in custom.css and custom.js
-style = None
-html_theme = "sphinx_rtd_light_dark"
-# html_theme = "alabaster"
-# html_theme = "sphinx_rtd_theme"
-html_theme_options = {
- "logo_only": True,
- "collapse_navigation": True,
- "navigation_depth": 4,
- "prev_next_buttons_location": "bottom", # top and bottom
- "includehidden": True,
- "titles_only": True,
- "sticky_navigation": True,
-}
+# Shibuya is default. Keep legacy RTD-light-dark settings for fallback builds.
+if DOCS_THEME == "shibuya":
+ html_theme = "shibuya"
+ html_theme_options = {
+ "toctree_collapse": True,
+ "toctree_maxdepth": 4,
+ "toctree_titles_only": True,
+ "toctree_includehidden": True,
+ "globaltoc_expand_depth": 1,
+ "light_logo": "logo_square.png",
+ "dark_logo": "logo_square.png",
+ "logo_target": "index.html",
+ "accent_color": "blue",
+ "nav_links": [
+ {"title": "Why UltraPlot?", "url": "why"},
+ {"title": "Gallery", "url": "gallery/index"},
+ {"title": "Installation guide", "url": "install"},
+ {"title": "Usage", "url": "usage"},
+ {"title": "API", "url": "api"},
+ {"title": "GitHub", "url": "https://github.com/Ultraplot/UltraPlot"},
+ {
+ "title": "Discussions",
+ "url": "https://github.com/Ultraplot/UltraPlot/discussions",
+ },
+ ],
+ }
+else:
+ # Use modified RTD theme with overrides in custom.css and custom.js.
+ style = None
+ html_theme = "sphinx_rtd_light_dark"
+ html_theme_options = {
+ "logo_only": True,
+ "collapse_navigation": True,
+ "navigation_depth": 4,
+ "prev_next_buttons_location": "bottom", # top and bottom
+ "includehidden": True,
+ "titles_only": True,
+ "sticky_navigation": True,
+ }
# 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,
@@ -447,12 +513,12 @@ def _reset_ultraplot(gallery_conf, fname):
htmlhelp_basename = "ultraplotdoc"
-html_css_files = [
- "custom.css",
-]
-html_js_files = [
- "custom.js",
-]
+if HAVE_ULTRAPLOT_THEME_EXT:
+ html_css_files = []
+ html_js_files = []
+else:
+ html_css_files = ["custom.css"]
+ html_js_files = ["custom.js"]
# -- Options for LaTeX output ------------------------------------------------
diff --git a/docs/index.rst b/docs/index.rst
index 6e1b0256b..5b5ec248d 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -5,20 +5,21 @@
**UltraPlot** is a succinct wrapper around `matplotlib `__
for creating **beautiful, publication-quality graphics** with ease.
-🚀 **Key Features** | Create More, Code Less
-###################
-✔ **Simplified Subplot Management** – Create multi-panel plots effortlessly.
+Key Features
+############
+Build polished figures quickly with pragmatic defaults.
+**Simplified Subplot Management** – Create multi-panel plots effortlessly.
-🎨 **Smart Aesthetics** – Optimized colormaps, fonts, and styles out of the box.
+**Smart Aesthetics** – Optimized colormaps, fonts, and styles out of the box.
-📊 **Versatile Plot Types** – Cartesian plots, insets, colormaps, and more.
+**Versatile Plot Types** – Cartesian plots, insets, colormaps, and more.
-📌 **Get Started** → :doc:`Installation guide ` | :doc:`Why UltraPlot? ` | :doc:`Usage ` | :doc:`Gallery `
+**Get Started** → :doc:`Installation guide ` | :doc:`Why UltraPlot? ` | :doc:`Usage ` | :doc:`Gallery `
--------------------------------------
-**📖 User Guide**
-#################
+User Guide
+##########
A preview of what UltraPlot can do. For more see the sidebar!
.. grid:: 1 2 3 3
@@ -105,9 +106,8 @@ A preview of what UltraPlot can do. For more see the sidebar!
Use prebuilt colormaps and define your own color cycles.
-
-**📚 Reference & More**
-#######################
+Reference & More
+################
For more details, check the full :doc:`User guide ` and :doc:`API Reference `.
* :ref:`genindex`
diff --git a/pyproject.toml b/pyproject.toml
index 0d36747a1..c3594a083 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -78,6 +78,7 @@ docs = [
"sphinx-copybutton",
"sphinx-design",
"sphinx-gallery",
+ "shibuya",
"sphinx-rtd-light-dark @ git+https://github.com/ultraplot/UltraTheme.git",
"sphinx-sitemap",
"typing-extensions"