mirror of
https://github.com/quartz-community/fonts.git
synced 2026-07-22 03:00:28 +00:00
chore: fonts
This commit is contained in:
parent
fb6ece0478
commit
8be5be706d
14 changed files with 101 additions and 101 deletions
36
README.md
36
README.md
|
|
@ -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
6
dist/emitter.d.ts
vendored
|
|
@ -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
12
dist/emitter.js
vendored
|
|
@ -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
2
dist/emitter.js.map
vendored
File diff suppressed because one or more lines are too long
8
dist/index.d.ts
vendored
8
dist/index.d.ts
vendored
|
|
@ -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
20
dist/index.js
vendored
|
|
@ -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
2
dist/index.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/types.d.ts
vendored
4
dist/types.d.ts
vendored
|
|
@ -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 };
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ export type FontSpecification =
|
|||
includeItalic?: boolean;
|
||||
};
|
||||
|
||||
export interface QuartzFontsOptions {
|
||||
export interface FontsOptions {
|
||||
title?: FontSpecification;
|
||||
body?: FontSpecification;
|
||||
header?: FontSpecification;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in a new issue