1
0
mirror of https://gitlab.com/MisterBiggs/brain-quartz.git synced 2025-09-12 00:35:07 +00:00
Files
.github
content
docs
quartz
cli
components
i18n
plugins
processors
static
styles
util
clone.ts
ctx.ts
emoji.ts
emojimap.json
escape.ts
fileTrie.test.ts
fileTrie.ts
glob.ts
jsx.tsx
lang.ts
log.ts
og.tsx
path.test.ts
path.ts
perf.ts
random.ts
resources.tsx
sourcemap.ts
theme.ts
trace.ts
bootstrap-cli.mjs
bootstrap-worker.mjs
build.ts
cfg.ts
worker.ts
.gitattributes
.gitignore
.node-version
.npmrc
.prettierignore
.prettierrc
CODE_OF_CONDUCT.md
Dockerfile
LICENSE.txt
README.md
globals.d.ts
index.d.ts
package-lock.json
package.json
quartz.config.ts
quartz.layout.ts
tsconfig.json
brain-quartz/quartz/util/theme.ts
Felix Nie 25979ab216 feat(fonts): allow PageTitle to have its own font subset ()
* 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
2025-03-18 21:43:32 -07:00

168 lines
4.7 KiB
TypeScript

export interface ColorScheme {
light: string
lightgray: string
gray: string
darkgray: string
dark: string
secondary: string
tertiary: string
highlight: string
textHighlight: string
}
interface Colors {
lightMode: ColorScheme
darkMode: ColorScheme
}
export type FontSpecification =
| string
| {
name: string
weights?: number[]
includeItalic?: boolean
}
export interface Theme {
typography: {
title?: FontSpecification
header: FontSpecification
body: FontSpecification
code: FontSpecification
}
cdnCaching: boolean
colors: Colors
fontOrigin: "googleFonts" | "local"
}
export type ThemeKey = keyof Colors
const DEFAULT_SANS_SERIF =
'system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"'
const DEFAULT_MONO = "ui-monospace, SFMono-Regular, SF Mono, Menlo, monospace"
export function getFontSpecificationName(spec: FontSpecification): string {
if (typeof spec === "string") {
return spec
}
return spec.name
}
function formatFontSpecification(
type: "title" | "header" | "body" | "code",
spec: FontSpecification,
) {
if (typeof spec === "string") {
spec = { name: spec }
}
const defaultIncludeWeights = type === "header" ? [400, 700] : [400, 600]
const defaultIncludeItalic = type === "body"
const weights = spec.weights ?? defaultIncludeWeights
const italic = spec.includeItalic ?? defaultIncludeItalic
const features: string[] = []
if (italic) {
features.push("ital")
}
if (weights.length > 1) {
const weightSpec = italic
? weights
.flatMap((w) => [`0,${w}`, `1,${w}`])
.sort()
.join(";")
: weights.join(";")
features.push(`wght@${weightSpec}`)
}
if (features.length > 0) {
return `${spec.name}:${features.join(",")}`
}
return spec.name
}
export function googleFontHref(theme: Theme) {
const { header, body, code } = theme.typography
const headerFont = formatFontSpecification("header", header)
const bodyFont = formatFontSpecification("body", body)
const codeFont = formatFontSpecification("code", code)
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 {
url: string
filename: string
extension: string
}
export async function processGoogleFonts(
stylesheet: string,
baseUrl: string,
): Promise<{
processedStylesheet: string
fontFiles: GoogleFontFile[]
}> {
const fontSourceRegex = /url\((https:\/\/fonts.gstatic.com\/s\/[^)]+\.(woff2|ttf))\)/g
const fontFiles: GoogleFontFile[] = []
let processedStylesheet = stylesheet
let match
while ((match = fontSourceRegex.exec(stylesheet)) !== null) {
const url = match[1]
const [filename, extension] = url.split("/").pop()!.split(".")
const staticUrl = `https://${baseUrl}/static/fonts/${filename}.${extension}`
processedStylesheet = processedStylesheet.replace(url, staticUrl)
fontFiles.push({ url, filename, extension })
}
return { processedStylesheet, fontFiles }
}
export function joinStyles(theme: Theme, ...stylesheet: string[]) {
return `
${stylesheet.join("\n\n")}
:root {
--light: ${theme.colors.lightMode.light};
--lightgray: ${theme.colors.lightMode.lightgray};
--gray: ${theme.colors.lightMode.gray};
--darkgray: ${theme.colors.lightMode.darkgray};
--dark: ${theme.colors.lightMode.dark};
--secondary: ${theme.colors.lightMode.secondary};
--tertiary: ${theme.colors.lightMode.tertiary};
--highlight: ${theme.colors.lightMode.highlight};
--textHighlight: ${theme.colors.lightMode.textHighlight};
--titleFont: "${getFontSpecificationName(theme.typography.title || theme.typography.header)}", ${DEFAULT_SANS_SERIF};
--headerFont: "${getFontSpecificationName(theme.typography.header)}", ${DEFAULT_SANS_SERIF};
--bodyFont: "${getFontSpecificationName(theme.typography.body)}", ${DEFAULT_SANS_SERIF};
--codeFont: "${getFontSpecificationName(theme.typography.code)}", ${DEFAULT_MONO};
}
:root[saved-theme="dark"] {
--light: ${theme.colors.darkMode.light};
--lightgray: ${theme.colors.darkMode.lightgray};
--gray: ${theme.colors.darkMode.gray};
--darkgray: ${theme.colors.darkMode.darkgray};
--dark: ${theme.colors.darkMode.dark};
--secondary: ${theme.colors.darkMode.secondary};
--tertiary: ${theme.colors.darkMode.tertiary};
--highlight: ${theme.colors.darkMode.highlight};
--textHighlight: ${theme.colors.darkMode.textHighlight};
}
`
}