diff --git a/news/changelog-1.7.md b/news/changelog-1.7.md
index f11c2906dcb..c7296f85d62 100644
--- a/news/changelog-1.7.md
+++ b/news/changelog-1.7.md
@@ -17,6 +17,7 @@ All changes included in 1.7:
## Website projects
- ([#11701](https://github.com/quarto-dev/quarto-cli/issues/11701)): Wrap HTML emitted by EJS templates in `{=html}` blocks to avoid memory blowup issues with Pandoc's parser.
+- ([#12134](https://github.com/quarto-dev/quarto-cli/issues/12134)): Forward `logo.small` images in `_brand.yml` files to a website `favicon`.
## Blog projects
@@ -25,6 +26,7 @@ All changes included in 1.7:
## Book projects
- ([#11520](https://github.com/quarto-dev/quarto-cli/issues/11520)): Book's cover image now escapes lightbox treatment, which was incorrectly applied to it when `lightbox: true` was set in the book's configuration.
+- ([#12134](https://github.com/quarto-dev/quarto-cli/issues/12134)): Forward `logo.small` images in `_brand.yml` files to the `favicon` of the book's website.
## `quarto check`
diff --git a/src/core/brand/brand.ts b/src/core/brand/brand.ts
index d291fd4348a..b6292c40f7a 100644
--- a/src/core/brand/brand.ts
+++ b/src/core/brand/brand.ts
@@ -283,3 +283,11 @@ export class Brand {
}
}
}
+
+export const getFavicon = (brand: Brand): string | undefined => {
+ const logoInfo = brand.getLogo("small");
+ if (!logoInfo) {
+ return undefined;
+ }
+ return logoInfo.light.path;
+};
diff --git a/src/project/types/book/book-config.ts b/src/project/types/book/book-config.ts
index a66081bb3a8..05847a80ab2 100644
--- a/src/project/types/book/book-config.ts
+++ b/src/project/types/book/book-config.ts
@@ -125,6 +125,7 @@ import {
import { projectType } from "../project-types.ts";
import { BookRenderItem, BookRenderItemType } from "./book-types.ts";
import { isAbsoluteRef } from "../../../core/http.ts";
+import { getFavicon } from "../../../core/brand/brand.ts";
export async function bookProjectConfig(
project: ProjectContext,
@@ -141,6 +142,12 @@ export async function bookProjectConfig(
if (book) {
site[kSiteTitle] = book[kSiteTitle];
site[kSiteFavicon] = book[kSiteFavicon];
+ if (!site[kSiteFavicon]) {
+ const brand = await project.resolveBrand();
+ if (brand) {
+ site[kSiteFavicon] = getFavicon(brand);
+ }
+ }
site[kSiteUrl] = book[kSiteUrl];
site[kSitePath] = book[kSitePath];
site[kSiteRepoUrl] = book[kSiteRepoUrl];
diff --git a/src/project/types/website/website.ts b/src/project/types/website/website.ts
index c5248dd64ae..07ecb45aa8b 100644
--- a/src/project/types/website/website.ts
+++ b/src/project/types/website/website.ts
@@ -90,6 +90,7 @@ import { kFieldCategories } from "./listing/website-listing-shared.ts";
import { pandocNativeStr } from "../../../core/pandoc/codegen.ts";
import { asArray } from "../../../core/array.ts";
import { canonicalizeTitlePostprocessor } from "../../../format/html/format-html-title.ts";
+import { getFavicon } from "../../../core/brand/brand.ts";
export const kSiteTemplateDefault = "default";
export const kSiteTemplateBlog = "blog";
@@ -178,7 +179,13 @@ export const websiteProjectType: ProjectType = {
}
// dependency for favicon if we have one
- const favicon = websiteConfigString(kSiteFavicon, project.config);
+ let favicon = websiteConfigString(kSiteFavicon, project.config);
+ if (!favicon) {
+ const brand = await project.resolveBrand();
+ if (brand) {
+ favicon = getFavicon(brand);
+ }
+ }
if (favicon) {
const offset = projectOffset(project, source);
extras.html = extras.html || {};
diff --git a/tests/docs/smoke-all/brand/logo/brand-icon-small-favicon-book/.gitignore b/tests/docs/smoke-all/brand/logo/brand-icon-small-favicon-book/.gitignore
new file mode 100644
index 00000000000..075b2542afb
--- /dev/null
+++ b/tests/docs/smoke-all/brand/logo/brand-icon-small-favicon-book/.gitignore
@@ -0,0 +1 @@
+/.quarto/
diff --git a/tests/docs/smoke-all/brand/logo/brand-icon-small-favicon-book/_brand.yml b/tests/docs/smoke-all/brand/logo/brand-icon-small-favicon-book/_brand.yml
new file mode 100644
index 00000000000..acdfd043211
--- /dev/null
+++ b/tests/docs/smoke-all/brand/logo/brand-icon-small-favicon-book/_brand.yml
@@ -0,0 +1,2 @@
+logo:
+ small: logos/small.png
diff --git a/tests/docs/smoke-all/brand/logo/brand-icon-small-favicon-book/_quarto.yml b/tests/docs/smoke-all/brand/logo/brand-icon-small-favicon-book/_quarto.yml
new file mode 100644
index 00000000000..af340dc56bc
--- /dev/null
+++ b/tests/docs/smoke-all/brand/logo/brand-icon-small-favicon-book/_quarto.yml
@@ -0,0 +1,25 @@
+project:
+ type: book
+
+book:
+ title: "brand-icon-small-favicon-book"
+ author: "Norah Jones"
+ date: "2/21/2025"
+ chapters:
+ - index.qmd
+ - intro.qmd
+ - summary.qmd
+ - references.qmd
+
+bibliography: references.bib
+
+format:
+ html:
+ theme:
+ - cosmo
+ - brand
+ pdf:
+ documentclass: scrreprt
+
+
+
diff --git a/tests/docs/smoke-all/brand/logo/brand-icon-small-favicon-book/cover.png b/tests/docs/smoke-all/brand/logo/brand-icon-small-favicon-book/cover.png
new file mode 100644
index 00000000000..e1f5bc61d11
Binary files /dev/null and b/tests/docs/smoke-all/brand/logo/brand-icon-small-favicon-book/cover.png differ
diff --git a/tests/docs/smoke-all/brand/logo/brand-icon-small-favicon-book/index.qmd b/tests/docs/smoke-all/brand/logo/brand-icon-small-favicon-book/index.qmd
new file mode 100644
index 00000000000..06ec2006de2
--- /dev/null
+++ b/tests/docs/smoke-all/brand/logo/brand-icon-small-favicon-book/index.qmd
@@ -0,0 +1,14 @@
+---
+_quarto:
+ tests:
+ html:
+ ensureFileRegexMatches:
+ - ['']
+ - []
+---
+
+# Preface {.unnumbered}
+
+This is a Quarto book.
+
+To learn more about Quarto books visit .
diff --git a/tests/docs/smoke-all/brand/logo/brand-icon-small-favicon-book/intro.qmd b/tests/docs/smoke-all/brand/logo/brand-icon-small-favicon-book/intro.qmd
new file mode 100644
index 00000000000..efcf1721e9d
--- /dev/null
+++ b/tests/docs/smoke-all/brand/logo/brand-icon-small-favicon-book/intro.qmd
@@ -0,0 +1,5 @@
+# Introduction
+
+This is a book created from markdown and executable code.
+
+See @knuth84 for additional discussion of literate programming.
diff --git a/tests/docs/smoke-all/brand/logo/brand-icon-small-favicon-book/references.bib b/tests/docs/smoke-all/brand/logo/brand-icon-small-favicon-book/references.bib
new file mode 100644
index 00000000000..0220dbdf2e2
--- /dev/null
+++ b/tests/docs/smoke-all/brand/logo/brand-icon-small-favicon-book/references.bib
@@ -0,0 +1,19 @@
+@article{knuth84,
+ author = {Knuth, Donald E.},
+ title = {Literate Programming},
+ year = {1984},
+ issue_date = {May 1984},
+ publisher = {Oxford University Press, Inc.},
+ address = {USA},
+ volume = {27},
+ number = {2},
+ issn = {0010-4620},
+ url = {https://doi.org/10.1093/comjnl/27.2.97},
+ doi = {10.1093/comjnl/27.2.97},
+ journal = {Comput. J.},
+ month = may,
+ pages = {97–111},
+ numpages = {15}
+}
+
+
diff --git a/tests/docs/smoke-all/brand/logo/brand-icon-small-favicon-book/references.qmd b/tests/docs/smoke-all/brand/logo/brand-icon-small-favicon-book/references.qmd
new file mode 100644
index 00000000000..925f7c49464
--- /dev/null
+++ b/tests/docs/smoke-all/brand/logo/brand-icon-small-favicon-book/references.qmd
@@ -0,0 +1,4 @@
+# References {.unnumbered}
+
+::: {#refs}
+:::
diff --git a/tests/docs/smoke-all/brand/logo/brand-icon-small-favicon-book/summary.qmd b/tests/docs/smoke-all/brand/logo/brand-icon-small-favicon-book/summary.qmd
new file mode 100644
index 00000000000..b450ab7d0e3
--- /dev/null
+++ b/tests/docs/smoke-all/brand/logo/brand-icon-small-favicon-book/summary.qmd
@@ -0,0 +1,3 @@
+# Summary
+
+In summary, this book has no content whatsoever.
diff --git a/tests/docs/smoke-all/brand/logo/website-favicon/.gitignore b/tests/docs/smoke-all/brand/logo/website-favicon/.gitignore
new file mode 100644
index 00000000000..075b2542afb
--- /dev/null
+++ b/tests/docs/smoke-all/brand/logo/website-favicon/.gitignore
@@ -0,0 +1 @@
+/.quarto/
diff --git a/tests/docs/smoke-all/brand/logo/website-favicon/_brand.yml b/tests/docs/smoke-all/brand/logo/website-favicon/_brand.yml
new file mode 100644
index 00000000000..1c74182234f
--- /dev/null
+++ b/tests/docs/smoke-all/brand/logo/website-favicon/_brand.yml
@@ -0,0 +1,2 @@
+logo:
+ small: logos/small.png
diff --git a/tests/docs/smoke-all/brand/logo/website-favicon/_quarto.yml b/tests/docs/smoke-all/brand/logo/website-favicon/_quarto.yml
new file mode 100644
index 00000000000..448df0081f2
--- /dev/null
+++ b/tests/docs/smoke-all/brand/logo/website-favicon/_quarto.yml
@@ -0,0 +1,21 @@
+project:
+ type: website
+
+website:
+ title: "test-brand-favicon"
+ navbar:
+ left:
+ - href: index.qmd
+ text: Home
+ - about.qmd
+
+format:
+ html:
+ theme:
+ - cosmo
+ - brand
+ css: styles.css
+ toc: true
+
+
+
diff --git a/tests/docs/smoke-all/brand/logo/website-favicon/about.qmd b/tests/docs/smoke-all/brand/logo/website-favicon/about.qmd
new file mode 100644
index 00000000000..07c5e7f9d13
--- /dev/null
+++ b/tests/docs/smoke-all/brand/logo/website-favicon/about.qmd
@@ -0,0 +1,5 @@
+---
+title: "About"
+---
+
+About this site
diff --git a/tests/docs/smoke-all/brand/logo/website-favicon/index.qmd b/tests/docs/smoke-all/brand/logo/website-favicon/index.qmd
new file mode 100644
index 00000000000..f20e6354104
--- /dev/null
+++ b/tests/docs/smoke-all/brand/logo/website-favicon/index.qmd
@@ -0,0 +1,13 @@
+---
+title: "test-brand-favicon"
+_quarto:
+ tests:
+ html:
+ ensureFileRegexMatches:
+ - ['']
+ - []
+---
+
+This is a Quarto website.
+
+To learn more about Quarto websites visit .
diff --git a/tests/docs/smoke-all/brand/logo/website-favicon/logos/small.png b/tests/docs/smoke-all/brand/logo/website-favicon/logos/small.png
new file mode 100644
index 00000000000..fb4fbfd3f2a
Binary files /dev/null and b/tests/docs/smoke-all/brand/logo/website-favicon/logos/small.png differ
diff --git a/tests/docs/smoke-all/brand/logo/website-favicon/styles.css b/tests/docs/smoke-all/brand/logo/website-favicon/styles.css
new file mode 100644
index 00000000000..2ddf50c7b42
--- /dev/null
+++ b/tests/docs/smoke-all/brand/logo/website-favicon/styles.css
@@ -0,0 +1 @@
+/* css styles */