Skip to content

Commit cd67e14

Browse files
dark logo for sidebar; unified and simple brand logo
dashboard, revealjs, and typst old tests passing brand metadata now only contains one polarity of logo since we split the unified logo immediately and now our light and dark brands can just be that
1 parent 9a8ad80 commit cd67e14

12 files changed

Lines changed: 103 additions & 84 deletions

File tree

src/core/brand/brand.ts

Lines changed: 45 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@ import {
1010
BrandColorLightDark,
1111
BrandFont,
1212
BrandLogoExplicitResource,
13+
BrandLogoSingle,
14+
BrandLogoUnified,
1315
BrandNamedLogo,
1416
BrandNamedThemeColor,
1517
BrandSingle,
18+
BrandStringLightDark,
1619
BrandTypographyOptionsBase,
1720
BrandTypographyOptionsHeadingsSingle,
1821
BrandTypographySingle,
@@ -26,18 +29,13 @@ import { join, relative } from "../../deno_ral/path.ts";
2629
import { warnOnce } from "../log.ts";
2730
import { isCssColorName } from "../css/color-names.ts";
2831

29-
type CanonicalLogoInfo = {
30-
light: BrandLogoExplicitResource;
31-
dark: BrandLogoExplicitResource;
32-
};
33-
3432
type ProcessedBrandData = {
3533
color: Record<string, string>;
3634
typography: BrandTypographySingle;
3735
logo: {
38-
small?: CanonicalLogoInfo;
39-
medium?: CanonicalLogoInfo;
40-
large?: CanonicalLogoInfo;
36+
small?: BrandLogoExplicitResource;
37+
medium?: BrandLogoExplicitResource;
38+
large?: BrandLogoExplicitResource;
4139
images: Record<string, BrandLogoExplicitResource>;
4240
};
4341
};
@@ -253,30 +251,12 @@ export class Brand {
253251
};
254252
}
255253

256-
getLogo(name: BrandNamedLogo): CanonicalLogoInfo | undefined {
254+
getLogo(name: BrandNamedLogo): BrandLogoExplicitResource | undefined {
257255
const entry = this.data.logo?.[name];
258256
if (!entry) {
259257
return undefined;
260258
}
261-
if (typeof entry === "string") {
262-
const res = this.getLogoResource(entry);
263-
return {
264-
light: res,
265-
dark: res,
266-
};
267-
}
268-
const lightEntry = entry?.light
269-
? this.getLogoResource(entry.light)
270-
: undefined;
271-
const darkEntry = entry?.dark
272-
? this.getLogoResource(entry.dark)
273-
: undefined;
274-
if (lightEntry && darkEntry) {
275-
return {
276-
light: lightEntry,
277-
dark: darkEntry,
278-
};
279-
}
259+
return this.getLogoResource(entry);
280260
}
281261
}
282262

@@ -295,7 +275,7 @@ export const getFavicon = (brand: Brand): string | undefined => {
295275
if (!logoInfo) {
296276
return undefined;
297277
}
298-
return logoInfo.light.path;
278+
return logoInfo.path;
299279
};
300280

