chore: fonts

This commit is contained in:
saberzero1 2026-06-10 23:13:02 +02:00
parent fb6ece0478
commit 8be5be706d
No known key found for this signature in database
14 changed files with 101 additions and 101 deletions

View file

@ -1,18 +1,18 @@
# @quartz-community/quartz-fonts
# @quartz-community/fonts
Fine-grained font control for Quartz sites. Supports per-heading fonts, automatic theme font discovery via [QuartzTheme](https://github.com/saberzero1/quartz-themes), and Obsidian-compatible defaults.
## Installation
```bash
npx quartz plugin add github:quartz-community/quartz-fonts
npx quartz plugin add github:quartz-community/fonts
```
## Usage
```yaml title="quartz.config.yaml"
plugins:
- source: github:quartz-community/quartz-fonts
- source: github:quartz-community/fonts
enabled: true
```
@ -20,7 +20,7 @@ With custom fonts:
```yaml title="quartz.config.yaml"
plugins:
- source: github:quartz-community/quartz-fonts
- source: github:quartz-community/fonts
enabled: true
options:
body: '"Inter", sans-serif'
@ -62,7 +62,7 @@ body:
### Default options
```yaml title="quartz.config.yaml"
- source: github:quartz-community/quartz-fonts
- source: github:quartz-community/fonts
enabled: true
options:
useThemeFonts: true
@ -71,7 +71,7 @@ body:
## How it works
QuartzFonts resolves fonts using a priority chain:
Fonts resolves fonts using a priority chain:
```
User config (plugin options)
@ -93,29 +93,29 @@ title option -> header option -> theme font -> Obsidian default
### With QuartzTheme
When [QuartzTheme](https://github.com/saberzero1/quartz-themes) is installed and runs before QuartzFonts, theme fonts are automatically discovered and used as defaults. Any options you set in QuartzFonts will override the theme fonts.
When [QuartzTheme](https://github.com/saberzero1/quartz-themes) is installed and runs before Fonts, theme fonts are automatically discovered and used as defaults. Any options you set in Fonts will override the theme fonts.
QuartzFonts must run after QuartzTheme. This is handled automatically by `defaultOrder` (QuartzTheme = 50, QuartzFonts = 60).
Fonts must run after QuartzTheme. This is handled automatically by `defaultOrder` (QuartzTheme = 50, Fonts = 60).
### Without QuartzTheme
QuartzFonts works standalone. Without a theme, it falls back to Obsidian's default system font stacks.
Fonts works standalone. Without a theme, it falls back to Obsidian's default system font stacks.
## Examples
```yaml title="quartz.config.yaml"
# Use theme fonts automatically (default behavior)
- source: github:quartz-community/quartz-fonts
- source: github:quartz-community/fonts
enabled: true
# Override just the heading font
- source: github:quartz-community/quartz-fonts
- source: github:quartz-community/fonts
enabled: true
options:
header: '"Playfair Display", serif'
# Full control with per-heading fonts
- source: github:quartz-community/quartz-fonts
- source: github:quartz-community/fonts
enabled: true
options:
body: '"Inter", sans-serif'
@ -125,7 +125,7 @@ QuartzFonts works standalone. Without a theme, it falls back to Obsidian's defau
h2: '"Lora", serif'
# Load from Google Fonts automatically
- source: github:quartz-community/quartz-fonts
- source: github:quartz-community/fonts
enabled: true
options:
fontOrigin: googleFonts
@ -134,7 +134,7 @@ QuartzFonts works standalone. Without a theme, it falls back to Obsidian's defau
code: JetBrains Mono
# Google Fonts with weight/italic control
- source: github:quartz-community/quartz-fonts
- source: github:quartz-community/fonts
enabled: true
options:
fontOrigin: googleFonts
@ -150,7 +150,7 @@ QuartzFonts works standalone. Without a theme, it falls back to Obsidian's defau
weights: [400]
# Custom title font (separate from header)
- source: github:quartz-community/quartz-fonts
- source: github:quartz-community/fonts
enabled: true
options:
fontOrigin: googleFonts
@ -160,7 +160,7 @@ QuartzFonts works standalone. Without a theme, it falls back to Obsidian's defau
code: JetBrains Mono
# Ignore theme fonts entirely
- source: github:quartz-community/quartz-fonts
- source: github:quartz-community/fonts
enabled: true
options:
useThemeFonts: false
@ -169,7 +169,7 @@ QuartzFonts works standalone. Without a theme, it falls back to Obsidian's defau
## Google Fonts Validation
When `fontOrigin: googleFonts` is set and the optional [`google-font-metadata`](https://www.npmjs.com/package/google-font-metadata) package is installed, QuartzFonts validates your font configuration at build time:
When `fontOrigin: googleFonts` is set and the optional [`google-font-metadata`](https://www.npmjs.com/package/google-font-metadata) package is installed, Fonts validates your font configuration at build time:
- Checks that font family names exist in Google Fonts.
- Warns if requested weights are not available for a font.
@ -185,7 +185,7 @@ Validation warnings are logged to the console but do not block the build.
## Documentation
See the [Quartz documentation](https://quartz.jzhao.xyz/plugins/QuartzFonts) for more information.
See the [Quartz documentation](https://quartz.jzhao.xyz/plugins/Fonts) for more information.
## License

6
dist/emitter.d.ts vendored
View file

@ -1,6 +1,6 @@
import { QuartzEmitterPlugin } from '@quartz-community/types';
import { QuartzFontsOptions } from './types.js';
import { FontsOptions } from './types.js';
declare const QuartzFontsEmitter: QuartzEmitterPlugin<Partial<QuartzFontsOptions>>;
declare const FontsEmitter: QuartzEmitterPlugin<Partial<FontsOptions>>;
export { QuartzFontsEmitter };
export { FontsEmitter };

12
dist/emitter.js vendored
View file

@ -92,17 +92,17 @@ var defaultOptions = {
useThemeFonts: true,
fontOrigin: "googleFonts"
};
var QuartzFontsEmitter = (userOptions) => {
var FontsEmitter = (userOptions) => {
const options = { ...defaultOptions, ...userOptions };
return {
name: "QuartzFontsEmitter",
name: "FontsEmitter",
async *emit(ctx, _content, _resources) {
if (options.fontOrigin !== "selfHosted") {
return;
}
const baseUrl = ctx.cfg.configuration.baseUrl;
if (!baseUrl) {
throw new Error("[QuartzFontsEmitter] baseUrl is required for selfHosted fonts.");
throw new Error("[FontsEmitter] baseUrl is required for selfHosted fonts.");
}
const headerSpec = options.header ?? QUARTZ_DEFAULT_HEADER;
const bodySpec = options.body ?? QUARTZ_DEFAULT_BODY;
@ -116,7 +116,7 @@ var QuartzFontsEmitter = (userOptions) => {
const cssResponse = await fetch(href);
if (!cssResponse.ok) {
throw new Error(
`[QuartzFontsEmitter] Failed to fetch Google Fonts CSS: ${cssResponse.status} ${cssResponse.statusText}`
`[FontsEmitter] Failed to fetch Google Fonts CSS: ${cssResponse.status} ${cssResponse.statusText}`
);
}
const cssText = await cssResponse.text();
@ -127,7 +127,7 @@ var QuartzFontsEmitter = (userOptions) => {
const fontResponse = await fetch(fontFile.url);
if (!fontResponse.ok) {
throw new Error(
`[QuartzFontsEmitter] Failed to fetch font file: ${fontFile.url} (${fontResponse.status} ${fontResponse.statusText})`
`[FontsEmitter] Failed to fetch font file: ${fontFile.url} (${fontResponse.status} ${fontResponse.statusText})`
);
}
const buf = await fontResponse.arrayBuffer();
@ -142,6 +142,6 @@ var QuartzFontsEmitter = (userOptions) => {
};
};
export { QuartzFontsEmitter };
export { FontsEmitter };
//# sourceMappingURL=emitter.js.map
//# sourceMappingURL=emitter.js.map

2
dist/emitter.js.map vendored

File diff suppressed because one or more lines are too long

8
dist/index.d.ts vendored
View file

@ -1,9 +1,9 @@
import { QuartzTransformerPlugin } from '@quartz-community/types';
export { QuartzEmitterPlugin, QuartzTransformerPlugin } from '@quartz-community/types';
import { QuartzFontsOptions } from './types.js';
import { FontsOptions } from './types.js';
export { FontFileEntry, FontSpecification, GoogleFontFile, ProcessedFontResult, QuartzFontRegistry } from './types.js';
export { QuartzFontsEmitter } from './emitter.js';
export { FontsEmitter } from './emitter.js';
declare const QuartzFonts: QuartzTransformerPlugin<Partial<QuartzFontsOptions>>;
declare const Fonts: QuartzTransformerPlugin<Partial<FontsOptions>>;
export { QuartzFonts, QuartzFontsOptions };
export { Fonts, FontsOptions };

20
dist/index.js vendored
View file

@ -177,7 +177,7 @@ function resolveFonts(options) {
if (options.useThemeFonts !== false && !registry) {
if (isQuartzThemeEnabled()) {
console.warn(
"[QuartzFonts] QuartzTheme is enabled but its font registry is empty. Ensure QuartzTheme runs before QuartzFonts (lower defaultOrder number). Falling back to Obsidian defaults."
"[Fonts] QuartzTheme is enabled but its font registry is empty. Ensure QuartzTheme runs before Fonts (lower defaultOrder number). Falling back to Obsidian defaults."
);
}
}
@ -251,7 +251,7 @@ function runValidation(options) {
for (const { role, spec, fontRole } of specs) {
const warnings = validateFontSpec(role, spec, fontRole);
for (const w2 of warnings) {
console.warn(`[QuartzFonts] ${w2.role}: ${w2.message}`);
console.warn(`[Fonts] ${w2.role}: ${w2.message}`);
}
}
}
@ -302,13 +302,13 @@ function buildGoogleFontsHead(options) {
_("link", { rel: "stylesheet", href })
];
}
var QuartzFonts = (userOptions) => {
var Fonts = (userOptions) => {
const options = { ...defaultOptions, ...userOptions };
if (options.fontOrigin === "googleFonts") {
runValidation(options);
}
return {
name: "QuartzFonts",
name: "Fonts",
textTransform(_ctx, src) {
return src;
},
@ -359,17 +359,17 @@ var defaultOptions2 = {
useThemeFonts: true,
fontOrigin: "googleFonts"
};
var QuartzFontsEmitter = (userOptions) => {
var FontsEmitter = (userOptions) => {
const options = { ...defaultOptions2, ...userOptions };
return {
name: "QuartzFontsEmitter",
name: "FontsEmitter",
async *emit(ctx, _content, _resources) {
if (options.fontOrigin !== "selfHosted") {
return;
}
const baseUrl = ctx.cfg.configuration.baseUrl;
if (!baseUrl) {
throw new Error("[QuartzFontsEmitter] baseUrl is required for selfHosted fonts.");
throw new Error("[FontsEmitter] baseUrl is required for selfHosted fonts.");
}
const headerSpec = options.header ?? QUARTZ_DEFAULT_HEADER2;
const bodySpec = options.body ?? QUARTZ_DEFAULT_BODY2;
@ -383,7 +383,7 @@ var QuartzFontsEmitter = (userOptions) => {
const cssResponse = await fetch(href);
if (!cssResponse.ok) {
throw new Error(
`[QuartzFontsEmitter] Failed to fetch Google Fonts CSS: ${cssResponse.status} ${cssResponse.statusText}`
`[FontsEmitter] Failed to fetch Google Fonts CSS: ${cssResponse.status} ${cssResponse.statusText}`
);
}
const cssText = await cssResponse.text();
@ -394,7 +394,7 @@ var QuartzFontsEmitter = (userOptions) => {
const fontResponse = await fetch(fontFile.url);
if (!fontResponse.ok) {
throw new Error(
`[QuartzFontsEmitter] Failed to fetch font file: ${fontFile.url} (${fontResponse.status} ${fontResponse.statusText})`
`[FontsEmitter] Failed to fetch font file: ${fontFile.url} (${fontResponse.status} ${fontResponse.statusText})`
);
}
const buf = await fontResponse.arrayBuffer();
@ -409,6 +409,6 @@ var QuartzFontsEmitter = (userOptions) => {
};
};
export { QuartzFonts, QuartzFontsEmitter };
export { Fonts, FontsEmitter };
//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

4
dist/types.d.ts vendored
View file

@ -9,7 +9,7 @@ type FontSpecification = string | {
weights?: number[];
includeItalic?: boolean;
};
interface QuartzFontsOptions {
interface FontsOptions {
title?: FontSpecification;
body?: FontSpecification;
header?: FontSpecification;
@ -55,4 +55,4 @@ interface FontFileEntry {
unicodeRange?: string | null;
}
export type { FontFileEntry, FontSpecification, GoogleFontFile, ProcessedFontResult, QuartzFontRegistry, QuartzFontsOptions };
export type { FontFileEntry, FontSpecification, FontsOptions, GoogleFontFile, ProcessedFontResult, QuartzFontRegistry };

View file

@ -8,7 +8,7 @@
"homepage": "https://quartz.jzhao.xyz",
"repository": {
"type": "git",
"url": "https://github.com/quartz-community/quartz-fonts"
"url": "https://github.com/quartz-community/fonts"
},
"keywords": [
"quartz",
@ -86,8 +86,8 @@
"vitest": "^2.1.9"
},
"quartz": {
"name": "quartz-fonts",
"displayName": "Quartz Fonts",
"name": "fonts",
"displayName": "Fonts",
"category": [
"transformer",
"emitter"

View file

@ -1,7 +1,7 @@
import fs from "fs";
import path from "path";
import type { QuartzEmitterPlugin, BuildCtx, FilePath } from "@quartz-community/types";
import type { QuartzFontsOptions } from "./types";
import type { FontsOptions } from "./types";
import { googleFontHref } from "./util/google-fonts";
import { processGoogleFonts } from "./util/process-fonts";
@ -9,18 +9,18 @@ const QUARTZ_DEFAULT_HEADER = "Schibsted Grotesk";
const QUARTZ_DEFAULT_BODY = "Source Sans Pro";
const QUARTZ_DEFAULT_CODE = "IBM Plex Mono";
const defaultOptions: QuartzFontsOptions = {
const defaultOptions: FontsOptions = {
useThemeFonts: true,
fontOrigin: "googleFonts",
};
export const QuartzFontsEmitter: QuartzEmitterPlugin<Partial<QuartzFontsOptions>> = (
userOptions?: Partial<QuartzFontsOptions>,
export const FontsEmitter: QuartzEmitterPlugin<Partial<FontsOptions>> = (
userOptions?: Partial<FontsOptions>,
) => {
const options: QuartzFontsOptions = { ...defaultOptions, ...userOptions };
const options: FontsOptions = { ...defaultOptions, ...userOptions };
return {
name: "QuartzFontsEmitter",
name: "FontsEmitter",
async *emit(ctx: BuildCtx, _content: unknown[], _resources: unknown): AsyncGenerator<FilePath> {
if (options.fontOrigin !== "selfHosted") {
return;
@ -28,7 +28,7 @@ export const QuartzFontsEmitter: QuartzEmitterPlugin<Partial<QuartzFontsOptions>
const baseUrl = ctx.cfg.configuration.baseUrl;
if (!baseUrl) {
throw new Error("[QuartzFontsEmitter] baseUrl is required for selfHosted fonts.");
throw new Error("[FontsEmitter] baseUrl is required for selfHosted fonts.");
}
const headerSpec = options.header ?? QUARTZ_DEFAULT_HEADER;
@ -45,7 +45,7 @@ export const QuartzFontsEmitter: QuartzEmitterPlugin<Partial<QuartzFontsOptions>
const cssResponse = await fetch(href);
if (!cssResponse.ok) {
throw new Error(
`[QuartzFontsEmitter] Failed to fetch Google Fonts CSS: ${cssResponse.status} ${cssResponse.statusText}`,
`[FontsEmitter] Failed to fetch Google Fonts CSS: ${cssResponse.status} ${cssResponse.statusText}`,
);
}
const cssText = await cssResponse.text();
@ -59,7 +59,7 @@ export const QuartzFontsEmitter: QuartzEmitterPlugin<Partial<QuartzFontsOptions>
const fontResponse = await fetch(fontFile.url);
if (!fontResponse.ok) {
throw new Error(
`[QuartzFontsEmitter] Failed to fetch font file: ${fontFile.url} (${fontResponse.status} ${fontResponse.statusText})`,
`[FontsEmitter] Failed to fetch font file: ${fontFile.url} (${fontResponse.status} ${fontResponse.statusText})`,
);
}
const buf = await fontResponse.arrayBuffer();

View file

@ -1,9 +1,9 @@
export { QuartzFonts } from "./transformer";
export { QuartzFontsEmitter } from "./emitter";
export { Fonts } from "./transformer";
export { FontsEmitter } from "./emitter";
export type {
FontSpecification,
QuartzFontsOptions,
FontsOptions,
QuartzFontRegistry,
FontFileEntry,
ProcessedFontResult,

View file

@ -1,6 +1,6 @@
import { h } from "preact";
import type { QuartzTransformerPlugin, CSSResource } from "@quartz-community/types";
import type { FontSpecification, QuartzFontsOptions } from "./types";
import type { FontSpecification, FontsOptions } from "./types";
import { OBSIDIAN_SANS_STACK, OBSIDIAN_MONO_STACK } from "./defaults";
import { readFontRegistry, isQuartzThemeEnabled } from "./util/registry";
import { googleFontHref } from "./util/google-fonts";
@ -10,7 +10,7 @@ const QUARTZ_DEFAULT_HEADER = "Schibsted Grotesk";
const QUARTZ_DEFAULT_BODY = "Source Sans Pro";
const QUARTZ_DEFAULT_CODE = "IBM Plex Mono";
const defaultOptions: QuartzFontsOptions = {
const defaultOptions: FontsOptions = {
useThemeFonts: true,
fontOrigin: "googleFonts",
};
@ -34,14 +34,14 @@ interface ResolvedFonts {
h6: string;
}
function resolveFonts(options: QuartzFontsOptions): ResolvedFonts {
function resolveFonts(options: FontsOptions): ResolvedFonts {
const registry = options.useThemeFonts !== false ? readFontRegistry() : undefined;
if (options.useThemeFonts !== false && !registry) {
if (isQuartzThemeEnabled()) {
console.warn(
"[QuartzFonts] QuartzTheme is enabled but its font registry is empty. " +
"Ensure QuartzTheme runs before QuartzFonts (lower defaultOrder number). " +
"[Fonts] QuartzTheme is enabled but its font registry is empty. " +
"Ensure QuartzTheme runs before Fonts (lower defaultOrder number). " +
"Falling back to Obsidian defaults.",
);
}
@ -86,7 +86,7 @@ function resolveFonts(options: QuartzFontsOptions): ResolvedFonts {
const title = resolve(options.title, undefined, header);
const resolveHeading = (level: 1 | 2 | 3 | 4 | 5 | 6): string => {
const levelKey = `h${level}` as keyof QuartzFontsOptions;
const levelKey = `h${level}` as keyof FontsOptions;
const themeVarKey = `--h${level}-font`;
const userSpec = options[levelKey] as FontSpecification | undefined;
@ -112,7 +112,7 @@ function resolveFonts(options: QuartzFontsOptions): ResolvedFonts {
};
}
function runValidation(options: QuartzFontsOptions): void {
function runValidation(options: FontsOptions): void {
const specs: Array<{
role: string;
spec: FontSpecification;
@ -133,7 +133,7 @@ function runValidation(options: QuartzFontsOptions): void {
for (const { role, spec, fontRole } of specs) {
const warnings = validateFontSpec(role, spec, fontRole);
for (const w of warnings) {
console.warn(`[QuartzFonts] ${w.role}: ${w.message}`);
console.warn(`[Fonts] ${w.role}: ${w.message}`);
}
}
}
@ -171,7 +171,7 @@ function buildUnlayeredCSS(fonts: ResolvedFonts): string {
].join("\n");
}
function buildGoogleFontsHead(options: QuartzFontsOptions): unknown[] {
function buildGoogleFontsHead(options: FontsOptions): unknown[] {
const headerSpec = options.header ?? QUARTZ_DEFAULT_HEADER;
const bodySpec = options.body ?? QUARTZ_DEFAULT_BODY;
const codeSpec = options.code ?? QUARTZ_DEFAULT_CODE;
@ -190,17 +190,17 @@ function buildGoogleFontsHead(options: QuartzFontsOptions): unknown[] {
];
}
export const QuartzFonts: QuartzTransformerPlugin<Partial<QuartzFontsOptions>> = (
userOptions?: Partial<QuartzFontsOptions>,
export const Fonts: QuartzTransformerPlugin<Partial<FontsOptions>> = (
userOptions?: Partial<FontsOptions>,
) => {
const options: QuartzFontsOptions = { ...defaultOptions, ...userOptions };
const options: FontsOptions = { ...defaultOptions, ...userOptions };
if (options.fontOrigin === "googleFonts") {
runValidation(options);
}
return {
name: "QuartzFonts",
name: "Fonts",
textTransform(_ctx, src) {
return src;
},

View file

@ -19,7 +19,7 @@ export type FontSpecification =
includeItalic?: boolean;
};
export interface QuartzFontsOptions {
export interface FontsOptions {
title?: FontSpecification;
body?: FontSpecification;
header?: FontSpecification;

View file

@ -1,6 +1,6 @@
import { describe, expect, it, beforeEach, afterEach } from "vitest";
import type { BuildCtx } from "@quartz-community/types";
import { QuartzFonts } from "../src/transformer";
import { Fonts } from "../src/transformer";
import { formatFontSpecification, googleFontHref, getFontName } from "../src/util/google-fonts";
import { processGoogleFonts } from "../src/util/process-fonts";
@ -17,24 +17,24 @@ function clearRegistry() {
delete (globalThis as Record<string, unknown>)[REGISTRY_KEY];
}
function getCSSContent(plugin: ReturnType<typeof QuartzFonts>): string {
function getCSSContent(plugin: ReturnType<typeof Fonts>): string {
const resources = plugin.externalResources!(mockCtx);
return (resources?.css ?? []).map((r) => ("content" in r ? r.content : "")).join("\n");
}
function getAdditionalHead(plugin: ReturnType<typeof QuartzFonts>): unknown[] {
function getAdditionalHead(plugin: ReturnType<typeof Fonts>): unknown[] {
const resources = plugin.externalResources!(mockCtx);
return (resources as { additionalHead?: unknown[] })?.additionalHead ?? [];
}
describe("QuartzFonts", () => {
describe("Fonts", () => {
afterEach(() => {
clearRegistry();
});
describe("standalone (no QuartzTheme)", () => {
it("uses Quartz defaults when no options provided", () => {
const css = getCSSContent(QuartzFonts());
const css = getCSSContent(Fonts());
expect(css).toContain("--bodyFont: Source Sans Pro");
expect(css).toContain("--codeFont: IBM Plex Mono");
@ -42,14 +42,14 @@ describe("QuartzFonts", () => {
});
it("applies user-provided body font", () => {
const css = getCSSContent(QuartzFonts({ body: '"Inter", sans-serif' }));
const css = getCSSContent(Fonts({ body: '"Inter", sans-serif' }));
expect(css).toContain('--bodyFont: "Inter", sans-serif');
expect(css).toContain('--font-text: "Inter", sans-serif');
});
it("applies user-provided header font to all headings", () => {
const css = getCSSContent(QuartzFonts({ header: '"Playfair Display", serif' }));
const css = getCSSContent(Fonts({ header: '"Playfair Display", serif' }));
expect(css).toContain('--h1-font: "Playfair Display", serif');
expect(css).toContain('--h6-font: "Playfair Display", serif');
@ -57,7 +57,7 @@ describe("QuartzFonts", () => {
it("applies per-heading overrides", () => {
const css = getCSSContent(
QuartzFonts({
Fonts({
header: '"Lora", serif',
h1: '"Playfair Display", serif',
h3: '"Inter", sans-serif',
@ -70,7 +70,7 @@ describe("QuartzFonts", () => {
});
it("emits unlayered heading CSS", () => {
const plugin = QuartzFonts({ header: '"Lora", serif' });
const plugin = Fonts({ header: '"Lora", serif' });
const resources = plugin.externalResources!(mockCtx);
const unlayeredCSS = (resources?.css ?? [])
.map((r) => ("content" in r ? r.content : ""))
@ -84,25 +84,25 @@ describe("QuartzFonts", () => {
describe("title font", () => {
it("defaults title to header value", () => {
const css = getCSSContent(QuartzFonts({ header: '"Lora", serif' }));
const css = getCSSContent(Fonts({ header: '"Lora", serif' }));
expect(css).toContain('--titleFont: "Lora", serif');
});
it("defaults title to header default when no header set", () => {
const css = getCSSContent(QuartzFonts());
const css = getCSSContent(Fonts());
expect(css).toContain("--titleFont: Schibsted Grotesk");
});
it("applies explicit title font", () => {
const css = getCSSContent(
QuartzFonts({ title: '"Abril Fatface", serif', header: '"Lora", serif' }),
Fonts({ title: '"Abril Fatface", serif', header: '"Lora", serif' }),
);
expect(css).toContain('--titleFont: "Abril Fatface", serif');
expect(css).toContain('--headerFont: "Lora", serif');
});
it("accepts FontSpecification object for title", () => {
const css = getCSSContent(QuartzFonts({ title: { name: "Abril Fatface", weights: [400] } }));
const css = getCSSContent(Fonts({ title: { name: "Abril Fatface", weights: [400] } }));
expect(css).toContain("--titleFont: Abril Fatface");
});
});
@ -110,7 +110,7 @@ describe("QuartzFonts", () => {
describe("FontSpecification object form", () => {
it("extracts font name from object spec for CSS", () => {
const css = getCSSContent(
QuartzFonts({
Fonts({
body: { name: "Inter", weights: [400, 700], includeItalic: true },
header: { name: "Playfair Display", weights: [400, 700] },
code: { name: "JetBrains Mono", weights: [400] },
@ -124,7 +124,7 @@ describe("QuartzFonts", () => {
it("accepts object form for heading overrides", () => {
const css = getCSSContent(
QuartzFonts({
Fonts({
h1: { name: "Abril Fatface", weights: [400] },
h2: '"Lora", serif',
}),
@ -137,13 +137,13 @@ describe("QuartzFonts", () => {
describe("Google Fonts loading", () => {
it("does not inject link tags when fontOrigin is local", () => {
const head = getAdditionalHead(QuartzFonts({ fontOrigin: "local", body: "Inter" }));
const head = getAdditionalHead(Fonts({ fontOrigin: "local", body: "Inter" }));
expect(head).toHaveLength(0);
});
it("injects preconnect and stylesheet VNodes for googleFonts", () => {
const head = getAdditionalHead(
QuartzFonts({
Fonts({
fontOrigin: "googleFonts",
body: "Inter",
header: "Playfair Display",
@ -170,7 +170,7 @@ describe("QuartzFonts", () => {
it("includes title font in Google Fonts URL when different from header", () => {
const head = getAdditionalHead(
QuartzFonts({
Fonts({
fontOrigin: "googleFonts",
title: "Abril Fatface",
header: "Playfair Display",
@ -186,7 +186,7 @@ describe("QuartzFonts", () => {
it("deduplicates title and header when they match", () => {
const head = getAdditionalHead(
QuartzFonts({
Fonts({
fontOrigin: "googleFonts",
title: "Inter",
header: "Inter",
@ -204,7 +204,7 @@ describe("QuartzFonts", () => {
describe("selfHosted fontOrigin", () => {
it("injects link to self-hosted CSS file instead of Google Fonts", () => {
const head = getAdditionalHead(
QuartzFonts({
Fonts({
fontOrigin: "selfHosted",
body: "Inter",
header: "Playfair Display",
@ -220,7 +220,7 @@ describe("QuartzFonts", () => {
it("still emits layered CSS with font variables", () => {
const css = getCSSContent(
QuartzFonts({
Fonts({
fontOrigin: "selfHosted",
body: "Inter",
header: "Playfair Display",
@ -247,7 +247,7 @@ describe("QuartzFonts", () => {
});
it("reads theme fonts from registry", () => {
const css = getCSSContent(QuartzFonts());
const css = getCSSContent(Fonts());
expect(css).toContain('--bodyFont: "JetBrains Mono", monospace');
expect(css).toContain('--codeFont: "Fira Code", monospace');
@ -255,14 +255,14 @@ describe("QuartzFonts", () => {
});
it("user options override theme fonts", () => {
const css = getCSSContent(QuartzFonts({ body: '"Inter", sans-serif' }));
const css = getCSSContent(Fonts({ body: '"Inter", sans-serif' }));
expect(css).toContain('--bodyFont: "Inter", sans-serif');
expect(css).toContain('--codeFont: "Fira Code", monospace');
});
it("useThemeFonts: false ignores registry", () => {
const css = getCSSContent(QuartzFonts({ useThemeFonts: false }));
const css = getCSSContent(Fonts({ useThemeFonts: false }));
expect(css).toContain("--bodyFont: Source Sans Pro");
expect(css).not.toContain("JetBrains Mono");
@ -271,12 +271,12 @@ describe("QuartzFonts", () => {
describe("plugin metadata", () => {
it("has the correct name", () => {
const plugin = QuartzFonts();
expect(plugin.name).toBe("QuartzFonts");
const plugin = Fonts();
expect(plugin.name).toBe("Fonts");
});
it("textTransform passes through source unchanged", () => {
const plugin = QuartzFonts();
const plugin = Fonts();
const input = "# Hello World\n";
const result = plugin.textTransform!(mockCtx, input);
expect(result).toBe(input);