feat(fonts): allow PageTitle to have its own font subset (#1848)
* fix(explorer): vertically center the Explorer toggle under mobile view * Added a separate title font configuration * Added googleSubFontHref function * Applied --titleFont to PageTitle * Made googleFontHref return array of URLs * Dealing with empty and undefined title * Minor update * Dealing with empty and undefined title * Refined font inclusion logic * Adopted the googleFontHref + googleFontSubsetHref method * Adaptively include font subset for PageTitle * Restored default config * Minor changes on configuration docs * Formatted source code
This commit is contained in:
		@@ -41,11 +41,12 @@ This part of the configuration concerns anything that can affect the whole site.
 | 
				
			|||||||
- `ignorePatterns`: a list of [glob](<https://en.wikipedia.org/wiki/Glob_(programming)>) patterns that Quartz should ignore and not search through when looking for files inside the `content` folder. See [[private pages]] for more details.
 | 
					- `ignorePatterns`: a list of [glob](<https://en.wikipedia.org/wiki/Glob_(programming)>) patterns that Quartz should ignore and not search through when looking for files inside the `content` folder. See [[private pages]] for more details.
 | 
				
			||||||
- `defaultDateType`: whether to use created, modified, or published as the default date to display on pages and page listings.
 | 
					- `defaultDateType`: whether to use created, modified, or published as the default date to display on pages and page listings.
 | 
				
			||||||
- `theme`: configure how the site looks.
 | 
					- `theme`: configure how the site looks.
 | 
				
			||||||
  - `cdnCaching`: If `true` (default), use Google CDN to cache the fonts. This will generally will be faster. Disable (`false`) this if you want Quartz to download the fonts to be self-contained.
 | 
					  - `cdnCaching`: if `true` (default), use Google CDN to cache the fonts. This will generally be faster. Disable (`false`) this if you want Quartz to download the fonts to be self-contained.
 | 
				
			||||||
  - `typography`: what fonts to use. Any font available on [Google Fonts](https://fonts.google.com/) works here.
 | 
					  - `typography`: what fonts to use. Any font available on [Google Fonts](https://fonts.google.com/) works here.
 | 
				
			||||||
    - `header`: Font to use for headers
 | 
					    - `title`: font for the title of the site (optional, same as `header` by default)
 | 
				
			||||||
    - `code`: Font for inline and block quotes.
 | 
					    - `header`: font to use for headers
 | 
				
			||||||
    - `body`: Font for everything
 | 
					    - `code`: font for inline and block quotes
 | 
				
			||||||
 | 
					    - `body`: font for everything
 | 
				
			||||||
  - `colors`: controls the theming of the site.
 | 
					  - `colors`: controls the theming of the site.
 | 
				
			||||||
    - `light`: page background
 | 
					    - `light`: page background
 | 
				
			||||||
    - `lightgray`: borders
 | 
					    - `lightgray`: borders
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
import { i18n } from "../i18n"
 | 
					import { i18n } from "../i18n"
 | 
				
			||||||
import { FullSlug, getFileExtension, joinSegments, pathToRoot } from "../util/path"
 | 
					import { FullSlug, getFileExtension, joinSegments, pathToRoot } from "../util/path"
 | 
				
			||||||
import { CSSResourceToStyleElement, JSResourceToScriptElement } from "../util/resources"
 | 
					import { CSSResourceToStyleElement, JSResourceToScriptElement } from "../util/resources"
 | 
				
			||||||
import { googleFontHref } from "../util/theme"
 | 
					import { googleFontHref, googleFontSubsetHref } from "../util/theme"
 | 
				
			||||||
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
 | 
					import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
 | 
				
			||||||
import { unescapeHTML } from "../util/escape"
 | 
					import { unescapeHTML } from "../util/escape"
 | 
				
			||||||
import { CustomOgImagesEmitterName } from "../plugins/emitters/ogImage"
 | 
					import { CustomOgImagesEmitterName } from "../plugins/emitters/ogImage"
 | 
				
			||||||
@@ -45,6 +45,9 @@ export default (() => {
 | 
				
			|||||||
            <link rel="preconnect" href="https://fonts.googleapis.com" />
 | 
					            <link rel="preconnect" href="https://fonts.googleapis.com" />
 | 
				
			||||||
            <link rel="preconnect" href="https://fonts.gstatic.com" />
 | 
					            <link rel="preconnect" href="https://fonts.gstatic.com" />
 | 
				
			||||||
            <link rel="stylesheet" href={googleFontHref(cfg.theme)} />
 | 
					            <link rel="stylesheet" href={googleFontHref(cfg.theme)} />
 | 
				
			||||||
 | 
					            {cfg.theme.typography.title && (
 | 
				
			||||||
 | 
					              <link rel="stylesheet" href={googleFontSubsetHref(cfg.theme, cfg.pageTitle)} />
 | 
				
			||||||
 | 
					            )}
 | 
				
			||||||
          </>
 | 
					          </>
 | 
				
			||||||
        )}
 | 
					        )}
 | 
				
			||||||
        <link rel="preconnect" href="https://cdnjs.cloudflare.com" crossOrigin="anonymous" />
 | 
					        <link rel="preconnect" href="https://cdnjs.cloudflare.com" crossOrigin="anonymous" />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,6 +17,7 @@ PageTitle.css = `
 | 
				
			|||||||
.page-title {
 | 
					.page-title {
 | 
				
			||||||
  font-size: 1.75rem;
 | 
					  font-size: 1.75rem;
 | 
				
			||||||
  margin: 0;
 | 
					  margin: 0;
 | 
				
			||||||
 | 
					  font-family: var(--titleFont);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
`
 | 
					`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,12 @@ import styles from "../../styles/custom.scss"
 | 
				
			|||||||
import popoverStyle from "../../components/styles/popover.scss"
 | 
					import popoverStyle from "../../components/styles/popover.scss"
 | 
				
			||||||
import { BuildCtx } from "../../util/ctx"
 | 
					import { BuildCtx } from "../../util/ctx"
 | 
				
			||||||
import { QuartzComponent } from "../../components/types"
 | 
					import { QuartzComponent } from "../../components/types"
 | 
				
			||||||
import { googleFontHref, joinStyles, processGoogleFonts } from "../../util/theme"
 | 
					import {
 | 
				
			||||||
 | 
					  googleFontHref,
 | 
				
			||||||
 | 
					  googleFontSubsetHref,
 | 
				
			||||||
 | 
					  joinStyles,
 | 
				
			||||||
 | 
					  processGoogleFonts,
 | 
				
			||||||
 | 
					} from "../../util/theme"
 | 
				
			||||||
import { Features, transform } from "lightningcss"
 | 
					import { Features, transform } from "lightningcss"
 | 
				
			||||||
import { transform as transpile } from "esbuild"
 | 
					import { transform as transpile } from "esbuild"
 | 
				
			||||||
import { write } from "./helpers"
 | 
					import { write } from "./helpers"
 | 
				
			||||||
@@ -211,9 +216,16 @@ export const ComponentResources: QuartzEmitterPlugin = () => {
 | 
				
			|||||||
        // let the user do it themselves in css
 | 
					        // let the user do it themselves in css
 | 
				
			||||||
      } else if (cfg.theme.fontOrigin === "googleFonts" && !cfg.theme.cdnCaching) {
 | 
					      } else if (cfg.theme.fontOrigin === "googleFonts" && !cfg.theme.cdnCaching) {
 | 
				
			||||||
        // when cdnCaching is true, we link to google fonts in Head.tsx
 | 
					        // when cdnCaching is true, we link to google fonts in Head.tsx
 | 
				
			||||||
        const response = await fetch(googleFontHref(ctx.cfg.configuration.theme))
 | 
					        const theme = ctx.cfg.configuration.theme
 | 
				
			||||||
 | 
					        const response = await fetch(googleFontHref(theme))
 | 
				
			||||||
        googleFontsStyleSheet = await response.text()
 | 
					        googleFontsStyleSheet = await response.text()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (theme.typography.title) {
 | 
				
			||||||
 | 
					          const title = ctx.cfg.configuration.pageTitle
 | 
				
			||||||
 | 
					          const response = await fetch(googleFontSubsetHref(theme, title))
 | 
				
			||||||
 | 
					          googleFontsStyleSheet += `\n${await response.text()}`
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!cfg.baseUrl) {
 | 
					        if (!cfg.baseUrl) {
 | 
				
			||||||
          throw new Error(
 | 
					          throw new Error(
 | 
				
			||||||
            "baseUrl must be defined when using Google Fonts without cfg.theme.cdnCaching",
 | 
					            "baseUrl must be defined when using Google Fonts without cfg.theme.cdnCaching",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,6 +25,7 @@ export type FontSpecification =
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export interface Theme {
 | 
					export interface Theme {
 | 
				
			||||||
  typography: {
 | 
					  typography: {
 | 
				
			||||||
 | 
					    title?: FontSpecification
 | 
				
			||||||
    header: FontSpecification
 | 
					    header: FontSpecification
 | 
				
			||||||
    body: FontSpecification
 | 
					    body: FontSpecification
 | 
				
			||||||
    code: FontSpecification
 | 
					    code: FontSpecification
 | 
				
			||||||
@@ -48,7 +49,10 @@ export function getFontSpecificationName(spec: FontSpecification): string {
 | 
				
			|||||||
  return spec.name
 | 
					  return spec.name
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function formatFontSpecification(type: "header" | "body" | "code", spec: FontSpecification) {
 | 
					function formatFontSpecification(
 | 
				
			||||||
 | 
					  type: "title" | "header" | "body" | "code",
 | 
				
			||||||
 | 
					  spec: FontSpecification,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
  if (typeof spec === "string") {
 | 
					  if (typeof spec === "string") {
 | 
				
			||||||
    spec = { name: spec }
 | 
					    spec = { name: spec }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -82,12 +86,19 @@ function formatFontSpecification(type: "header" | "body" | "code", spec: FontSpe
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function googleFontHref(theme: Theme) {
 | 
					export function googleFontHref(theme: Theme) {
 | 
				
			||||||
  const { code, header, body } = theme.typography
 | 
					  const { header, body, code } = theme.typography
 | 
				
			||||||
  const headerFont = formatFontSpecification("header", header)
 | 
					  const headerFont = formatFontSpecification("header", header)
 | 
				
			||||||
  const bodyFont = formatFontSpecification("body", body)
 | 
					  const bodyFont = formatFontSpecification("body", body)
 | 
				
			||||||
  const codeFont = formatFontSpecification("code", code)
 | 
					  const codeFont = formatFontSpecification("code", code)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return `https://fonts.googleapis.com/css2?family=${bodyFont}&family=${headerFont}&family=${codeFont}&display=swap`
 | 
					  return `https://fonts.googleapis.com/css2?family=${headerFont}&family=${bodyFont}&family=${codeFont}&display=swap`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function googleFontSubsetHref(theme: Theme, text: string) {
 | 
				
			||||||
 | 
					  const title = theme.typography.title || theme.typography.header
 | 
				
			||||||
 | 
					  const titleFont = formatFontSpecification("title", title)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return `https://fonts.googleapis.com/css2?family=${titleFont}&text=${encodeURIComponent(text)}&display=swap`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface GoogleFontFile {
 | 
					export interface GoogleFontFile {
 | 
				
			||||||
@@ -135,6 +146,7 @@ ${stylesheet.join("\n\n")}
 | 
				
			|||||||
  --highlight: ${theme.colors.lightMode.highlight};
 | 
					  --highlight: ${theme.colors.lightMode.highlight};
 | 
				
			||||||
  --textHighlight: ${theme.colors.lightMode.textHighlight};
 | 
					  --textHighlight: ${theme.colors.lightMode.textHighlight};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  --titleFont: "${getFontSpecificationName(theme.typography.title || theme.typography.header)}", ${DEFAULT_SANS_SERIF};
 | 
				
			||||||
  --headerFont: "${getFontSpecificationName(theme.typography.header)}", ${DEFAULT_SANS_SERIF};
 | 
					  --headerFont: "${getFontSpecificationName(theme.typography.header)}", ${DEFAULT_SANS_SERIF};
 | 
				
			||||||
  --bodyFont: "${getFontSpecificationName(theme.typography.body)}", ${DEFAULT_SANS_SERIF};
 | 
					  --bodyFont: "${getFontSpecificationName(theme.typography.body)}", ${DEFAULT_SANS_SERIF};
 | 
				
			||||||
  --codeFont: "${getFontSpecificationName(theme.typography.code)}", ${DEFAULT_MONO};
 | 
					  --codeFont: "${getFontSpecificationName(theme.typography.code)}", ${DEFAULT_MONO};
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user