301281
function splitColorLightDark(
@@ -307,9 +287,8 @@ function splitColorLightDark(
307287
return bcld;
308288
}
309289

310-
function enablesDarkMode(blcd: BrandColorLightDark) {
311-
return typeof blcd === "object" && "dark" in blcd;
312-
}
290+
const enablesDarkMode = (x: BrandColorLightDark | BrandStringLightDark) =>
291+
typeof x === "object" && x?.dark;
313292

314293
export function brandHasDarkMode(brand: BrandUnified): boolean {
315294
if (brand.color) {
@@ -324,7 +303,7 @@ export function brandHasDarkMode(brand: BrandUnified): boolean {
324303
}
325304
if (brand.typography) {
326305
for (const elementName of Zod.BrandNamedTypographyElements.options) {
327-
const element = brand.typography![elementName];
306+
const element = brand.typography[elementName];
328307
if (!element || typeof element === "string") {
329308
continue;
330309
}
@@ -342,6 +321,17 @@ export function brandHasDarkMode(brand: BrandUnified): boolean {
342321
}
343322
}
344323
}
324+
if (brand.logo) {
325+
for (const logoName of Zod.BrandNamedLogo.options) {
326+
const logo = brand.logo[logoName];
327+
if (!logo || typeof logo === "string") {
328+
continue;
329+
}
330+
if (enablesDarkMode(logo)) {
331+
return true;
332+
}
333+
}
334+
}
345335
return false;
346336
}
347337

@@ -367,6 +357,25 @@ function sharedTypography(
367357
}
368358
return ret;
369359
}
360+
361+
function splitLogo(
362+
unifiedLogo: BrandLogoUnified,
363+
): { light: BrandLogoSingle; dark: BrandLogoSingle } {
364+
const light: BrandLogoSingle = { images: unifiedLogo.images },
365+
dark: BrandLogoSingle = { images: unifiedLogo.images };
366+
for (const logoName of Zod.BrandNamedLogo.options) {
367+
if (unifiedLogo[logoName]) {
368+
if (typeof unifiedLogo[logoName] === "string") {
369+
light[logoName] = dark[logoName] = unifiedLogo[logoName];
370+
continue;
371+
}
372+
({ light: light[logoName], dark: dark[logoName] } =
373+
unifiedLogo[logoName]);
374+
}
375+
}
376+
return { light, dark };
377+
}
378+
370379
export function splitUnifiedBrand(
371380
unified: unknown,
372381
brandDir: string,
@@ -506,18 +515,19 @@ export function splitUnifiedBrand(
506515
linkBackgroundColor[mode],
507516
},
508517
};
518+
const logos = unifiedBrand.logo && splitLogo(unifiedBrand.logo);
509519
const lightBrand: BrandSingle = {
510520
meta: unifiedBrand.meta,
511521
color: { palette: unifiedBrand.color && { ...unifiedBrand.color.palette } },
512522
typography: typography && specializeTypography(typography, "light"),
513-
logo: unifiedBrand.logo,
523+
logo: logos && logos.light,
514524
defaults: unifiedBrand.defaults,
515525
};
516526
const darkBrand: BrandSingle = {
517527
meta: unifiedBrand.meta,
518528
color: { palette: unifiedBrand.color && { ...unifiedBrand.color.palette } },
519529
typography: typography && specializeTypography(typography, "dark"),
520-
logo: unifiedBrand.logo,
530+
logo: logos && logos.dark,
521531
defaults: unifiedBrand.defaults,
522532
};
523533
if (unifiedBrand.color) {

src/format/reveal/format-reveal.ts

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import {
2525
Metadata,
2626
PandocFlags,
2727
} from "../../config/types.ts";
28+
import { BrandNamedLogo, Zod } from "../../resources/types/zod/schema-types.ts";
29+
2830
import { mergeConfigs } from "../../core/config.ts";
2931
import { formatResourcePath } from "../../core/resources.ts";
3032
import { renderEjs } from "../../core/ejs.ts";
@@ -377,18 +379,14 @@ export function revealjsFormat() {
377379
const determineRevealLogo = (format: Format): string | undefined => {
378380
const brandData = format.render.brand?.light?.processedData;
379381
if (brandData?.logo) {
380-
const keys: ("medium" | "small" | "large")[] = ["medium", "small", "large"];
381382
// add slide logo if we have one
382-
for (const size of keys) {
383+
for (const size of Zod.BrandNamedLogo.options) {
383384
const logoInfo = brandData.logo[size];
384385
if (!logoInfo) {
385386
continue;
386387
}
387-
if (typeof logoInfo === "string") {
388-
return logoInfo;
389-
} else {
390-
// what to do about light vs dark?
391-
return logoInfo?.light.path ?? logoInfo?.dark.path;
388+
if (logoInfo) {
389+
return logoInfo.path;
392390
}
393391
}
394392
}
@@ -403,14 +401,12 @@ function revealMarkdownAfterBody(format: Format) {
403401
if (typeof revealLogo === "object") {
404402
revealLogo = revealLogo.path;
405403
}
406-
if (["small", "medium", "large"].includes(revealLogo)) {
404+
if (Zod.BrandNamedLogo.options.includes(revealLogo as BrandNamedLogo)) {
407405
const brandData = format.render.brand?.light?.processedData;
408406
const logoInfo = brandData?.logo
409-
?.[revealLogo as ("medium" | "small" | "large")];
410-
if (typeof logoInfo === "string") {
411-
revealLogo = logoInfo;
412-
} else {
413-
revealLogo = logoInfo?.light.path ?? logoInfo?.dark.path;
407+
?.[revealLogo as BrandNamedLogo];
408+
if (logoInfo) {
409+
revealLogo = logoInfo.path;
414410
}
415411
}
416412
} else {

src/project/types.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { ExecutionEngine, ExecutionTarget } from "../execute/types.ts";
1414
import { InspectedMdCell } from "../inspect/inspect-types.ts";
1515
import { NotebookContext } from "../render/notebook/notebook-types.ts";
1616
import {
17+
BrandLogoExplicitResource,
1718
NavigationItem as NavItem,
1819
NavigationItemObject,
1920
NavigationItemObject as SidebarTool,
@@ -149,6 +150,11 @@ export const kLogoHref = "logo-href";
149150

150151
export const kSidebarMenus = "sidebar-menus";
151152

153+
export interface LightDarkLogo {
154+
light?: BrandLogoExplicitResource;
155+
dark?: BrandLogoExplicitResource;
156+
}
157+
152158
export interface Navbar {
153159
title?: string | false;
154160
logo?: string;
@@ -197,8 +203,7 @@ export interface Sidebar {
197203
id?: string;
198204
title?: string;
199205
subtitle?: string;
200-
logo?: string;
201-
[kLogoAlt]?: string;
206+
logo?: LightDarkLogo;
202207
[kLogoHref]?: string;
203208
alignment?: "left" | "right" | "center";
204209
align?: "left" | "right" | "center"; // This is here only because older versions of Quarto used to use it even though it wasn't documented

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,8 +1012,12 @@ async function sidebarEjsData(project: ProjectContext, sidebar: Sidebar) {
10121012

10131013
// ensure title and search are present
10141014
sidebar.title = await sidebarTitle(sidebar, project) as string | undefined;
1015-
sidebar.logo = resolveLogo(sidebar.logo);
1016-
1015+
if (sidebar.logo?.light) {
1016+
sidebar.logo.light.path = resolveLogo(sidebar.logo.light.path)!;
1017+
}
1018+
if (sidebar.logo?.dark) {
1019+
sidebar.logo.dark.path = resolveLogo(sidebar.logo.dark.path)!;
1020+
}
10171021
const searchOpts = await searchOptions(project);
10181022
sidebar.search = sidebar.search !== undefined
10191023
? sidebar.search

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

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -172,9 +172,16 @@ export async function websiteNavigationConfig(project: ProjectContext) {
172172
const logo = projectBrand.light.processedData.logo.medium ??
173173
projectBrand.light.processedData.logo.small ??
174174
projectBrand.light.processedData.logo.large;
175-
if (logo) {
176-
sidebars[0].logo = logo.light.path; // TODO: This needs smarts to work on light+dark themes
177-
sidebars[0]["logo-alt"] = logo.light.alt;
175+
const darkLogo = projectBrand.dark && (
176+
projectBrand.dark.processedData.logo.medium ??
177+
projectBrand.dark.processedData.logo.small ??
178+
projectBrand.dark.processedData.logo.large
179+
);
180+
if (logo || darkLogo) {
181+
sidebars[0].logo = {
182+
light: logo,
183+
dark: darkLogo,
184+
};
178185
}
179186
}
180187
}
@@ -186,8 +193,9 @@ export async function websiteNavigationConfig(project: ProjectContext) {
186193
projectBrand.light.processedData.logo.medium ??
187194
projectBrand.light.processedData.logo.large;
188195
if (logo) {
189-
navbar.logo = logo.light.path; // TODO: This needs smarts to work on light+dark themes
190-
navbar["logo-alt"] = logo.light.alt;
196+
navbar.logo = logo.path; // TODO: This needs smarts to work on light+dark themes
197+
navbar["logo-alt"] = logo.alt;
198+
console.log("navbar logo", logo);
191199
}
192200
}
193201

src/resources/filters/quarto-post/dashboard.lua

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -743,18 +743,15 @@ function render_dashboard()
743743
local logo = meta.logo
744744
local resolved
745745
if logo then
746-
local which
747746
if pandoc.utils.type(logo) == 'Inlines' then
748-
which = logo[1].text
749-
local brandLogo = _quarto.modules.brand.get_logo('light', logo[1].text)
750-
resolved = brandLogo and brandLogo.light
747+
resolved = _quarto.modules.brand.get_logo('light', logo[1].text)
751748
elseif type(logo) == 'table' then
752749
local brandLogo = _quarto.modules.brand.get_logo('light', logo.path[1].text)
753750
if brandLogo then
754-
resolved = brandLogo.light
755-
if logo.alt then
756-
resolved.alt = logo.alt
757-
end
751+
resolved = {
752+
path = brandLogo.path,
753+
alt = logo.alt or brandLogo.alt
754+
}
758755
else
759756
resolved = {
760757
path = logo.path,
@@ -763,10 +760,9 @@ function render_dashboard()
763760
end
764761
end
765762
else
766-
logo = _quarto.modules.brand.get_logo('light', 'small')
763+
resolved = _quarto.modules.brand.get_logo('light', 'small')
767764
or _quarto.modules.brand.get_logo('light', 'medium')
768765
or _quarto.modules.brand.get_logo('light', 'large')
769-
resolved = logo and logo.light
770766
end
771767
if resolved then
772768
meta.logo = resolved.path

src/resources/filters/quarto-post/typst-brand-yaml.lua

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -219,13 +219,13 @@ function render_typst_brand_yaml()
219219
local foundLogo = null
220220
if logo then
221221
if type(logo) == 'string' then
222-
foundLogo = _quarto.modules.brand.get_logo(brandMode, logo) or {light={path=logo}}
222+
foundLogo = _quarto.modules.brand.get_logo(brandMode, logo) or {path=logo}
223223
elseif type(logo) == 'table' then
224224
for k, v in pairs(logo) do
225225
logoOptions[k] = v
226226
end
227227
if logo.path then
228-
foundLogo = _quarto.modules.brand.get_logo(brandMode, logo.path) or {light={path=logo}}
228+
foundLogo = _quarto.modules.brand.get_logo(brandMode, logo.path) or {path=logo}
229229
end
230230
end
231231
end
@@ -236,13 +236,8 @@ function render_typst_brand_yaml()
236236
or _quarto.modules.brand.get_logo(brandMode, 'large')
237237
end
238238
if foundLogo then
239-
if foundLogo.light then
240-
logoOptions.path = foundLogo.light.path
241-
logoOptions.alt = foundLogo.light.alt
242-
elseif foundLogo.dark then
243-
logoOptions.path = foundLogo.dark.path
244-
logoOptions.alt = foundLogo.dark.alt
245-
end
239+
logoOptions.path = foundLogo.path
240+
logoOptions.alt = foundLogo.alt
246241

247242
local pads = {}
248243
for k, v in _quarto.utils.table.sortedPairs(logoOptions) do

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,12 @@
3636
<div class="pt-lg-2 mt-2 <%= alignCss %> sidebar-header<%= sidebar.logo && sidebar.title ? ' sidebar-header-stacked' : '' %>">
3737
<% if (sidebar.logo) { %>
3838
<a href="<%- sidebar['logo-href'] || '/index.html' %>" class="sidebar-logo-link">
39-
<img src="<%- sidebar.logo %>" alt="<%- sidebar['logo-alt'] || '' %>" class="sidebar-logo py-0 d-lg-inline d-none"/>
39+
<% if (sidebar.logo.light) { %>
40+
<img src="<%- sidebar.logo.light.path %>" alt="<%- sidebar.logo.light.alt || '' %>" class="sidebar-logo light-content py-0 d-lg-inline d-none"/>
41+
<% } %>
42+
<% if (sidebar.logo.dark) { %>
43+
<img src="<%- sidebar.logo.dark.path %>" alt="<%- sidebar.logo.dark.alt || '' %>" class="sidebar-logo dark-content py-0 d-lg-inline d-none"/>
44+
<% } %>
4045
</a>
4146
<% } %>
4247
<% if (needsTools && toolsLocation === "logo") { %>

src/resources/schema/definitions.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3222,7 +3222,7 @@
32223222
meta:
32233223
ref: brand-meta
32243224
logo:
3225-
ref: brand-logo-unified # temporary
3225+
ref: brand-logo-single
32263226
color:
32273227
ref: brand-color-single
32283228
typography:

tests/docs/smoke-all/brand/logo/choose-logo-resource.qmd

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ logo: large
1717
_quarto:
1818
tests:
1919
dashboard:
20-
ensureFileRegexMatches:
20+
ensureHtmlElements:
2121
-
22-
- '<img src="posit-logo-2024\.svg" alt="posit logo"'
22+
- 'img[src="posit-logo-2024.svg"][alt="posit logo"]'
2323
- []
2424
revealjs:
2525
ensureFileRegexMatches:

0 commit comments

Comments
 (0)