Skip to content

Commit 2546270

Browse files
committed
feat(website): enhanced control over navbar and sidebar titles (#13477)
- Allow `navbar.title` and `sidebar.title` to be `boolean` or `string`. - Fix bug where disabling navbar title erroneously hid sidebar title. - Support custom titles for navbar and sidebar. - Add smoke tests for title control options. - Update schema documentation for titling options.
1 parent a678464 commit 2546270

22 files changed

Lines changed: 291 additions & 31 deletions

File tree

src/project/types/book/book-config.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,6 @@ export async function bookProjectConfig(
191191
// if we have a top-level 'contents' or 'appendix' fields fold into sidebar
192192
site[kSiteSidebar] = site[kSiteSidebar] || {};
193193
const siteSidebar = site[kSiteSidebar] as Metadata;
194-
siteSidebar[kSiteTitle] = siteSidebar[kSiteTitle] || book?.[kSiteTitle];
195194
siteSidebar[kSidebarLogo] = siteSidebar[kSidebarLogo] || book?.[kSidebarLogo];
196195
siteSidebar[kSidebarLogoHref] = siteSidebar[kSidebarLogoHref] ||
197196
book?.[kSidebarLogoHref];

src/project/types/website/website-navigation.ts

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,13 +1009,15 @@ async function sidebarsEjsData(project: ProjectContext, sidebars: Sidebar[]) {
10091009
async function sidebarEjsData(project: ProjectContext, sidebar: Sidebar) {
10101010
sidebar = ld.cloneDeep(sidebar);
10111011

1012+
// ensure title is present
1013+
sidebar.title = await sidebarTitle(sidebar, project) as string | undefined;
1014+
10121015
// if the sidebar has a title and no id generate the id
10131016
if (sidebar.title && !sidebar.id) {
10141017
sidebar.id = asHtmlId(sidebar.title);
10151018
}
10161019

1017-
// ensure title and search are present
1018-
sidebar.title = await sidebarTitle(sidebar, project) as string | undefined;
1020+
// ensure search is present
10191021
const searchOpts = await searchOptions(project);
10201022
sidebar.search = sidebar.search !== undefined
10211023
? sidebar.search
@@ -1251,7 +1253,8 @@ async function navbarEjsData(
12511253
};
12521254
// if there is no navbar title and it hasn't been set to 'false'
12531255
// then use the site title
1254-
if (!data.title && data.title !== false) {
1256+
const navbarTitle = data.title as unknown as string | boolean | undefined;
1257+
if (navbarTitle === true || (!navbarTitle && navbarTitle !== false)) {
12551258
data.title = websiteTitle(project.config);
12561259
}
12571260
data.title = data.title || "";
@@ -1479,21 +1482,57 @@ function looksLikeShortCode(href: string) {
14791482

14801483
async function sidebarTitle(sidebar: Sidebar, project: ProjectContext) {
14811484
const { navbar } = await websiteNavigationConfig(project);
1482-
if (sidebar.title) {
1483-
// Title was explicitly set
1484-
return sidebar.title;
1485-
} else if (!sidebar.logo) {
1486-
if (!navbar) {
1487-
// If there isn't a logo and there isn't a sidebar, use the project title
1488-
return websiteTitle(project.config);
1489-
} else {
1490-
// The navbar will display the title
1491-
return undefined;
1492-
}
1493-
} else {
1494-
// There is a logo, just let the logo appear
1485+
1486+
const sidebarTitleValue = sidebar.title as unknown as string | boolean | undefined;
1487+
const projectTitle = websiteTitle(project.config);
1488+
1489+
// 1. "text": include the custom text
1490+
if (typeof sidebarTitleValue === "string") {
1491+
return sidebarTitleValue;
1492+
}
1493+
1494+
// 2. false: no title
1495+
if (sidebarTitleValue === false) {
14951496
return undefined;
14961497
}
1498+
1499+
// 3. true: includes the website/book title
1500+
if (sidebarTitleValue === true) {
1501+
return projectTitle;
1502+
}
1503+
1504+
// 4. undefined (title line not included)
1505+
// include website/book title if and only if (no title is included in navbar AND no logo is included in the sidebar)
1506+
// Exception: for books, a sidebar logo does not hide the title.
1507+
1508+
const hasLogo = (logo?: any) => {
1509+
if (!logo) return false;
1510+
if (typeof logo === "string") return true;
1511+
return !!(logo.light || logo.dark || logo.path);
1512+
};
1513+
1514+
const hasSidebarLogo = hasLogo(sidebar.logo);
1515+
1516+
let navbarHasAnyTitle = false;
1517+
if (navbar) {
1518+
const navbarTitleValue = navbar.title as unknown as string | boolean | undefined;
1519+
if (navbarTitleValue !== false) {
1520+
navbarHasAnyTitle = true;
1521+
}
1522+
}
1523+
1524+
const isBook = project.config?.project?.[kProjectType] === "book";
1525+
1526+
if (!navbarHasAnyTitle) {
1527+
// Navbar has no title.
1528+
// Rule: show sidebar title if no logo in sidebar.
1529+
// Exception: books ignore the sidebar logo constraint.
1530+
if (!hasSidebarLogo || isBook) {
1531+
return projectTitle;
1532+
}
1533+
}
1534+
1535+
return undefined;
14971536
}
14981537

14991538
async function websiteHeadroom(project: ProjectContext) {

src/resources/projects/website/templates/navbrand.ejs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
<% if (navbar.title || navbar.logo) { %>
1+
<% if (navbar.title || (navbar.logo && (navbar.logo.light || navbar.logo.dark))) { %>
22
<div class="navbar-brand-container mx-auto">
3-
<% if (navbar.logo) { %>
3+
<% if (navbar.logo && (navbar.logo.light || navbar.logo.dark)) { %>
44
<a href="<%- navbar['logo-href'] || '/index.html' %>" class="navbar-brand navbar-brand-logo">
55
<% if (navbar.logo.light) { %>
66
<img src="<%- navbar.logo.light.path %>" alt="<%- navbar.logo.light.alt || '' %>" class="navbar-logo light-content" />

src/resources/projects/website/templates/sidebar.ejs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@
2121
2222
let toolsLocation;
2323
// Under the title if that will be displayed
24-
if (sidebar.title && !navbar) {
24+
if (sidebar.title) {
2525
toolsLocation = "title";
26-
} else if (sidebar.logo) {
26+
} else if (sidebar.logo && (sidebar.logo.light || sidebar.logo.dark)) {
2727
toolsLocation = "logo";
2828
} else if (sidebar.search && sidebar.search !== "overlay") {
2929
toolsLocation = "search";
@@ -32,9 +32,9 @@
3232
}
3333
%>
3434
35-
<% if (sidebar.logo || (sidebar.title && !navbar)) { %>
36-
<div class="pt-lg-2 mt-2 <%= alignCss %> sidebar-header<%= sidebar.logo && sidebar.title ? ' sidebar-header-stacked' : '' %>">
37-
<% if (sidebar.logo) { %>
35+
<% if ((sidebar.logo && (sidebar.logo.light || sidebar.logo.dark)) || sidebar.title) { %>
36+
<div class="pt-lg-2 mt-2 <%= alignCss %> sidebar-header<%= (sidebar.logo && (sidebar.logo.light || sidebar.logo.dark)) && sidebar.title ? ' sidebar-header-stacked' : '' %>">
37+
<% if (sidebar.logo && (sidebar.logo.light || sidebar.logo.dark)) { %>
3838
<a href="<%- sidebar['logo-href'] || '/index.html' %>" class="sidebar-logo-link">
3939
<% if (sidebar.logo.light) { %>
4040
<img src="<%- sidebar.logo.light.path %>" alt="<%- sidebar.logo.light.alt || '' %>" class="sidebar-logo light-content py-0 d-lg-inline d-none"/>
@@ -48,19 +48,15 @@
4848
<% partial('navtools.ejs', { align: 'start', tools: sidebar.tools, className: 'sidebar-tools-main', darkToggle: sidebar.darkToggle, search: sidebar.search, readerToggle: sidebar.readerToggle, language: language })%>
4949
<% } %>
5050
51-
<% if (!navbar) { %>
5251
<% if (sidebar.title) { %>
5352
<div class="sidebar-title mb-0 py-0">
54-
<% if (!navbar) { %>
5553
<a href="/">
5654
<%- sidebar.title %>
5755
</a>
58-
<% } %>
5956
<% if (needsTools && toolsLocation === "title") { %>
6057
<% partial('navtools.ejs', { align: 'start', tools: sidebar.tools, className: 'sidebar-tools-main', darkToggle: sidebar.darkToggle, search: sidebar.search, readerToggle: sidebar.readerToggle, language: language })%>
6158
<% } %>
6259
</div>
63-
<% } %>
6460
<% } %>
6561
</div>
6662
<% } %>
@@ -97,4 +93,4 @@
9793
<% } %>
9894

9995
</nav>
100-
<div id="quarto-sidebar-glass" class="quarto-sidebar-collapse-item" data-bs-toggle="collapse" data-bs-target=".quarto-sidebar-collapse-item" ></div>
96+
<div id="quarto-sidebar-glass" class="quarto-sidebar-collapse-item" data-bs-toggle="collapse" data-bs-target=".quarto-sidebar-collapse-item" ></div>

src/resources/schema/definitions.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -989,7 +989,7 @@
989989
anyOf:
990990
- string
991991
- boolean
992-
description: "The navbar title. Uses the project title if none is specified."
992+
description: "The navbar title. Use a string for custom text, `true` (the default) to use the project title, or `false` to hide the title."
993993
logo:
994994
ref: logo-light-dark-specifier
995995
description: "Specification of image that will be displayed to the left of the title."
@@ -1071,7 +1071,7 @@
10711071
anyOf:
10721072
- string
10731073
- boolean
1074-
description: "The sidebar title. Uses the project title if none is specified."
1074+
description: "The sidebar title. Use a string for custom text, `true` to use the project title, or `false` to hide the title. If unspecified, the title is shown if there is no navbar title and no sidebar logo (except for books, where a sidebar logo does not hide the title)."
10751075
logo:
10761076
ref: logo-light-dark-specifier
10771077
description: "Specification of image that will be displayed in the sidebar."
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/.quarto/
2+
**/*.quarto_ipynb
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
project:
2+
type: book
3+
title: "My Book"
4+
5+
book:
6+
chapters:
7+
- index.qmd
8+
sidebar:
9+
logo: logo.png
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Preface {.unnumbered}
2+
3+
This is a book.
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/.quarto/
2+
**/*.quarto_ipynb

0 commit comments

Comments
 (0)