fontaine
基于字体度量的自动字体回退
特性
- 💪 通过使用精心设计的字体度量的本地字体回退来减少 CLS。
- ✨ 自动生成字体度量和覆盖。
- ⚡️ 纯 CSS,零运行时开销。
在演示项目中,启用/禁用 fontaine
可以在渲染 /
时产生以下差异,无需任何自定义:
之前 | 之后 | |
---|---|---|
CLS | 0.24 | 0.054 |
性能 | 92 | 100 |
安装
使用 pnpm
pnpm add -D fontaine
或者,使用 npm
npm install -D fontaine
或者,使用 yarn
yarn add -D fontaine
使用方法
import { FontaineTransform } from 'fontaine'
// Astro 配置 - astro.config.mjs
import { defineConfig } from 'astro/config'
const options = {
fallbacks: ['BlinkMacSystemFont', 'Segoe UI', 'Helvetica Neue', 'Arial', 'Noto Sans'],
// 你可能需要将资源如 `/fonts/Roboto.woff2` 解析到特定目录
resolvePath: id => `file:///path/to/public/dir${id}`,
// overrideName: (originalName) => `${name} override`
// sourcemap: false
// skipFontFaceGeneration: (fallbackName) => fallbackName === 'Roboto override'
}
// Vite
export default {
plugins: [FontaineTransform.vite(options)]
}
// Next.js
export default {
webpack(config) {
config.plugins = config.plugins || []
config.plugins.push(FontaineTransform.webpack(options))
return config
},
}
// Docusaurus 插件 - 提供给 docusaurus.config.js 的 plugins 选项
// 注意:你可能需要使用 require 而不是 import 来引入 fontaine
const fontaine = require('fontaine')
function fontainePlugin(_context, _options) {
return {
name: 'fontaine-plugin',
configureWebpack(_config, _isServer) {
return {
plugins: [
fontaine.FontaineTransform.webpack(options),
],
}
},
}
}
// Gatsby 配置 - gatsby-node.js
const { FontaineTransform } = require('fontaine')
exports.onCreateWebpackConfig = ({ stage, actions, getConfig }) => {
const config = getConfig()
config.plugins.push(FontaineTransform.webpack(options))
actions.replaceWebpackConfig(config)
}
export default defineConfig({
integrations: [],
vite: {
plugins: [
FontaineTransform.vite({
fallbacks: ['Arial'],
resolvePath: id => new URL(`./public${id}`, import.meta.url), // id 是 CSS 中的字体 src 值
}),
],
},
})
注意 如果你使用 Nuxt,请查看 nuxt-font-metrics,它在底层使用了
fontaine
。
如果你的自定义字体是通过 CSS 变量机制使用的,你需要对 CSS 变量进行一些调整以帮助 fontaine。Docusaurus 就是一个例子,它使用 --ifm-font-family-base
变量来引用自定义字体。为了让 fontaine 能够将变量与字体关联起来,我们需要在该变量中添加 {字体名称} override
后缀。这看起来是什么样的?假设我们使用的自定义字体是 Poppins,它通过 --ifm-font-family-base
变量引用,我们需要进行以下调整:
:root {
/* ... */
- --ifm-font-family-base: 'Poppins';
+ --ifm-font-family-base: 'Poppins', 'Poppins override';
在幕后,fontaine 创建了一个 'Poppins override' @font-face
规则。通过手动将这个覆盖字体系列添加到我们的 CSS 变量中,我们使我们的网站使用 fontaine 生成的具有正确字体度量的回退 @font-face
规则。
工作原理
fontaine
将扫描你的 @font-face
规则并生成具有正确度量的回退规则。例如:
@font-face {
font-family: 'Roboto';
font-display: swap;
src: url('/fonts/Roboto.woff2') format('woff2'), url('/fonts/Roboto.woff')
format('woff');
font-weight: 700;
}
/* 这个额外的 font-face 声明将被添加到你的 CSS 中。 */
@font-face {
font-family: 'Roboto override';
src: local('BlinkMacSystemFont'), local('Segoe UI'), local('Helvetica Neue'),
local('Arial'), local('Noto Sans');
ascent-override: 92.7734375%;
descent-override: 24.4140625%;
line-gap-override: 0%;
}
然后,每当你使用 font-family: 'Roboto'
时,fontaine
将在 font-family 中添加覆盖:
:root {
font-family: 'Roboto';
/* 这将变成 */
font-family: 'Roboto', 'Roboto override';
}
💻 开发
- 克隆此仓库
- 使用
corepack enable
启用 Corepack(对于 Node.js < 16.10,使用npm i -g corepack
) - 使用
pnpm install
安装依赖 - 使用
pnpm dev
运行交互式测试;使用pnpm demo:dev
启动使用源代码的 vite 服务器
致谢
如果没有以下支持,这个项目将不可能实现:
- 来自 capsizecss 的出色工具和生成的度量
- 来自 Google Aurora 团队的 Katie Hempenius 和 Kara Erickson 的建议和算法 - 参见计算字体度量覆盖的说明
- 来自 @clemcode 的包名建议
许可证
用❤️制作
根据 MIT 许可证 发布。