@nebutra/fonts 3.0.0 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -0
- package/NOTICE-FONTS.md +26 -8
- package/README.md +34 -13
- package/dist/{chunk-JHYTIKKY.js → chunk-A4PPXTH5.js} +1 -1
- package/dist/chunk-A4PPXTH5.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/next-cjk.js +1 -1
- package/dist/next.js +197 -53
- package/dist/next.js.map +1 -1
- package/generated/dm-sans.woff2 +0 -0
- package/generated/registry/dm-sans-latin-wght-normal.woff2 +0 -0
- package/generated/registry/figtree-latin-wght-normal.woff2 +0 -0
- package/generated/registry/fira-code-latin-wght-normal.woff2 +0 -0
- package/generated/registry/fraunces-latin-wght-normal.woff2 +0 -0
- package/generated/registry/inter-latin-wght-normal.woff2 +0 -0
- package/generated/registry/inter-tight-latin-wght-normal.woff2 +0 -0
- package/generated/registry/jetbrains-mono-latin-wght-normal.woff2 +0 -0
- package/generated/registry/lexend-latin-wght-normal.woff2 +0 -0
- package/generated/registry/manrope-latin-wght-normal.woff2 +0 -0
- package/generated/registry/montserrat-latin-wght-normal.woff2 +0 -0
- package/generated/registry/outfit-latin-wght-normal.woff2 +0 -0
- package/generated/registry/playfair-display-latin-wght-normal.woff2 +0 -0
- package/generated/registry/plus-jakarta-sans-latin-wght-normal.woff2 +0 -0
- package/generated/registry/roboto-mono-latin-wght-normal.woff2 +0 -0
- package/generated/registry/sora-latin-wght-normal.woff2 +0 -0
- package/generated/registry/source-code-pro-latin-wght-normal.woff2 +0 -0
- package/generated/registry/source-serif-4-latin-wght-normal.woff2 +0 -0
- package/generated/registry/space-grotesk-latin-wght-normal.woff2 +0 -0
- package/generated/registry/work-sans-latin-wght-normal.woff2 +0 -0
- package/package.json +25 -3
- package/src/index.ts +1 -1
- package/src/next-cjk.test.ts +9 -0
- package/src/next-cjk.ts +4 -4
- package/src/next.test.ts +80 -0
- package/src/next.ts +247 -71
- package/dist/chunk-JHYTIKKY.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# @nebutra/fonts
|
|
2
2
|
|
|
3
|
+
## 4.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`076251c`](https://github.com/Nebutra/Nebutra-Sailor/commit/076251c6f5a1d74bf98192727d0b1895d88fe607) Thanks [@TsekaLuk](https://github.com/TsekaLuk)! - Fix `next/font/local` paths in `@nebutra/fonts/next` breaking on a hoisted
|
|
8
|
+
`npm install` of the published package. The 19 registry faces pointed at
|
|
9
|
+
`../node_modules/@fontsource-variable/<name>/files/...`, which only resolved
|
|
10
|
+
because pnpm nests a workspace package's own dependencies under its own
|
|
11
|
+
node_modules; npm commonly hoists `@fontsource-variable/*` to the installing
|
|
12
|
+
project's top-level node_modules instead, so `next build` failed to resolve
|
|
13
|
+
the font file there.
|
|
14
|
+
|
|
15
|
+
The woff2 bytes are now vendored into the package itself
|
|
16
|
+
(`generated/registry/`, copied at build/prepack time by
|
|
17
|
+
`scripts/copy-registry-fonts.mjs` and committed to git, same pattern as the
|
|
18
|
+
existing `generated/dm-sans.woff2`), and `next.ts` references them by a
|
|
19
|
+
package-relative path instead. `@fontsource-variable/*` moved from
|
|
20
|
+
`dependencies` to `devDependencies` — they are only needed to vendor the
|
|
21
|
+
bytes at build time, never at runtime by a consumer.
|
|
22
|
+
|
|
23
|
+
Also fixes `generated/dm-sans.woff2` itself being omitted from the npm
|
|
24
|
+
`files` list — the same class of bug, just not yet hit by a downstream build.
|
|
25
|
+
|
|
26
|
+
- Updated dependencies []:
|
|
27
|
+
- @nebutra/brand@4.0.1
|
|
28
|
+
|
|
29
|
+
## 4.0.0
|
|
30
|
+
|
|
31
|
+
### Patch Changes
|
|
32
|
+
|
|
33
|
+
- Updated dependencies []:
|
|
34
|
+
- @nebutra/brand@4.0.0
|
|
35
|
+
|
|
3
36
|
## 3.0.0
|
|
4
37
|
|
|
5
38
|
### Patch Changes
|
package/NOTICE-FONTS.md
CHANGED
|
@@ -8,8 +8,8 @@ This package's MIT licence covers **first-party code only**.
|
|
|
8
8
|
This software uses the **MiSans** typeface by Xiaomi.
|
|
9
9
|
|
|
10
10
|
MiSans is free for commercial use and may be embedded in software on the
|
|
11
|
-
condition that the software states it uses MiSans (this notice, and the
|
|
12
|
-
|
|
11
|
+
condition that the software states it uses MiSans (this notice, and the
|
|
12
|
+
landing site's /credits page, which every public footer links to). The font may not be distributed on its own or
|
|
13
13
|
have its appearance altered. Licence text: `vendor/misans/LICENSE.txt`.
|
|
14
14
|
|
|
15
15
|
## DM Sans
|
|
@@ -17,11 +17,29 @@ have its appearance altered. Licence text: `vendor/misans/LICENSE.txt`.
|
|
|
17
17
|
This software uses the **DM Sans** typeface, licensed under the SIL Open Font
|
|
18
18
|
License 1.1. Licence text: `vendor/dm-sans/OFL.txt`.
|
|
19
19
|
|
|
20
|
+
## Theme / DESIGN.md registry faces
|
|
21
|
+
|
|
22
|
+
`src/next.ts` loads 19 faces from woff2 files vendored under
|
|
23
|
+
`generated/registry/`, sourced from `@fontsource-variable/*` npm packages
|
|
24
|
+
(Fontsource, version 5.3.0), each licensed under the **SIL Open Font License
|
|
25
|
+
1.1**, which permits use, embedding and redistribution, including bundling with
|
|
26
|
+
software: Inter, Inter Tight, Space Grotesk, Playfair Display, Source Serif 4,
|
|
27
|
+
Fraunces, JetBrains Mono, Manrope, Sora, Work Sans, DM Sans, Plus Jakarta Sans,
|
|
28
|
+
Outfit, Figtree, Montserrat, Lexend, Fira Code, Roboto Mono, Source Code Pro.
|
|
29
|
+
Each package carries its licence text (`LICENSE`) and copyright notice. The
|
|
30
|
+
`@fontsource-variable/*` packages are devDependencies only, used to vendor the
|
|
31
|
+
woff2 bytes at build time (`scripts/copy-registry-fonts.mjs`, wired into
|
|
32
|
+
`build` and `prepack`); they are not required at runtime by anything that
|
|
33
|
+
depends on `@nebutra/fonts`.
|
|
34
|
+
|
|
20
35
|
## Distribution
|
|
21
36
|
|
|
22
|
-
The DM Sans subset (`generated/dm-sans.woff2`)
|
|
23
|
-
`
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
37
|
+
The DM Sans subset (`generated/dm-sans.woff2`) and the 19 registry faces
|
|
38
|
+
(`generated/registry/*.woff2`) are committed to git AND included in the npm
|
|
39
|
+
`files` list, so `next/font/local`'s package-relative `path` resolves the
|
|
40
|
+
same whether the package is a pnpm workspace symlink or a hoisted `npm
|
|
41
|
+
install` of the published tarball — the installing project's node_modules
|
|
42
|
+
layout no longer matters. MiSans subsets are the one exception: never
|
|
43
|
+
committed and never shipped, because the licence forbids distributing the
|
|
44
|
+
font on its own; they are uploaded to the deployment's asset CDN and
|
|
45
|
+
`<CjkFontFace />` points at them instead.
|
package/README.md
CHANGED
|
@@ -4,8 +4,8 @@ Status: **WIP** — not yet published to npm.
|
|
|
4
4
|
|
|
5
5
|
The Simplified-Chinese face is wired into every Next app that loads Geist (web,
|
|
6
6
|
landing, design-docs, sailor-docs, admin, forge, router, sleptons, typelens,
|
|
7
|
-
mail-preview). The theme / DESIGN.md registry (
|
|
8
|
-
|
|
7
|
+
mail-preview). The theme / DESIGN.md registry (19 OSS faces) is applied by
|
|
8
|
+
`apps/web`, `apps/landing` and `apps/design`.
|
|
9
9
|
|
|
10
10
|
Self-hosted fonts for Nebutra: the CJK body face, plus an OSS font registry for
|
|
11
11
|
themes and imported DESIGN.md font families.
|
|
@@ -19,9 +19,10 @@ The package has three entries:
|
|
|
19
19
|
also keeps it working in a network-sandboxed dev server. **This is the one every
|
|
20
20
|
app needs.**
|
|
21
21
|
- `@nebutra/fonts/next` is server-only and declares the build-time
|
|
22
|
-
`next/font/
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
registry faces (`next/font/local` over `@fontsource-variable/*` packages — no
|
|
23
|
+
network at build or dev time) plus the combined registry class name. It
|
|
24
|
+
re-exports the CJK face, but importing it just for that would pull 19 faces
|
|
25
|
+
into the app's CSS — use `./next/cjk`.
|
|
25
26
|
|
|
26
27
|
## Installation
|
|
27
28
|
|
|
@@ -88,7 +89,10 @@ the reason vivo Sans was removed (b5e73db35). `pnpm subset:cjk --upload` writes
|
|
|
88
89
|
content-hashed subsets to the bucket behind your public asset origin
|
|
89
90
|
(`MISANS_R2_BUCKET`, under `fonts/misans/`) and commits only their keys. Until
|
|
90
91
|
they are uploaded, or offline, the requests fail and the stack falls back to
|
|
91
|
-
PingFang / YaHei.
|
|
92
|
+
PingFang / YaHei. The product states it uses MiSans on the landing site's
|
|
93
|
+
`/credits` page, linked from every public footer — the licence asks for the
|
|
94
|
+
statement, not a footer line (see NOTICE-FONTS.md; guarded by
|
|
95
|
+
`tests/architecture/font-license.test.ts`).
|
|
92
96
|
|
|
93
97
|
### Stack order is the design decision
|
|
94
98
|
|
|
@@ -177,17 +181,34 @@ Both licences are separate from this package's MIT licence. See
|
|
|
177
181
|
|
|
178
182
|
## Registered Families
|
|
179
183
|
|
|
180
|
-
The registry includes Geist, Inter, Space Grotesk, Playfair
|
|
181
|
-
|
|
182
|
-
|
|
184
|
+
The registry includes Geist, Inter, Inter Tight, Space Grotesk, Playfair
|
|
185
|
+
Display, Source Serif 4, Fraunces, JetBrains Mono, Manrope, Sora, Work Sans, DM
|
|
186
|
+
Sans, Plus Jakarta Sans, Outfit, Figtree, Montserrat, Lexend, Fira Code, Roboto
|
|
187
|
+
Mono, and Source Code Pro.
|
|
183
188
|
|
|
184
189
|
## Runtime Model
|
|
185
190
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
191
|
+
Every face is a file that arrives with `pnpm install`: the registry loads the
|
|
192
|
+
Latin, upright, `wght`-axis variable build from its `@fontsource-variable/*`
|
|
193
|
+
package through `next/font/local`, so `next build` and `next dev` never contact
|
|
194
|
+
fonts.googleapis.com / fonts.gstatic.com. That matters wherever Google is
|
|
195
|
+
unreachable — mainland China, corporate proxies, offline machines — where
|
|
196
|
+
`next/font/google` failed the build and made the Turbopack dev server answer
|
|
197
|
+
500. npm packages come through any registry mirror (npmmirror in China).
|
|
198
|
+
|
|
199
|
+
The files are Google Fonts' own builds as republished by Fontsource (same font
|
|
200
|
+
version, same axis; shaping checked glyph by glyph against what
|
|
201
|
+
`next/font/google` downloaded) and the `@font-face` family names are Google's,
|
|
202
|
+
so pages render pixel-identically. Only the Latin subset is shipped — the one
|
|
203
|
+
the registry declared and preloaded; characters outside it fall back to the
|
|
204
|
+
metric-matched system font. `scripts/lint-no-google-fonts.mjs` (in `pnpm lint`)
|
|
205
|
+
keeps `next/font/google` out of every file the template ships.
|
|
206
|
+
|
|
207
|
+
At runtime the browser requests fonts from the application origin only when an
|
|
208
|
+
element uses the corresponding CSS variable.
|
|
189
209
|
|
|
190
210
|
## License
|
|
191
211
|
|
|
192
|
-
MIT for first-party code. DM Sans
|
|
212
|
+
MIT for first-party code. DM Sans and every registry face are SIL OFL 1.1
|
|
213
|
+
(see `NOTICE-FONTS.md`). MiSans binaries are never
|
|
193
214
|
committed or published to npm; they are served from the deployment's asset CDN.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/next-cjk.ts","../src/cjk-font-face.tsx"],"sourcesContent":["/**\n * @nebutra/fonts/next/cjk — the self-hosted brand faces (server-only).\n *\n * The Latin brand face lives here through `next/font/local` so first-party apps work\n * offline, in CI and in the network sandbox (the registry in `./next` is local too):\n *\n * - MiSans — the Simplified-Chinese face — is CDN-hosted; see <CjkFontFace /> below.\n * - DM Sans — the Latin display/heading face (SIL OFL). Chosen the same day\n * from the same measurement (DeepSeek, Databricks). Body/UI Latin stays\n * Geist: its tabular figures are what dense dashboard tables need.\n *\n * WHY A SEPARATE ENTRY FROM `./next`: that module declares 19\n * faces for the theme / DESIGN.md registry; importing it for the brand faces\n * would put all of them into every app's CSS. `./next`\n * re-exports this file, so an app already applying `fontRegistryClassName`\n * still needs one import.\n *\n * next/font is a compile-time transform: SWC statically analyses the call, so\n * the options object is spelled out as a literal.\n */\n\nimport localFont from \"next/font/local\";\n\n/**\n * MiSans is NOT loaded here: its licence forbids distributing the font on its\n * own and this repository is public (vivo Sans was removed for the same reason,\n * b5e73db35). The subsets are served from the deployment's asset CDN and\n * declared by <CjkFontFace />, re-exported below — render it in the root\n * layout next to this class name. The token stacks reference\n * var(--font-misans, \"MiSans\"): with no variable set, the literal family name\n * resolves to that @font-face.\n */\nexport { CjkFontFace, misansFontFaceCss } from \"./cjk-font-face\";\n\n/**\n * DM Sans — one variable file (opsz 9–40, wght 100–1000), Latin subset, 67KB.\n * Preloaded: headings render above the fold on most pages.\n */\nexport const dmSans = localFont({\n src: [{ path: \"../generated/dm-sans.woff2\", weight: \"100 1000\", style: \"normal\" }],\n display: \"swap\",\n variable: \"--font-dm-sans\",\n});\n\n/**\n * Apply to <html> next to the Geist loaders so `--font-dm-sans` exists:\n *\n * className={`${GeistSans.variable} ${GeistMono.variable} ${cjkFontClassName}`}\n *\n * The name predates DM Sans; it now carries both brand faces so every app that\n * already applies it picks them up without a layout change.\n */\nexport const brandFontClassName = dmSans.variable;\nexport const cjkFontClassName = brandFontClassName;\n","/**\n * MiSans @font-face, rendered at request time rather than shipped as CSS.\n *\n * The MiSans licence forbids distributing the font on its own and this\n * repository is public, so the subsets live in the deployment's public asset\n * bucket and only their keys are committed (../generated/index.ts). A static\n * stylesheet would have to spell out a host, and the host differs per\n * deployment: Nebutra's CDN for Nebutra, the scaffold's own for a template\n * user. publicAssetUrl() resolves it the way it resolves every other public\n * asset — NEXT_PUBLIC_R2_PUBLIC_URL, then R2_PUBLIC_URL, then the brand's cdn\n * origin.\n *\n * Before the subsets have been uploaded (a fresh scaffold, `pnpm subset:cjk\n * --upload` not yet run) the requests 404 and the token stacks fall through to\n * PingFang / YaHei. That is the intended degraded state, not an error.\n */\n\nimport { publicAssetUrl } from \"@nebutra/brand/metadata-helpers\";\nimport { MISANS_FAMILY, MISANS_FILES, MISANS_UNICODE_RANGE } from \"../generated/index\";\n\n/** One @font-face per weight, CJK-only unicode-range, `swap` so text is never invisible. */\nexport function misansFontFaceCss(origin?: string): string {\n return MISANS_FILES.map(\n (face) =>\n `@font-face{font-family:\"${MISANS_FAMILY}\";font-style:normal;font-weight:${face.weight};` +\n `font-display:swap;src:url(\"${publicAssetUrl(face.key, origin)}\") format(\"woff2\");` +\n `unicode-range:${MISANS_UNICODE_RANGE}}`,\n ).join(\"\\n\");\n}\n\nexport interface CjkFontFaceProps {\n /** Asset origin override; defaults to the publicAssetUrl() resolution. */\n origin?: string;\n /**\n * CSP nonce, for apps whose style-src allows inline styles only by nonce\n * (apps/web). Without it the whole @font-face block is refused. The page's\n * font-src must also allow publicAssetOrigin().\n */\n nonce?: string;\n}\n\n/**\n * Render once in each root layout, next to `cjkFontClassName` on <html>.\n * React 19 hoists a `<style>` carrying `href` + `precedence` into <head> and\n * dedupes it by `href`, so rendering it twice costs nothing.\n */\nexport function CjkFontFace({ origin, nonce }: CjkFontFaceProps) {\n return (\n <style href=\"nebutra-misans\" precedence=\"default\" nonce={nonce || undefined}>\n {misansFontFaceCss(origin)}\n </style>\n );\n}\n"],"mappings":";;;;;;;AAqBA,OAAO,eAAe;;;ACJtB,SAAS,sBAAsB;AA+B3B;AA3BG,SAAS,kBAAkB,QAAyB;AACzD,SAAO,aAAa;AAAA,IAClB,CAAC,SACC,2BAA2B,aAAa,mCAAmC,KAAK,MAAM,+BACxD,eAAe,KAAK,KAAK,MAAM,CAAC,oCAC7C,oBAAoB;AAAA,EACzC,EAAE,KAAK,IAAI;AACb;AAkBO,SAAS,YAAY,EAAE,QAAQ,MAAM,GAAqB;AAC/D,SACE,oBAAC,WAAM,MAAK,kBAAiB,YAAW,WAAU,OAAO,SAAS,QAC/D,4BAAkB,MAAM,GAC3B;AAEJ;;;ADdO,IAAM,SAAS,UAAU;AAAA,EAC9B,KAAK,CAAC,EAAE,MAAM,8BAA8B,QAAQ,YAAY,OAAO,SAAS,CAAC;AAAA,EACjF,SAAS;AAAA,EACT,UAAU;AACZ,CAAC;AAUM,IAAM,qBAAqB,OAAO;AAClC,IAAM,mBAAmB;","names":[]}
|
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ var FONT_REGISTRY = {
|
|
|
9
9
|
// Latin away from Geist. DM Sans is the Latin display/heading face.
|
|
10
10
|
misans: "--font-misans",
|
|
11
11
|
"dm sans display": "--font-dm-sans",
|
|
12
|
-
// Self-hosted via next/font/
|
|
12
|
+
// Self-hosted via next/font/local over @fontsource-variable/* (see ./next)
|
|
13
13
|
inter: "--font-inter",
|
|
14
14
|
"inter tight": "--font-reg-inter-tight",
|
|
15
15
|
"space grotesk": "--font-space-grotesk",
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @nebutra/fonts — self-hosted OSS font registry (client-safe entry).\n *\n * Maps a normalized theme / DESIGN.md font-family name to the CSS variable that\n * the build-time self-hosted face defines (declared with next/font in\n * `@nebutra/fonts/next`, applied to <html> via `fontRegistryClassName`).\n *\n * WHY: next/font registers each face under a HASHED family name reachable ONLY\n * via its CSS variable — `font-family: 'Inter'` does NOT use the self-hosted\n * Inter. So when a theme / imported DESIGN.md font's primary family matches an\n * entry here, callers prepend `var(--font-…)` to the stack, making the\n * self-hosted font actually render — with ZERO runtime external requests\n * (next/font self-hosts at build time) and next/font's automatic metric-matched\n * fallback (no layout shift). Unmatched families keep their declared stack.\n *\n * This entry is FREE of `next/font` imports so client modules can use it.\n * The `./next` subpath holds the (server-only) next/font declarations and MUST\n * keep its CSS-variable names in sync with FONT_REGISTRY below.\n */\n\nexport const FONT_REGISTRY: Record<string, string> = {\n // Self-hosted via geist/font (default brand faces, loaded by the app shell)\n geist: \"--font-geist-sans\",\n \"geist sans\": \"--font-geist-sans\",\n \"geist mono\": \"--font-geist-mono\",\n // Self-hosted via next/font/local (see ./next-cjk). MiSans is the\n // Simplified-Chinese face — CJK only via unicode-range, so it cannot take\n // Latin away from Geist. DM Sans is the Latin display/heading face.\n misans: \"--font-misans\",\n \"dm sans display\": \"--font-dm-sans\",\n // Self-hosted via next/font/
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @nebutra/fonts — self-hosted OSS font registry (client-safe entry).\n *\n * Maps a normalized theme / DESIGN.md font-family name to the CSS variable that\n * the build-time self-hosted face defines (declared with next/font in\n * `@nebutra/fonts/next`, applied to <html> via `fontRegistryClassName`).\n *\n * WHY: next/font registers each face under a HASHED family name reachable ONLY\n * via its CSS variable — `font-family: 'Inter'` does NOT use the self-hosted\n * Inter. So when a theme / imported DESIGN.md font's primary family matches an\n * entry here, callers prepend `var(--font-…)` to the stack, making the\n * self-hosted font actually render — with ZERO runtime external requests\n * (next/font self-hosts at build time) and next/font's automatic metric-matched\n * fallback (no layout shift). Unmatched families keep their declared stack.\n *\n * This entry is FREE of `next/font` imports so client modules can use it.\n * The `./next` subpath holds the (server-only) next/font declarations and MUST\n * keep its CSS-variable names in sync with FONT_REGISTRY below.\n */\n\nexport const FONT_REGISTRY: Record<string, string> = {\n // Self-hosted via geist/font (default brand faces, loaded by the app shell)\n geist: \"--font-geist-sans\",\n \"geist sans\": \"--font-geist-sans\",\n \"geist mono\": \"--font-geist-mono\",\n // Self-hosted via next/font/local (see ./next-cjk). MiSans is the\n // Simplified-Chinese face — CJK only via unicode-range, so it cannot take\n // Latin away from Geist. DM Sans is the Latin display/heading face.\n misans: \"--font-misans\",\n \"dm sans display\": \"--font-dm-sans\",\n // Self-hosted via next/font/local over @fontsource-variable/* (see ./next)\n inter: \"--font-inter\",\n \"inter tight\": \"--font-reg-inter-tight\",\n \"space grotesk\": \"--font-space-grotesk\",\n \"playfair display\": \"--font-playfair-display\",\n fraunces: \"--font-reg-fraunces\",\n \"source serif 4\": \"--font-reg-source-serif-4\",\n \"jetbrains mono\": \"--font-jetbrains-mono\",\n manrope: \"--font-reg-manrope\",\n sora: \"--font-reg-sora\",\n \"work sans\": \"--font-reg-work-sans\",\n \"dm sans\": \"--font-reg-dm-sans\",\n \"plus jakarta sans\": \"--font-reg-plus-jakarta-sans\",\n outfit: \"--font-reg-outfit\",\n figtree: \"--font-reg-figtree\",\n montserrat: \"--font-reg-montserrat\",\n lexend: \"--font-reg-lexend\",\n \"fira code\": \"--font-reg-fira-code\",\n \"roboto mono\": \"--font-reg-roboto-mono\",\n \"source code pro\": \"--font-reg-source-code-pro\",\n};\n\n/** Normalize a single font-family token: strip quotes/whitespace, lowercase. */\nfunction normalizeFamily(name: string): string {\n // Strip ALL quotes with a quantifier-free global replace (quotes only appear\n // at token boundaries in a font-family value). Avoids the end-anchored /['\"]+$/\n // form, which CodeQL flags as polynomial ReDoS (scanned from every position).\n return name.replace(/['\"]/g, \"\").trim().toLowerCase();\n}\n\n/** The first (primary) family in a CSS font-family list, normalized. */\nexport function primaryFamily(stack: string): string {\n return normalizeFamily(stack.split(\",\")[0] ?? \"\");\n}\n\n/** Registry CSS variable for a stack's primary family, or undefined. */\nexport function resolveRegistryVar(stack: string): string | undefined {\n return FONT_REGISTRY[primaryFamily(stack)];\n}\n\n/**\n * Return `stack` with the self-hosted registry font prepended when its primary\n * family is registered; otherwise return it unchanged.\n * e.g. \"Space Grotesk, sans-serif\" → \"var(--font-space-grotesk), Space Grotesk, sans-serif\"\n */\nexport function withRegistryFont(stack: string | undefined): string | undefined {\n if (!stack) return stack;\n const variable = resolveRegistryVar(stack);\n return variable ? `var(${variable}), ${stack}` : stack;\n}\n\n/**\n * Like `withRegistryFont`, but matches the first registered family ANYWHERE in\n * the stack rather than only in first position.\n *\n * A brand package names the typeface the design language actually uses, and\n * those are frequently licensed faces we have no right to serve — Söhne, Mori,\n * Lyon Text, Reckless. The declared stack already says what to do when they are\n * absent: fall to the next family. But \"the next family\" is usually a bare name\n * like `Inter`, which does NOT reach next/font's hashed face, so the stack\n * skidded past every self-hosted option and landed on `ui-sans-serif`. All\n * seven built-in design languages rendered in the system font until 2026-08-18.\n *\n * Prepending the nearest registered family produces exactly the outcome the\n * declared chain intended, and leaves the licensed names in place so a customer\n * who does own the font still gets it by shipping the face themselves.\n */\nexport function withNearestRegistryFont(stack: string | undefined): string | undefined {\n if (!stack) return stack;\n for (const token of stack.split(\",\")) {\n const variable = FONT_REGISTRY[normalizeFamily(token)];\n if (variable) return `var(${variable}), ${stack}`;\n }\n return stack;\n}\n"],"mappings":";AAoBO,IAAM,gBAAwC;AAAA;AAAA,EAEnD,OAAO;AAAA,EACP,cAAc;AAAA,EACd,cAAc;AAAA;AAAA;AAAA;AAAA,EAId,QAAQ;AAAA,EACR,mBAAmB;AAAA;AAAA,EAEnB,OAAO;AAAA,EACP,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,UAAU;AAAA,EACV,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,SAAS;AAAA,EACT,MAAM;AAAA,EACN,aAAa;AAAA,EACb,WAAW;AAAA,EACX,qBAAqB;AAAA,EACrB,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,eAAe;AAAA,EACf,mBAAmB;AACrB;AAGA,SAAS,gBAAgB,MAAsB;AAI7C,SAAO,KAAK,QAAQ,SAAS,EAAE,EAAE,KAAK,EAAE,YAAY;AACtD;AAGO,SAAS,cAAc,OAAuB;AACnD,SAAO,gBAAgB,MAAM,MAAM,GAAG,EAAE,CAAC,KAAK,EAAE;AAClD;AAGO,SAAS,mBAAmB,OAAmC;AACpE,SAAO,cAAc,cAAc,KAAK,CAAC;AAC3C;AAOO,SAAS,iBAAiB,OAA+C;AAC9E,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,WAAW,mBAAmB,KAAK;AACzC,SAAO,WAAW,OAAO,QAAQ,MAAM,KAAK,KAAK;AACnD;AAkBO,SAAS,wBAAwB,OAA+C;AACrF,MAAI,CAAC,MAAO,QAAO;AACnB,aAAW,SAAS,MAAM,MAAM,GAAG,GAAG;AACpC,UAAM,WAAW,cAAc,gBAAgB,KAAK,CAAC;AACrD,QAAI,SAAU,QAAO,OAAO,QAAQ,MAAM,KAAK;AAAA,EACjD;AACA,SAAO;AACT;","names":[]}
|
package/dist/next-cjk.js
CHANGED
package/dist/next.js
CHANGED
|
@@ -3,96 +3,240 @@ import {
|
|
|
3
3
|
brandFontClassName,
|
|
4
4
|
cjkFontClassName,
|
|
5
5
|
dmSans
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-A4PPXTH5.js";
|
|
7
7
|
import "./chunk-FUPJK5RT.js";
|
|
8
8
|
|
|
9
9
|
// src/next.ts
|
|
10
|
-
import
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
var inter = Inter({ subsets: ["latin"], display: "swap", variable: "--font-inter" });
|
|
32
|
-
var interTight = Inter_Tight({
|
|
33
|
-
subsets: ["latin"],
|
|
10
|
+
import localFont from "next/font/local";
|
|
11
|
+
var inter = localFont({
|
|
12
|
+
src: [
|
|
13
|
+
{
|
|
14
|
+
path: "../generated/registry/inter-latin-wght-normal.woff2",
|
|
15
|
+
weight: "100 900",
|
|
16
|
+
style: "normal"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
display: "swap",
|
|
20
|
+
declarations: [{ prop: "font-family", value: "'Inter'" }],
|
|
21
|
+
variable: "--font-inter"
|
|
22
|
+
});
|
|
23
|
+
var interTight = localFont({
|
|
24
|
+
src: [
|
|
25
|
+
{
|
|
26
|
+
path: "../generated/registry/inter-tight-latin-wght-normal.woff2",
|
|
27
|
+
weight: "100 900",
|
|
28
|
+
style: "normal"
|
|
29
|
+
}
|
|
30
|
+
],
|
|
34
31
|
display: "swap",
|
|
32
|
+
declarations: [{ prop: "font-family", value: "'Inter Tight'" }],
|
|
35
33
|
variable: "--font-reg-inter-tight"
|
|
36
34
|
});
|
|
37
|
-
var spaceGrotesk =
|
|
38
|
-
|
|
35
|
+
var spaceGrotesk = localFont({
|
|
36
|
+
src: [
|
|
37
|
+
{
|
|
38
|
+
path: "../generated/registry/space-grotesk-latin-wght-normal.woff2",
|
|
39
|
+
weight: "300 700",
|
|
40
|
+
style: "normal"
|
|
41
|
+
}
|
|
42
|
+
],
|
|
39
43
|
display: "swap",
|
|
44
|
+
declarations: [{ prop: "font-family", value: "'Space Grotesk'" }],
|
|
40
45
|
variable: "--font-space-grotesk"
|
|
41
46
|
});
|
|
42
|
-
var playfairDisplay =
|
|
43
|
-
|
|
47
|
+
var playfairDisplay = localFont({
|
|
48
|
+
src: [
|
|
49
|
+
{
|
|
50
|
+
path: "../generated/registry/playfair-display-latin-wght-normal.woff2",
|
|
51
|
+
weight: "400 900",
|
|
52
|
+
style: "normal"
|
|
53
|
+
}
|
|
54
|
+
],
|
|
44
55
|
display: "swap",
|
|
56
|
+
declarations: [{ prop: "font-family", value: "'Playfair Display'" }],
|
|
57
|
+
adjustFontFallback: "Times New Roman",
|
|
45
58
|
variable: "--font-playfair-display"
|
|
46
59
|
});
|
|
47
|
-
var fraunces =
|
|
48
|
-
|
|
60
|
+
var fraunces = localFont({
|
|
61
|
+
src: [
|
|
62
|
+
{
|
|
63
|
+
path: "../generated/registry/fraunces-latin-wght-normal.woff2",
|
|
64
|
+
weight: "100 900",
|
|
65
|
+
style: "normal"
|
|
66
|
+
}
|
|
67
|
+
],
|
|
49
68
|
display: "swap",
|
|
69
|
+
declarations: [{ prop: "font-family", value: "'Fraunces'" }],
|
|
70
|
+
adjustFontFallback: "Times New Roman",
|
|
50
71
|
variable: "--font-reg-fraunces"
|
|
51
72
|
});
|
|
52
|
-
var jetbrainsMono =
|
|
53
|
-
|
|
73
|
+
var jetbrainsMono = localFont({
|
|
74
|
+
src: [
|
|
75
|
+
{
|
|
76
|
+
path: "../generated/registry/jetbrains-mono-latin-wght-normal.woff2",
|
|
77
|
+
weight: "100 800",
|
|
78
|
+
style: "normal"
|
|
79
|
+
}
|
|
80
|
+
],
|
|
54
81
|
display: "swap",
|
|
82
|
+
declarations: [{ prop: "font-family", value: "'JetBrains Mono'" }],
|
|
55
83
|
variable: "--font-jetbrains-mono"
|
|
56
84
|
});
|
|
57
|
-
var manrope =
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
85
|
+
var manrope = localFont({
|
|
86
|
+
src: [
|
|
87
|
+
{
|
|
88
|
+
path: "../generated/registry/manrope-latin-wght-normal.woff2",
|
|
89
|
+
weight: "200 800",
|
|
90
|
+
style: "normal"
|
|
91
|
+
}
|
|
92
|
+
],
|
|
93
|
+
display: "swap",
|
|
94
|
+
declarations: [{ prop: "font-family", value: "'Manrope'" }],
|
|
95
|
+
variable: "--font-reg-manrope"
|
|
96
|
+
});
|
|
97
|
+
var sora = localFont({
|
|
98
|
+
src: [
|
|
99
|
+
{
|
|
100
|
+
path: "../generated/registry/sora-latin-wght-normal.woff2",
|
|
101
|
+
weight: "100 800",
|
|
102
|
+
style: "normal"
|
|
103
|
+
}
|
|
104
|
+
],
|
|
105
|
+
display: "swap",
|
|
106
|
+
declarations: [{ prop: "font-family", value: "'Sora'" }],
|
|
107
|
+
variable: "--font-reg-sora"
|
|
108
|
+
});
|
|
109
|
+
var workSans = localFont({
|
|
110
|
+
src: [
|
|
111
|
+
{
|
|
112
|
+
path: "../generated/registry/work-sans-latin-wght-normal.woff2",
|
|
113
|
+
weight: "100 900",
|
|
114
|
+
style: "normal"
|
|
115
|
+
}
|
|
116
|
+
],
|
|
61
117
|
display: "swap",
|
|
118
|
+
declarations: [{ prop: "font-family", value: "'Work Sans'" }],
|
|
62
119
|
variable: "--font-reg-work-sans"
|
|
63
120
|
});
|
|
64
|
-
var dmSans2 =
|
|
65
|
-
|
|
66
|
-
|
|
121
|
+
var dmSans2 = localFont({
|
|
122
|
+
src: [
|
|
123
|
+
{
|
|
124
|
+
path: "../generated/registry/dm-sans-latin-wght-normal.woff2",
|
|
125
|
+
weight: "100 1000",
|
|
126
|
+
style: "normal"
|
|
127
|
+
}
|
|
128
|
+
],
|
|
67
129
|
display: "swap",
|
|
130
|
+
declarations: [{ prop: "font-family", value: "'DM Sans'" }],
|
|
131
|
+
variable: "--font-reg-dm-sans"
|
|
132
|
+
});
|
|
133
|
+
var plusJakartaSans = localFont({
|
|
134
|
+
src: [
|
|
135
|
+
{
|
|
136
|
+
path: "../generated/registry/plus-jakarta-sans-latin-wght-normal.woff2",
|
|
137
|
+
weight: "200 800",
|
|
138
|
+
style: "normal"
|
|
139
|
+
}
|
|
140
|
+
],
|
|
141
|
+
display: "swap",
|
|
142
|
+
declarations: [{ prop: "font-family", value: "'Plus Jakarta Sans'" }],
|
|
68
143
|
variable: "--font-reg-plus-jakarta-sans"
|
|
69
144
|
});
|
|
70
|
-
var outfit =
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
145
|
+
var outfit = localFont({
|
|
146
|
+
src: [
|
|
147
|
+
{
|
|
148
|
+
path: "../generated/registry/outfit-latin-wght-normal.woff2",
|
|
149
|
+
weight: "100 900",
|
|
150
|
+
style: "normal"
|
|
151
|
+
}
|
|
152
|
+
],
|
|
153
|
+
display: "swap",
|
|
154
|
+
declarations: [{ prop: "font-family", value: "'Outfit'" }],
|
|
155
|
+
variable: "--font-reg-outfit"
|
|
156
|
+
});
|
|
157
|
+
var figtree = localFont({
|
|
158
|
+
src: [
|
|
159
|
+
{
|
|
160
|
+
path: "../generated/registry/figtree-latin-wght-normal.woff2",
|
|
161
|
+
weight: "300 900",
|
|
162
|
+
style: "normal"
|
|
163
|
+
}
|
|
164
|
+
],
|
|
74
165
|
display: "swap",
|
|
166
|
+
declarations: [{ prop: "font-family", value: "'Figtree'" }],
|
|
167
|
+
variable: "--font-reg-figtree"
|
|
168
|
+
});
|
|
169
|
+
var montserrat = localFont({
|
|
170
|
+
src: [
|
|
171
|
+
{
|
|
172
|
+
path: "../generated/registry/montserrat-latin-wght-normal.woff2",
|
|
173
|
+
weight: "100 900",
|
|
174
|
+
style: "normal"
|
|
175
|
+
}
|
|
176
|
+
],
|
|
177
|
+
display: "swap",
|
|
178
|
+
declarations: [{ prop: "font-family", value: "'Montserrat'" }],
|
|
75
179
|
variable: "--font-reg-montserrat"
|
|
76
180
|
});
|
|
77
|
-
var lexend =
|
|
78
|
-
|
|
79
|
-
|
|
181
|
+
var lexend = localFont({
|
|
182
|
+
src: [
|
|
183
|
+
{
|
|
184
|
+
path: "../generated/registry/lexend-latin-wght-normal.woff2",
|
|
185
|
+
weight: "100 900",
|
|
186
|
+
style: "normal"
|
|
187
|
+
}
|
|
188
|
+
],
|
|
189
|
+
display: "swap",
|
|
190
|
+
declarations: [{ prop: "font-family", value: "'Lexend'" }],
|
|
191
|
+
variable: "--font-reg-lexend"
|
|
192
|
+
});
|
|
193
|
+
var firaCode = localFont({
|
|
194
|
+
src: [
|
|
195
|
+
{
|
|
196
|
+
path: "../generated/registry/fira-code-latin-wght-normal.woff2",
|
|
197
|
+
weight: "300 700",
|
|
198
|
+
style: "normal"
|
|
199
|
+
}
|
|
200
|
+
],
|
|
80
201
|
display: "swap",
|
|
202
|
+
declarations: [{ prop: "font-family", value: "'Fira Code'" }],
|
|
81
203
|
variable: "--font-reg-fira-code"
|
|
82
204
|
});
|
|
83
|
-
var robotoMono =
|
|
84
|
-
|
|
205
|
+
var robotoMono = localFont({
|
|
206
|
+
src: [
|
|
207
|
+
{
|
|
208
|
+
path: "../generated/registry/roboto-mono-latin-wght-normal.woff2",
|
|
209
|
+
weight: "100 700",
|
|
210
|
+
style: "normal"
|
|
211
|
+
}
|
|
212
|
+
],
|
|
85
213
|
display: "swap",
|
|
214
|
+
declarations: [{ prop: "font-family", value: "'Roboto Mono'" }],
|
|
86
215
|
variable: "--font-reg-roboto-mono"
|
|
87
216
|
});
|
|
88
|
-
var sourceSerif4 =
|
|
89
|
-
|
|
217
|
+
var sourceSerif4 = localFont({
|
|
218
|
+
src: [
|
|
219
|
+
{
|
|
220
|
+
path: "../generated/registry/source-serif-4-latin-wght-normal.woff2",
|
|
221
|
+
weight: "200 900",
|
|
222
|
+
style: "normal"
|
|
223
|
+
}
|
|
224
|
+
],
|
|
90
225
|
display: "swap",
|
|
226
|
+
declarations: [{ prop: "font-family", value: "'Source Serif 4'" }],
|
|
227
|
+
adjustFontFallback: "Times New Roman",
|
|
91
228
|
variable: "--font-reg-source-serif-4"
|
|
92
229
|
});
|
|
93
|
-
var sourceCodePro =
|
|
94
|
-
|
|
230
|
+
var sourceCodePro = localFont({
|
|
231
|
+
src: [
|
|
232
|
+
{
|
|
233
|
+
path: "../generated/registry/source-code-pro-latin-wght-normal.woff2",
|
|
234
|
+
weight: "200 900",
|
|
235
|
+
style: "normal"
|
|
236
|
+
}
|
|
237
|
+
],
|
|
95
238
|
display: "swap",
|
|
239
|
+
declarations: [{ prop: "font-family", value: "'Source Code Pro'" }],
|
|
96
240
|
variable: "--font-reg-source-code-pro"
|
|
97
241
|
});
|
|
98
242
|
var FONT_REGISTRY_FACES = [
|
package/dist/next.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/next.ts"],"sourcesContent":["/**\n * @nebutra/fonts/next — build-time self-hosted OSS font faces (server-only).\n *\n * Each face is loaded via next/font/google, which downloads the font AT BUILD\n * TIME and self-hosts it from the app's own origin — there are ZERO runtime\n * requests to Google (no IP leak / GDPR concern, no third-party runtime\n * dependency). Each face exposes a CSS variable; the browser only fetches a\n * given font file when an element actually uses that variable, so declaring the\n * whole registry is cheap.\n *\n * Apply `fontRegistryClassName` to <html> so the `--font-*` variables exist;\n * the appearance layer then prepends the matching `var(--font-*)` (see the\n * client-safe map in `@nebutra/fonts`) when a theme / DESIGN.md font matches.\n *\n * All declarations use a literal options object — next/font statically analyses\n * the call, so the config must NOT be computed. Variable fonts omit `weight`.\n * Keep the `variable` names in sync with FONT_REGISTRY in `../index.ts`.\n *\n * The brand faces live in `./next-cjk` — DM Sans (next/font/local, no Google\n * fetch) and <CjkFontFace /> for the CDN-hosted MiSans — and are re-exported\n * here for discoverability. They are NOT folded into `fontRegistryClassName`:\n * they are core typography, not optional theme faces, and every app wires them\n * the same way — `${cjkFontClassName}` on <html> beside the Geist loaders and\n * <CjkFontFace /> in the root layout. Apps that only need them should import\n * `@nebutra/fonts/next/cjk` directly so they don't pull in the Google faces below.\n */\n\nimport {\n DM_Sans,\n Figtree,\n Fira_Code,\n Fraunces,\n Inter,\n Inter_Tight,\n JetBrains_Mono,\n Lexend,\n Manrope,\n Montserrat,\n Outfit,\n Playfair_Display,\n Plus_Jakarta_Sans,\n Roboto_Mono,\n Sora,\n Source_Code_Pro,\n Source_Serif_4,\n Space_Grotesk,\n Work_Sans,\n} from \"next/font/google\";\n\nexport { brandFontClassName, CjkFontFace, cjkFontClassName, dmSans } from \"./next-cjk\";\n\nconst inter = Inter({ subsets: [\"latin\"], display: \"swap\", variable: \"--font-inter\" });\nconst interTight = Inter_Tight({\n subsets: [\"latin\"],\n display: \"swap\",\n variable: \"--font-reg-inter-tight\",\n});\nconst spaceGrotesk = Space_Grotesk({\n subsets: [\"latin\"],\n display: \"swap\",\n variable: \"--font-space-grotesk\",\n});\nconst playfairDisplay = Playfair_Display({\n subsets: [\"latin\"],\n display: \"swap\",\n variable: \"--font-playfair-display\",\n});\nconst fraunces = Fraunces({\n subsets: [\"latin\"],\n display: \"swap\",\n variable: \"--font-reg-fraunces\",\n});\nconst jetbrainsMono = JetBrains_Mono({\n subsets: [\"latin\"],\n display: \"swap\",\n variable: \"--font-jetbrains-mono\",\n});\nconst manrope = Manrope({ subsets: [\"latin\"], display: \"swap\", variable: \"--font-reg-manrope\" });\nconst sora = Sora({ subsets: [\"latin\"], display: \"swap\", variable: \"--font-reg-sora\" });\nconst workSans = Work_Sans({\n subsets: [\"latin\"],\n display: \"swap\",\n variable: \"--font-reg-work-sans\",\n});\nconst dmSans = DM_Sans({ subsets: [\"latin\"], display: \"swap\", variable: \"--font-reg-dm-sans\" });\nconst plusJakartaSans = Plus_Jakarta_Sans({\n subsets: [\"latin\"],\n display: \"swap\",\n variable: \"--font-reg-plus-jakarta-sans\",\n});\nconst outfit = Outfit({ subsets: [\"latin\"], display: \"swap\", variable: \"--font-reg-outfit\" });\nconst figtree = Figtree({ subsets: [\"latin\"], display: \"swap\", variable: \"--font-reg-figtree\" });\nconst montserrat = Montserrat({\n subsets: [\"latin\"],\n display: \"swap\",\n variable: \"--font-reg-montserrat\",\n});\nconst lexend = Lexend({ subsets: [\"latin\"], display: \"swap\", variable: \"--font-reg-lexend\" });\nconst firaCode = Fira_Code({\n subsets: [\"latin\"],\n display: \"swap\",\n variable: \"--font-reg-fira-code\",\n});\nconst robotoMono = Roboto_Mono({\n subsets: [\"latin\"],\n display: \"swap\",\n variable: \"--font-reg-roboto-mono\",\n});\nconst sourceSerif4 = Source_Serif_4({\n subsets: [\"latin\"],\n display: \"swap\",\n variable: \"--font-reg-source-serif-4\",\n});\nconst sourceCodePro = Source_Code_Pro({\n subsets: [\"latin\"],\n display: \"swap\",\n variable: \"--font-reg-source-code-pro\",\n});\n\n/** All registry faces, in declaration order. */\nexport const FONT_REGISTRY_FACES = [\n inter,\n interTight,\n spaceGrotesk,\n playfairDisplay,\n sourceSerif4,\n fraunces,\n jetbrainsMono,\n manrope,\n sora,\n workSans,\n dmSans,\n plusJakartaSans,\n outfit,\n figtree,\n montserrat,\n lexend,\n firaCode,\n robotoMono,\n sourceCodePro,\n] as const;\n\n/**\n * Space-joined `.variable` classNames for every registry face. Apply to <html>\n * so all `--font-*` registry variables are defined (font files lazy-load on\n * first use). Combine with the app's own Geist faces.\n */\nexport const fontRegistryClassName = FONT_REGISTRY_FACES.map((face) => face.variable).join(\" \");\n"],"mappings":"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","names":["dmSans"]}
|
|
1
|
+
{"version":3,"sources":["../src/next.ts"],"sourcesContent":["/**\n * @nebutra/fonts/next — the theme / DESIGN.md registry faces (server-only).\n *\n * Every face is loaded through `next/font/local` from a woff2 vendored into\n * this package's own `../generated/registry/` — the build and the dev server\n * make NO request to fonts.googleapis.com / fonts.gstatic.com. That is the\n * point: with `next/font/google`, a machine that cannot reach Google\n * (mainland China, a corporate proxy, a plane) failed `next build` outright\n * and answered 500 from the Turbopack dev server (\"Can't resolve\n * '@vercel/turbopack-next/internal/font/google/font'\"), so a scaffold's\n * preview site was dead on arrival. npm packages are mirrored everywhere\n * (npmmirror in China); Google Fonts is not.\n *\n * The files are the same Google Fonts builds (same font version, same `wght`\n * axis, identical shaping — checked glyph by glyph against what\n * next/font/google downloaded): Fontsource republishes them under the SIL OFL\n * 1.1, which permits redistribution. Each face is the Latin subset of the\n * upright variable font, the subset the registry declared (`subsets: [\"latin\"]`)\n * and the only one it preloaded. Weight ranges are the ones Google served.\n * Serif faces take Times New Roman as the metric-matched fallback, as\n * next/font/google did; the rest take Arial. Each face keeps its Google family\n * name through `declarations` — without it next/font/local names the family\n * after the const, and this file's `dmSans` collided with the brand `dmSans` in\n * ./next-cjk: two @font-face rules under one family, and the heading face\n * rendered from the wrong file.\n *\n * Each face exposes a CSS variable; the browser only fetches a given file when\n * an element resolves to that variable, so declaring the whole registry is\n * cheap. Apply `fontRegistryClassName` to <html> so the `--font-*` variables\n * exist; the appearance layer then prepends the matching `var(--font-*)` (see\n * the client-safe map in `@nebutra/fonts`) when a theme / DESIGN.md font\n * matches. Keep the `variable` names in sync with FONT_REGISTRY in `../index.ts`.\n *\n * next/font is a compile-time transform: SWC statically analyses each call, so\n * every options object is a literal and every `path` a string literal,\n * resolved relative to this file. It used to point at\n * `../node_modules/@fontsource-variable/<name>/files/...`, which only exists\n * because pnpm nests every workspace package's own dependencies under its own\n * node_modules — a hoisted `npm install` of the published `@nebutra/fonts`\n * commonly resolves `@fontsource-variable/*` to the installing project's\n * top-level node_modules instead, so that path did not exist there and\n * `next build` failed to resolve the font file. The woff2 files are copied\n * into `../generated/registry/` by `scripts/copy-registry-fonts.mjs` (wired\n * into `build` and `prepack`, and the copies are committed so a clean clone\n * works without running it) and committed to git — same pattern as\n * `../generated/dm-sans.woff2` below — so the path is package-relative and\n * resolves the same regardless of how npm/pnpm/yarn laid out node_modules.\n * `scripts/lint-no-google-fonts.mjs` keeps `next/font/google` out of\n * template-shipped code.\n *\n * The brand faces live in `./next-cjk` — DM Sans (next/font/local) and\n * <CjkFontFace /> for the CDN-hosted MiSans — and are re-exported here for\n * discoverability. They are NOT folded into `fontRegistryClassName`: they are\n * core typography, not optional theme faces, and every app wires them the same\n * way — `${cjkFontClassName}` on <html> beside the Geist loaders and\n * <CjkFontFace /> in the root layout. Apps that only need them should import\n * `@nebutra/fonts/next/cjk` directly so they don't pull in the registry below.\n */\n\nimport localFont from \"next/font/local\";\n\nexport { brandFontClassName, CjkFontFace, cjkFontClassName, dmSans } from \"./next-cjk\";\n\nconst inter = localFont({\n src: [\n {\n path: \"../generated/registry/inter-latin-wght-normal.woff2\",\n weight: \"100 900\",\n style: \"normal\",\n },\n ],\n display: \"swap\",\n declarations: [{ prop: \"font-family\", value: \"'Inter'\" }],\n variable: \"--font-inter\",\n});\nconst interTight = localFont({\n src: [\n {\n path: \"../generated/registry/inter-tight-latin-wght-normal.woff2\",\n weight: \"100 900\",\n style: \"normal\",\n },\n ],\n display: \"swap\",\n declarations: [{ prop: \"font-family\", value: \"'Inter Tight'\" }],\n variable: \"--font-reg-inter-tight\",\n});\nconst spaceGrotesk = localFont({\n src: [\n {\n path: \"../generated/registry/space-grotesk-latin-wght-normal.woff2\",\n weight: \"300 700\",\n style: \"normal\",\n },\n ],\n display: \"swap\",\n declarations: [{ prop: \"font-family\", value: \"'Space Grotesk'\" }],\n variable: \"--font-space-grotesk\",\n});\nconst playfairDisplay = localFont({\n src: [\n {\n path: \"../generated/registry/playfair-display-latin-wght-normal.woff2\",\n weight: \"400 900\",\n style: \"normal\",\n },\n ],\n display: \"swap\",\n declarations: [{ prop: \"font-family\", value: \"'Playfair Display'\" }],\n adjustFontFallback: \"Times New Roman\",\n variable: \"--font-playfair-display\",\n});\nconst fraunces = localFont({\n src: [\n {\n path: \"../generated/registry/fraunces-latin-wght-normal.woff2\",\n weight: \"100 900\",\n style: \"normal\",\n },\n ],\n display: \"swap\",\n declarations: [{ prop: \"font-family\", value: \"'Fraunces'\" }],\n adjustFontFallback: \"Times New Roman\",\n variable: \"--font-reg-fraunces\",\n});\nconst jetbrainsMono = localFont({\n src: [\n {\n path: \"../generated/registry/jetbrains-mono-latin-wght-normal.woff2\",\n weight: \"100 800\",\n style: \"normal\",\n },\n ],\n display: \"swap\",\n declarations: [{ prop: \"font-family\", value: \"'JetBrains Mono'\" }],\n variable: \"--font-jetbrains-mono\",\n});\nconst manrope = localFont({\n src: [\n {\n path: \"../generated/registry/manrope-latin-wght-normal.woff2\",\n weight: \"200 800\",\n style: \"normal\",\n },\n ],\n display: \"swap\",\n declarations: [{ prop: \"font-family\", value: \"'Manrope'\" }],\n variable: \"--font-reg-manrope\",\n});\nconst sora = localFont({\n src: [\n {\n path: \"../generated/registry/sora-latin-wght-normal.woff2\",\n weight: \"100 800\",\n style: \"normal\",\n },\n ],\n display: \"swap\",\n declarations: [{ prop: \"font-family\", value: \"'Sora'\" }],\n variable: \"--font-reg-sora\",\n});\nconst workSans = localFont({\n src: [\n {\n path: \"../generated/registry/work-sans-latin-wght-normal.woff2\",\n weight: \"100 900\",\n style: \"normal\",\n },\n ],\n display: \"swap\",\n declarations: [{ prop: \"font-family\", value: \"'Work Sans'\" }],\n variable: \"--font-reg-work-sans\",\n});\nconst dmSans = localFont({\n src: [\n {\n path: \"../generated/registry/dm-sans-latin-wght-normal.woff2\",\n weight: \"100 1000\",\n style: \"normal\",\n },\n ],\n display: \"swap\",\n declarations: [{ prop: \"font-family\", value: \"'DM Sans'\" }],\n variable: \"--font-reg-dm-sans\",\n});\nconst plusJakartaSans = localFont({\n src: [\n {\n path: \"../generated/registry/plus-jakarta-sans-latin-wght-normal.woff2\",\n weight: \"200 800\",\n style: \"normal\",\n },\n ],\n display: \"swap\",\n declarations: [{ prop: \"font-family\", value: \"'Plus Jakarta Sans'\" }],\n variable: \"--font-reg-plus-jakarta-sans\",\n});\nconst outfit = localFont({\n src: [\n {\n path: \"../generated/registry/outfit-latin-wght-normal.woff2\",\n weight: \"100 900\",\n style: \"normal\",\n },\n ],\n display: \"swap\",\n declarations: [{ prop: \"font-family\", value: \"'Outfit'\" }],\n variable: \"--font-reg-outfit\",\n});\nconst figtree = localFont({\n src: [\n {\n path: \"../generated/registry/figtree-latin-wght-normal.woff2\",\n weight: \"300 900\",\n style: \"normal\",\n },\n ],\n display: \"swap\",\n declarations: [{ prop: \"font-family\", value: \"'Figtree'\" }],\n variable: \"--font-reg-figtree\",\n});\nconst montserrat = localFont({\n src: [\n {\n path: \"../generated/registry/montserrat-latin-wght-normal.woff2\",\n weight: \"100 900\",\n style: \"normal\",\n },\n ],\n display: \"swap\",\n declarations: [{ prop: \"font-family\", value: \"'Montserrat'\" }],\n variable: \"--font-reg-montserrat\",\n});\nconst lexend = localFont({\n src: [\n {\n path: \"../generated/registry/lexend-latin-wght-normal.woff2\",\n weight: \"100 900\",\n style: \"normal\",\n },\n ],\n display: \"swap\",\n declarations: [{ prop: \"font-family\", value: \"'Lexend'\" }],\n variable: \"--font-reg-lexend\",\n});\nconst firaCode = localFont({\n src: [\n {\n path: \"../generated/registry/fira-code-latin-wght-normal.woff2\",\n weight: \"300 700\",\n style: \"normal\",\n },\n ],\n display: \"swap\",\n declarations: [{ prop: \"font-family\", value: \"'Fira Code'\" }],\n variable: \"--font-reg-fira-code\",\n});\nconst robotoMono = localFont({\n src: [\n {\n path: \"../generated/registry/roboto-mono-latin-wght-normal.woff2\",\n weight: \"100 700\",\n style: \"normal\",\n },\n ],\n display: \"swap\",\n declarations: [{ prop: \"font-family\", value: \"'Roboto Mono'\" }],\n variable: \"--font-reg-roboto-mono\",\n});\nconst sourceSerif4 = localFont({\n src: [\n {\n path: \"../generated/registry/source-serif-4-latin-wght-normal.woff2\",\n weight: \"200 900\",\n style: \"normal\",\n },\n ],\n display: \"swap\",\n declarations: [{ prop: \"font-family\", value: \"'Source Serif 4'\" }],\n adjustFontFallback: \"Times New Roman\",\n variable: \"--font-reg-source-serif-4\",\n});\nconst sourceCodePro = localFont({\n src: [\n {\n path: \"../generated/registry/source-code-pro-latin-wght-normal.woff2\",\n weight: \"200 900\",\n style: \"normal\",\n },\n ],\n display: \"swap\",\n declarations: [{ prop: \"font-family\", value: \"'Source Code Pro'\" }],\n variable: \"--font-reg-source-code-pro\",\n});\n\n/** All registry faces, in declaration order. */\nexport const FONT_REGISTRY_FACES = [\n inter,\n interTight,\n spaceGrotesk,\n playfairDisplay,\n sourceSerif4,\n fraunces,\n jetbrainsMono,\n manrope,\n sora,\n workSans,\n dmSans,\n plusJakartaSans,\n outfit,\n figtree,\n montserrat,\n lexend,\n firaCode,\n robotoMono,\n sourceCodePro,\n] as const;\n\n/**\n * Space-joined `.variable` classNames for every registry face. Apply to <html>\n * so all `--font-*` registry variables are defined (font files lazy-load on\n * first use). Combine with the app's own Geist faces.\n */\nexport const fontRegistryClassName = FONT_REGISTRY_FACES.map((face) => face.variable).join(\" \");\n"],"mappings":";;;;;;;;;AA2DA,OAAO,eAAe;AAItB,IAAM,QAAQ,UAAU;AAAA,EACtB,KAAK;AAAA,IACH;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,SAAS;AAAA,EACT,cAAc,CAAC,EAAE,MAAM,eAAe,OAAO,UAAU,CAAC;AAAA,EACxD,UAAU;AACZ,CAAC;AACD,IAAM,aAAa,UAAU;AAAA,EAC3B,KAAK;AAAA,IACH;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,SAAS;AAAA,EACT,cAAc,CAAC,EAAE,MAAM,eAAe,OAAO,gBAAgB,CAAC;AAAA,EAC9D,UAAU;AACZ,CAAC;AACD,IAAM,eAAe,UAAU;AAAA,EAC7B,KAAK;AAAA,IACH;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,SAAS;AAAA,EACT,cAAc,CAAC,EAAE,MAAM,eAAe,OAAO,kBAAkB,CAAC;AAAA,EAChE,UAAU;AACZ,CAAC;AACD,IAAM,kBAAkB,UAAU;AAAA,EAChC,KAAK;AAAA,IACH;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,SAAS;AAAA,EACT,cAAc,CAAC,EAAE,MAAM,eAAe,OAAO,qBAAqB,CAAC;AAAA,EACnE,oBAAoB;AAAA,EACpB,UAAU;AACZ,CAAC;AACD,IAAM,WAAW,UAAU;AAAA,EACzB,KAAK;AAAA,IACH;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,SAAS;AAAA,EACT,cAAc,CAAC,EAAE,MAAM,eAAe,OAAO,aAAa,CAAC;AAAA,EAC3D,oBAAoB;AAAA,EACpB,UAAU;AACZ,CAAC;AACD,IAAM,gBAAgB,UAAU;AAAA,EAC9B,KAAK;AAAA,IACH;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,SAAS;AAAA,EACT,cAAc,CAAC,EAAE,MAAM,eAAe,OAAO,mBAAmB,CAAC;AAAA,EACjE,UAAU;AACZ,CAAC;AACD,IAAM,UAAU,UAAU;AAAA,EACxB,KAAK;AAAA,IACH;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,SAAS;AAAA,EACT,cAAc,CAAC,EAAE,MAAM,eAAe,OAAO,YAAY,CAAC;AAAA,EAC1D,UAAU;AACZ,CAAC;AACD,IAAM,OAAO,UAAU;AAAA,EACrB,KAAK;AAAA,IACH;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,SAAS;AAAA,EACT,cAAc,CAAC,EAAE,MAAM,eAAe,OAAO,SAAS,CAAC;AAAA,EACvD,UAAU;AACZ,CAAC;AACD,IAAM,WAAW,UAAU;AAAA,EACzB,KAAK;AAAA,IACH;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,SAAS;AAAA,EACT,cAAc,CAAC,EAAE,MAAM,eAAe,OAAO,cAAc,CAAC;AAAA,EAC5D,UAAU;AACZ,CAAC;AACD,IAAMA,UAAS,UAAU;AAAA,EACvB,KAAK;AAAA,IACH;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,SAAS;AAAA,EACT,cAAc,CAAC,EAAE,MAAM,eAAe,OAAO,YAAY,CAAC;AAAA,EAC1D,UAAU;AACZ,CAAC;AACD,IAAM,kBAAkB,UAAU;AAAA,EAChC,KAAK;AAAA,IACH;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,SAAS;AAAA,EACT,cAAc,CAAC,EAAE,MAAM,eAAe,OAAO,sBAAsB,CAAC;AAAA,EACpE,UAAU;AACZ,CAAC;AACD,IAAM,SAAS,UAAU;AAAA,EACvB,KAAK;AAAA,IACH;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,SAAS;AAAA,EACT,cAAc,CAAC,EAAE,MAAM,eAAe,OAAO,WAAW,CAAC;AAAA,EACzD,UAAU;AACZ,CAAC;AACD,IAAM,UAAU,UAAU;AAAA,EACxB,KAAK;AAAA,IACH;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,SAAS;AAAA,EACT,cAAc,CAAC,EAAE,MAAM,eAAe,OAAO,YAAY,CAAC;AAAA,EAC1D,UAAU;AACZ,CAAC;AACD,IAAM,aAAa,UAAU;AAAA,EAC3B,KAAK;AAAA,IACH;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,SAAS;AAAA,EACT,cAAc,CAAC,EAAE,MAAM,eAAe,OAAO,eAAe,CAAC;AAAA,EAC7D,UAAU;AACZ,CAAC;AACD,IAAM,SAAS,UAAU;AAAA,EACvB,KAAK;AAAA,IACH;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,SAAS;AAAA,EACT,cAAc,CAAC,EAAE,MAAM,eAAe,OAAO,WAAW,CAAC;AAAA,EACzD,UAAU;AACZ,CAAC;AACD,IAAM,WAAW,UAAU;AAAA,EACzB,KAAK;AAAA,IACH;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,SAAS;AAAA,EACT,cAAc,CAAC,EAAE,MAAM,eAAe,OAAO,cAAc,CAAC;AAAA,EAC5D,UAAU;AACZ,CAAC;AACD,IAAM,aAAa,UAAU;AAAA,EAC3B,KAAK;AAAA,IACH;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,SAAS;AAAA,EACT,cAAc,CAAC,EAAE,MAAM,eAAe,OAAO,gBAAgB,CAAC;AAAA,EAC9D,UAAU;AACZ,CAAC;AACD,IAAM,eAAe,UAAU;AAAA,EAC7B,KAAK;AAAA,IACH;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,SAAS;AAAA,EACT,cAAc,CAAC,EAAE,MAAM,eAAe,OAAO,mBAAmB,CAAC;AAAA,EACjE,oBAAoB;AAAA,EACpB,UAAU;AACZ,CAAC;AACD,IAAM,gBAAgB,UAAU;AAAA,EAC9B,KAAK;AAAA,IACH;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,SAAS;AAAA,EACT,cAAc,CAAC,EAAE,MAAM,eAAe,OAAO,oBAAoB,CAAC;AAAA,EAClE,UAAU;AACZ,CAAC;AAGM,IAAM,sBAAsB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACAA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAOO,IAAM,wBAAwB,oBAAoB,IAAI,CAAC,SAAS,KAAK,QAAQ,EAAE,KAAK,GAAG;","names":["dmSans"]}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nebutra/fonts",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Self-hosted OSS font registry — build-time next/font faces + a name→CSS-var resolver so theme / DESIGN.md fonts render with zero runtime external requests",
|
|
6
6
|
"private": false,
|
|
@@ -27,6 +27,8 @@
|
|
|
27
27
|
"src",
|
|
28
28
|
"generated/subset-manifest.json",
|
|
29
29
|
"generated/index.ts",
|
|
30
|
+
"generated/dm-sans.woff2",
|
|
31
|
+
"generated/registry",
|
|
30
32
|
"vendor/misans/LICENSE.txt",
|
|
31
33
|
"vendor/dm-sans/OFL.txt",
|
|
32
34
|
"NOTICE-FONTS.md",
|
|
@@ -69,9 +71,28 @@
|
|
|
69
71
|
}
|
|
70
72
|
},
|
|
71
73
|
"dependencies": {
|
|
72
|
-
"@nebutra/brand": "
|
|
74
|
+
"@nebutra/brand": "4.0.1"
|
|
73
75
|
},
|
|
74
76
|
"devDependencies": {
|
|
77
|
+
"@fontsource-variable/dm-sans": "5.3.0",
|
|
78
|
+
"@fontsource-variable/figtree": "5.3.0",
|
|
79
|
+
"@fontsource-variable/fira-code": "5.3.0",
|
|
80
|
+
"@fontsource-variable/fraunces": "5.3.0",
|
|
81
|
+
"@fontsource-variable/inter": "5.3.0",
|
|
82
|
+
"@fontsource-variable/inter-tight": "5.3.0",
|
|
83
|
+
"@fontsource-variable/jetbrains-mono": "5.3.0",
|
|
84
|
+
"@fontsource-variable/lexend": "5.3.0",
|
|
85
|
+
"@fontsource-variable/manrope": "5.3.0",
|
|
86
|
+
"@fontsource-variable/montserrat": "5.3.0",
|
|
87
|
+
"@fontsource-variable/outfit": "5.3.0",
|
|
88
|
+
"@fontsource-variable/playfair-display": "5.3.0",
|
|
89
|
+
"@fontsource-variable/plus-jakarta-sans": "5.3.0",
|
|
90
|
+
"@fontsource-variable/roboto-mono": "5.3.0",
|
|
91
|
+
"@fontsource-variable/sora": "5.3.0",
|
|
92
|
+
"@fontsource-variable/source-code-pro": "5.3.0",
|
|
93
|
+
"@fontsource-variable/source-serif-4": "5.3.0",
|
|
94
|
+
"@fontsource-variable/space-grotesk": "5.3.0",
|
|
95
|
+
"@fontsource-variable/work-sans": "5.3.0",
|
|
75
96
|
"@types/react": "^19.2.14",
|
|
76
97
|
"typescript": "^5.9.3",
|
|
77
98
|
"vitest": "^4.1.4",
|
|
@@ -86,8 +107,9 @@
|
|
|
86
107
|
},
|
|
87
108
|
"scripts": {
|
|
88
109
|
"subset:cjk": "node scripts/subset-cjk.mjs",
|
|
110
|
+
"fonts:registry": "node scripts/copy-registry-fonts.mjs",
|
|
89
111
|
"test": "vitest run",
|
|
90
112
|
"typecheck": "tsc --noEmit",
|
|
91
|
-
"build": "tsup"
|
|
113
|
+
"build": "pnpm run fonts:registry && tsup"
|
|
92
114
|
}
|
|
93
115
|
}
|
package/src/index.ts
CHANGED
|
@@ -28,7 +28,7 @@ export const FONT_REGISTRY: Record<string, string> = {
|
|
|
28
28
|
// Latin away from Geist. DM Sans is the Latin display/heading face.
|
|
29
29
|
misans: "--font-misans",
|
|
30
30
|
"dm sans display": "--font-dm-sans",
|
|
31
|
-
// Self-hosted via next/font/
|
|
31
|
+
// Self-hosted via next/font/local over @fontsource-variable/* (see ./next)
|
|
32
32
|
inter: "--font-inter",
|
|
33
33
|
"inter tight": "--font-reg-inter-tight",
|
|
34
34
|
"space grotesk": "--font-space-grotesk",
|
package/src/next-cjk.test.ts
CHANGED
|
@@ -53,4 +53,13 @@ describe("DM Sans wiring", () => {
|
|
|
53
53
|
expect(NEXT_CJK_SOURCE).toContain('variable: "--font-dm-sans"');
|
|
54
54
|
expect(FONT_REGISTRY["dm sans display"]).toBe("--font-dm-sans");
|
|
55
55
|
});
|
|
56
|
+
|
|
57
|
+
it("ships generated/dm-sans.woff2 in the npm `files` list", () => {
|
|
58
|
+
// Previously omitted: `path` is package-relative (correct — see next.ts),
|
|
59
|
+
// but the woff2 wasn't in `files`, so a published install never actually
|
|
60
|
+
// had the byte the path points at. Same root cause as the 19 registry
|
|
61
|
+
// faces, caught by the same audit.
|
|
62
|
+
const pkg = JSON.parse(read("../package.json")) as { files: string[] };
|
|
63
|
+
expect(pkg.files).toContain("generated/dm-sans.woff2");
|
|
64
|
+
});
|
|
56
65
|
});
|
package/src/next-cjk.ts
CHANGED
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
* @nebutra/fonts/next/cjk — the self-hosted brand faces (server-only).
|
|
3
3
|
*
|
|
4
4
|
* The Latin brand face lives here through `next/font/local` so first-party apps work
|
|
5
|
-
* offline, in CI and in the network sandbox (
|
|
5
|
+
* offline, in CI and in the network sandbox (the registry in `./next` is local too):
|
|
6
6
|
*
|
|
7
7
|
* - MiSans — the Simplified-Chinese face — is CDN-hosted; see <CjkFontFace /> below.
|
|
8
8
|
* - DM Sans — the Latin display/heading face (SIL OFL). Chosen the same day
|
|
9
9
|
* from the same measurement (DeepSeek, Databricks). Body/UI Latin stays
|
|
10
10
|
* Geist: its tabular figures are what dense dashboard tables need.
|
|
11
11
|
*
|
|
12
|
-
* WHY A SEPARATE ENTRY FROM `./next`: that module declares
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* WHY A SEPARATE ENTRY FROM `./next`: that module declares 19
|
|
13
|
+
* faces for the theme / DESIGN.md registry; importing it for the brand faces
|
|
14
|
+
* would put all of them into every app's CSS. `./next`
|
|
15
15
|
* re-exports this file, so an app already applying `fontRegistryClassName`
|
|
16
16
|
* still needs one import.
|
|
17
17
|
*
|
package/src/next.test.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
import { FONT_REGISTRY } from "./index";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* ./next.ts is a next/font compile-time transform, so it is read as TEXT: the
|
|
8
|
+
* point is what the build will do, and the build must not need the network.
|
|
9
|
+
*/
|
|
10
|
+
const here = (rel: string) => fileURLToPath(new URL(rel, import.meta.url));
|
|
11
|
+
const SOURCE = readFileSync(here("./next.ts"), "utf8");
|
|
12
|
+
const faces = [
|
|
13
|
+
...SOURCE.matchAll(
|
|
14
|
+
/const (\w+) = localFont\(\{\s*src: \[\s*\{\s*path: "([^"]+)",\s*weight: "(\d+ \d+)",\s*style: "normal",\s*\},\s*\],\s*display: "swap",\s*(?:adjustFontFallback: "Times New Roman",\s*)?declarations: \[\{ prop: "font-family", value: "'([^']+)'" \}\],\s*(?:adjustFontFallback: "Times New Roman",\s*)?variable: "(--font-[\w-]+)",/g,
|
|
15
|
+
),
|
|
16
|
+
].map(([, name = "", path = "", weight = "", family = "", variable = ""]) => ({
|
|
17
|
+
name,
|
|
18
|
+
path,
|
|
19
|
+
weight,
|
|
20
|
+
family,
|
|
21
|
+
variable,
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
describe("registry faces (@nebutra/fonts/next)", () => {
|
|
25
|
+
it("never fetches from Google at build or dev time", () => {
|
|
26
|
+
expect(SOURCE).not.toMatch(/from\s+["']next\/font\/google["']/);
|
|
27
|
+
expect(SOURCE).toContain('import localFont from "next/font/local";');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("declares every face the registry list exports", () => {
|
|
31
|
+
const listed = /FONT_REGISTRY_FACES = \[([^\]]+)\]/.exec(SOURCE)?.[1] ?? "";
|
|
32
|
+
const names = listed
|
|
33
|
+
.split(",")
|
|
34
|
+
.map((s) => s.trim())
|
|
35
|
+
.filter(Boolean);
|
|
36
|
+
expect(names).toHaveLength(19);
|
|
37
|
+
expect(faces.map((f) => f.name).sort()).toEqual([...names].sort());
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("loads each face from the package's own vendored generated/registry/ (not node_modules)", () => {
|
|
41
|
+
// Package-relative, not `../node_modules/@fontsource-variable/...`: that
|
|
42
|
+
// path only existed because pnpm nests a workspace package's own deps
|
|
43
|
+
// under its own node_modules. A hoisted `npm install` of the published
|
|
44
|
+
// package resolves @fontsource-variable/* elsewhere, so the woff2 bytes
|
|
45
|
+
// are vendored into the package itself (scripts/copy-registry-fonts.mjs)
|
|
46
|
+
// and referenced from a path that resolves the same everywhere.
|
|
47
|
+
for (const { path } of faces) {
|
|
48
|
+
expect(path).toMatch(/^\.\.\/generated\/registry\/([a-z0-9-]+)-latin-wght-normal\.woff2$/);
|
|
49
|
+
expect(existsSync(here(path)), path).toBe(true);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("depends on every @fontsource-variable package it vendors from, pinned (devDependency)", () => {
|
|
54
|
+
const pkg = JSON.parse(readFileSync(here("../package.json"), "utf8")) as {
|
|
55
|
+
devDependencies: Record<string, string>;
|
|
56
|
+
};
|
|
57
|
+
for (const { path } of faces) {
|
|
58
|
+
const fileName = /registry\/([a-z0-9-]+)-latin-wght-normal\.woff2$/.exec(path)?.[1] ?? "";
|
|
59
|
+
const name = `@fontsource-variable/${fileName}`;
|
|
60
|
+
expect(pkg.devDependencies[name], name).toMatch(/^\d+\.\d+\.\d+$/);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("ships the vendored registry woff2 files in the npm `files` list", () => {
|
|
65
|
+
const pkg = JSON.parse(readFileSync(here("../package.json"), "utf8")) as {
|
|
66
|
+
files: string[];
|
|
67
|
+
};
|
|
68
|
+
expect(pkg.files).toContain("generated/registry");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("keeps the variables FONT_REGISTRY resolves to, and distinct family names", () => {
|
|
72
|
+
const registryVars = new Set(Object.values(FONT_REGISTRY));
|
|
73
|
+
for (const { variable } of faces) expect(registryVars.has(variable), variable).toBe(true);
|
|
74
|
+
const families = faces.map((f) => f.family);
|
|
75
|
+
expect(new Set(families).size).toBe(families.length);
|
|
76
|
+
// The brand heading face in ./next-cjk is family "dmSans"; the registry's
|
|
77
|
+
// must not share it, or one @font-face family serves two files.
|
|
78
|
+
expect(families).not.toContain("dmSans");
|
|
79
|
+
});
|
|
80
|
+
});
|
package/src/next.ts
CHANGED
|
@@ -1,119 +1,295 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @nebutra/fonts/next —
|
|
2
|
+
* @nebutra/fonts/next — the theme / DESIGN.md registry faces (server-only).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* Every face is loaded through `next/font/local` from a woff2 vendored into
|
|
5
|
+
* this package's own `../generated/registry/` — the build and the dev server
|
|
6
|
+
* make NO request to fonts.googleapis.com / fonts.gstatic.com. That is the
|
|
7
|
+
* point: with `next/font/google`, a machine that cannot reach Google
|
|
8
|
+
* (mainland China, a corporate proxy, a plane) failed `next build` outright
|
|
9
|
+
* and answered 500 from the Turbopack dev server ("Can't resolve
|
|
10
|
+
* '@vercel/turbopack-next/internal/font/google/font'"), so a scaffold's
|
|
11
|
+
* preview site was dead on arrival. npm packages are mirrored everywhere
|
|
12
|
+
* (npmmirror in China); Google Fonts is not.
|
|
10
13
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
+
* The files are the same Google Fonts builds (same font version, same `wght`
|
|
15
|
+
* axis, identical shaping — checked glyph by glyph against what
|
|
16
|
+
* next/font/google downloaded): Fontsource republishes them under the SIL OFL
|
|
17
|
+
* 1.1, which permits redistribution. Each face is the Latin subset of the
|
|
18
|
+
* upright variable font, the subset the registry declared (`subsets: ["latin"]`)
|
|
19
|
+
* and the only one it preloaded. Weight ranges are the ones Google served.
|
|
20
|
+
* Serif faces take Times New Roman as the metric-matched fallback, as
|
|
21
|
+
* next/font/google did; the rest take Arial. Each face keeps its Google family
|
|
22
|
+
* name through `declarations` — without it next/font/local names the family
|
|
23
|
+
* after the const, and this file's `dmSans` collided with the brand `dmSans` in
|
|
24
|
+
* ./next-cjk: two @font-face rules under one family, and the heading face
|
|
25
|
+
* rendered from the wrong file.
|
|
14
26
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
27
|
+
* Each face exposes a CSS variable; the browser only fetches a given file when
|
|
28
|
+
* an element resolves to that variable, so declaring the whole registry is
|
|
29
|
+
* cheap. Apply `fontRegistryClassName` to <html> so the `--font-*` variables
|
|
30
|
+
* exist; the appearance layer then prepends the matching `var(--font-*)` (see
|
|
31
|
+
* the client-safe map in `@nebutra/fonts`) when a theme / DESIGN.md font
|
|
32
|
+
* matches. Keep the `variable` names in sync with FONT_REGISTRY in `../index.ts`.
|
|
18
33
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
34
|
+
* next/font is a compile-time transform: SWC statically analyses each call, so
|
|
35
|
+
* every options object is a literal and every `path` a string literal,
|
|
36
|
+
* resolved relative to this file. It used to point at
|
|
37
|
+
* `../node_modules/@fontsource-variable/<name>/files/...`, which only exists
|
|
38
|
+
* because pnpm nests every workspace package's own dependencies under its own
|
|
39
|
+
* node_modules — a hoisted `npm install` of the published `@nebutra/fonts`
|
|
40
|
+
* commonly resolves `@fontsource-variable/*` to the installing project's
|
|
41
|
+
* top-level node_modules instead, so that path did not exist there and
|
|
42
|
+
* `next build` failed to resolve the font file. The woff2 files are copied
|
|
43
|
+
* into `../generated/registry/` by `scripts/copy-registry-fonts.mjs` (wired
|
|
44
|
+
* into `build` and `prepack`, and the copies are committed so a clean clone
|
|
45
|
+
* works without running it) and committed to git — same pattern as
|
|
46
|
+
* `../generated/dm-sans.woff2` below — so the path is package-relative and
|
|
47
|
+
* resolves the same regardless of how npm/pnpm/yarn laid out node_modules.
|
|
48
|
+
* `scripts/lint-no-google-fonts.mjs` keeps `next/font/google` out of
|
|
49
|
+
* template-shipped code.
|
|
50
|
+
*
|
|
51
|
+
* The brand faces live in `./next-cjk` — DM Sans (next/font/local) and
|
|
52
|
+
* <CjkFontFace /> for the CDN-hosted MiSans — and are re-exported here for
|
|
53
|
+
* discoverability. They are NOT folded into `fontRegistryClassName`: they are
|
|
54
|
+
* core typography, not optional theme faces, and every app wires them the same
|
|
55
|
+
* way — `${cjkFontClassName}` on <html> beside the Geist loaders and
|
|
24
56
|
* <CjkFontFace /> in the root layout. Apps that only need them should import
|
|
25
|
-
* `@nebutra/fonts/next/cjk` directly so they don't pull in the
|
|
57
|
+
* `@nebutra/fonts/next/cjk` directly so they don't pull in the registry below.
|
|
26
58
|
*/
|
|
27
59
|
|
|
28
|
-
import
|
|
29
|
-
DM_Sans,
|
|
30
|
-
Figtree,
|
|
31
|
-
Fira_Code,
|
|
32
|
-
Fraunces,
|
|
33
|
-
Inter,
|
|
34
|
-
Inter_Tight,
|
|
35
|
-
JetBrains_Mono,
|
|
36
|
-
Lexend,
|
|
37
|
-
Manrope,
|
|
38
|
-
Montserrat,
|
|
39
|
-
Outfit,
|
|
40
|
-
Playfair_Display,
|
|
41
|
-
Plus_Jakarta_Sans,
|
|
42
|
-
Roboto_Mono,
|
|
43
|
-
Sora,
|
|
44
|
-
Source_Code_Pro,
|
|
45
|
-
Source_Serif_4,
|
|
46
|
-
Space_Grotesk,
|
|
47
|
-
Work_Sans,
|
|
48
|
-
} from "next/font/google";
|
|
60
|
+
import localFont from "next/font/local";
|
|
49
61
|
|
|
50
62
|
export { brandFontClassName, CjkFontFace, cjkFontClassName, dmSans } from "./next-cjk";
|
|
51
63
|
|
|
52
|
-
const inter =
|
|
53
|
-
|
|
54
|
-
|
|
64
|
+
const inter = localFont({
|
|
65
|
+
src: [
|
|
66
|
+
{
|
|
67
|
+
path: "../generated/registry/inter-latin-wght-normal.woff2",
|
|
68
|
+
weight: "100 900",
|
|
69
|
+
style: "normal",
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
display: "swap",
|
|
73
|
+
declarations: [{ prop: "font-family", value: "'Inter'" }],
|
|
74
|
+
variable: "--font-inter",
|
|
75
|
+
});
|
|
76
|
+
const interTight = localFont({
|
|
77
|
+
src: [
|
|
78
|
+
{
|
|
79
|
+
path: "../generated/registry/inter-tight-latin-wght-normal.woff2",
|
|
80
|
+
weight: "100 900",
|
|
81
|
+
style: "normal",
|
|
82
|
+
},
|
|
83
|
+
],
|
|
55
84
|
display: "swap",
|
|
85
|
+
declarations: [{ prop: "font-family", value: "'Inter Tight'" }],
|
|
56
86
|
variable: "--font-reg-inter-tight",
|
|
57
87
|
});
|
|
58
|
-
const spaceGrotesk =
|
|
59
|
-
|
|
88
|
+
const spaceGrotesk = localFont({
|
|
89
|
+
src: [
|
|
90
|
+
{
|
|
91
|
+
path: "../generated/registry/space-grotesk-latin-wght-normal.woff2",
|
|
92
|
+
weight: "300 700",
|
|
93
|
+
style: "normal",
|
|
94
|
+
},
|
|
95
|
+
],
|
|
60
96
|
display: "swap",
|
|
97
|
+
declarations: [{ prop: "font-family", value: "'Space Grotesk'" }],
|
|
61
98
|
variable: "--font-space-grotesk",
|
|
62
99
|
});
|
|
63
|
-
const playfairDisplay =
|
|
64
|
-
|
|
100
|
+
const playfairDisplay = localFont({
|
|
101
|
+
src: [
|
|
102
|
+
{
|
|
103
|
+
path: "../generated/registry/playfair-display-latin-wght-normal.woff2",
|
|
104
|
+
weight: "400 900",
|
|
105
|
+
style: "normal",
|
|
106
|
+
},
|
|
107
|
+
],
|
|
65
108
|
display: "swap",
|
|
109
|
+
declarations: [{ prop: "font-family", value: "'Playfair Display'" }],
|
|
110
|
+
adjustFontFallback: "Times New Roman",
|
|
66
111
|
variable: "--font-playfair-display",
|
|
67
112
|
});
|
|
68
|
-
const fraunces =
|
|
69
|
-
|
|
113
|
+
const fraunces = localFont({
|
|
114
|
+
src: [
|
|
115
|
+
{
|
|
116
|
+
path: "../generated/registry/fraunces-latin-wght-normal.woff2",
|
|
117
|
+
weight: "100 900",
|
|
118
|
+
style: "normal",
|
|
119
|
+
},
|
|
120
|
+
],
|
|
70
121
|
display: "swap",
|
|
122
|
+
declarations: [{ prop: "font-family", value: "'Fraunces'" }],
|
|
123
|
+
adjustFontFallback: "Times New Roman",
|
|
71
124
|
variable: "--font-reg-fraunces",
|
|
72
125
|
});
|
|
73
|
-
const jetbrainsMono =
|
|
74
|
-
|
|
126
|
+
const jetbrainsMono = localFont({
|
|
127
|
+
src: [
|
|
128
|
+
{
|
|
129
|
+
path: "../generated/registry/jetbrains-mono-latin-wght-normal.woff2",
|
|
130
|
+
weight: "100 800",
|
|
131
|
+
style: "normal",
|
|
132
|
+
},
|
|
133
|
+
],
|
|
75
134
|
display: "swap",
|
|
135
|
+
declarations: [{ prop: "font-family", value: "'JetBrains Mono'" }],
|
|
76
136
|
variable: "--font-jetbrains-mono",
|
|
77
137
|
});
|
|
78
|
-
const manrope =
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
138
|
+
const manrope = localFont({
|
|
139
|
+
src: [
|
|
140
|
+
{
|
|
141
|
+
path: "../generated/registry/manrope-latin-wght-normal.woff2",
|
|
142
|
+
weight: "200 800",
|
|
143
|
+
style: "normal",
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
display: "swap",
|
|
147
|
+
declarations: [{ prop: "font-family", value: "'Manrope'" }],
|
|
148
|
+
variable: "--font-reg-manrope",
|
|
149
|
+
});
|
|
150
|
+
const sora = localFont({
|
|
151
|
+
src: [
|
|
152
|
+
{
|
|
153
|
+
path: "../generated/registry/sora-latin-wght-normal.woff2",
|
|
154
|
+
weight: "100 800",
|
|
155
|
+
style: "normal",
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
display: "swap",
|
|
159
|
+
declarations: [{ prop: "font-family", value: "'Sora'" }],
|
|
160
|
+
variable: "--font-reg-sora",
|
|
161
|
+
});
|
|
162
|
+
const workSans = localFont({
|
|
163
|
+
src: [
|
|
164
|
+
{
|
|
165
|
+
path: "../generated/registry/work-sans-latin-wght-normal.woff2",
|
|
166
|
+
weight: "100 900",
|
|
167
|
+
style: "normal",
|
|
168
|
+
},
|
|
169
|
+
],
|
|
82
170
|
display: "swap",
|
|
171
|
+
declarations: [{ prop: "font-family", value: "'Work Sans'" }],
|
|
83
172
|
variable: "--font-reg-work-sans",
|
|
84
173
|
});
|
|
85
|
-
const dmSans =
|
|
86
|
-
|
|
87
|
-
|
|
174
|
+
const dmSans = localFont({
|
|
175
|
+
src: [
|
|
176
|
+
{
|
|
177
|
+
path: "../generated/registry/dm-sans-latin-wght-normal.woff2",
|
|
178
|
+
weight: "100 1000",
|
|
179
|
+
style: "normal",
|
|
180
|
+
},
|
|
181
|
+
],
|
|
88
182
|
display: "swap",
|
|
183
|
+
declarations: [{ prop: "font-family", value: "'DM Sans'" }],
|
|
184
|
+
variable: "--font-reg-dm-sans",
|
|
185
|
+
});
|
|
186
|
+
const plusJakartaSans = localFont({
|
|
187
|
+
src: [
|
|
188
|
+
{
|
|
189
|
+
path: "../generated/registry/plus-jakarta-sans-latin-wght-normal.woff2",
|
|
190
|
+
weight: "200 800",
|
|
191
|
+
style: "normal",
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
display: "swap",
|
|
195
|
+
declarations: [{ prop: "font-family", value: "'Plus Jakarta Sans'" }],
|
|
89
196
|
variable: "--font-reg-plus-jakarta-sans",
|
|
90
197
|
});
|
|
91
|
-
const outfit =
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
198
|
+
const outfit = localFont({
|
|
199
|
+
src: [
|
|
200
|
+
{
|
|
201
|
+
path: "../generated/registry/outfit-latin-wght-normal.woff2",
|
|
202
|
+
weight: "100 900",
|
|
203
|
+
style: "normal",
|
|
204
|
+
},
|
|
205
|
+
],
|
|
206
|
+
display: "swap",
|
|
207
|
+
declarations: [{ prop: "font-family", value: "'Outfit'" }],
|
|
208
|
+
variable: "--font-reg-outfit",
|
|
209
|
+
});
|
|
210
|
+
const figtree = localFont({
|
|
211
|
+
src: [
|
|
212
|
+
{
|
|
213
|
+
path: "../generated/registry/figtree-latin-wght-normal.woff2",
|
|
214
|
+
weight: "300 900",
|
|
215
|
+
style: "normal",
|
|
216
|
+
},
|
|
217
|
+
],
|
|
95
218
|
display: "swap",
|
|
219
|
+
declarations: [{ prop: "font-family", value: "'Figtree'" }],
|
|
220
|
+
variable: "--font-reg-figtree",
|
|
221
|
+
});
|
|
222
|
+
const montserrat = localFont({
|
|
223
|
+
src: [
|
|
224
|
+
{
|
|
225
|
+
path: "../generated/registry/montserrat-latin-wght-normal.woff2",
|
|
226
|
+
weight: "100 900",
|
|
227
|
+
style: "normal",
|
|
228
|
+
},
|
|
229
|
+
],
|
|
230
|
+
display: "swap",
|
|
231
|
+
declarations: [{ prop: "font-family", value: "'Montserrat'" }],
|
|
96
232
|
variable: "--font-reg-montserrat",
|
|
97
233
|
});
|
|
98
|
-
const lexend =
|
|
99
|
-
|
|
100
|
-
|
|
234
|
+
const lexend = localFont({
|
|
235
|
+
src: [
|
|
236
|
+
{
|
|
237
|
+
path: "../generated/registry/lexend-latin-wght-normal.woff2",
|
|
238
|
+
weight: "100 900",
|
|
239
|
+
style: "normal",
|
|
240
|
+
},
|
|
241
|
+
],
|
|
242
|
+
display: "swap",
|
|
243
|
+
declarations: [{ prop: "font-family", value: "'Lexend'" }],
|
|
244
|
+
variable: "--font-reg-lexend",
|
|
245
|
+
});
|
|
246
|
+
const firaCode = localFont({
|
|
247
|
+
src: [
|
|
248
|
+
{
|
|
249
|
+
path: "../generated/registry/fira-code-latin-wght-normal.woff2",
|
|
250
|
+
weight: "300 700",
|
|
251
|
+
style: "normal",
|
|
252
|
+
},
|
|
253
|
+
],
|
|
101
254
|
display: "swap",
|
|
255
|
+
declarations: [{ prop: "font-family", value: "'Fira Code'" }],
|
|
102
256
|
variable: "--font-reg-fira-code",
|
|
103
257
|
});
|
|
104
|
-
const robotoMono =
|
|
105
|
-
|
|
258
|
+
const robotoMono = localFont({
|
|
259
|
+
src: [
|
|
260
|
+
{
|
|
261
|
+
path: "../generated/registry/roboto-mono-latin-wght-normal.woff2",
|
|
262
|
+
weight: "100 700",
|
|
263
|
+
style: "normal",
|
|
264
|
+
},
|
|
265
|
+
],
|
|
106
266
|
display: "swap",
|
|
267
|
+
declarations: [{ prop: "font-family", value: "'Roboto Mono'" }],
|
|
107
268
|
variable: "--font-reg-roboto-mono",
|
|
108
269
|
});
|
|
109
|
-
const sourceSerif4 =
|
|
110
|
-
|
|
270
|
+
const sourceSerif4 = localFont({
|
|
271
|
+
src: [
|
|
272
|
+
{
|
|
273
|
+
path: "../generated/registry/source-serif-4-latin-wght-normal.woff2",
|
|
274
|
+
weight: "200 900",
|
|
275
|
+
style: "normal",
|
|
276
|
+
},
|
|
277
|
+
],
|
|
111
278
|
display: "swap",
|
|
279
|
+
declarations: [{ prop: "font-family", value: "'Source Serif 4'" }],
|
|
280
|
+
adjustFontFallback: "Times New Roman",
|
|
112
281
|
variable: "--font-reg-source-serif-4",
|
|
113
282
|
});
|
|
114
|
-
const sourceCodePro =
|
|
115
|
-
|
|
283
|
+
const sourceCodePro = localFont({
|
|
284
|
+
src: [
|
|
285
|
+
{
|
|
286
|
+
path: "../generated/registry/source-code-pro-latin-wght-normal.woff2",
|
|
287
|
+
weight: "200 900",
|
|
288
|
+
style: "normal",
|
|
289
|
+
},
|
|
290
|
+
],
|
|
116
291
|
display: "swap",
|
|
292
|
+
declarations: [{ prop: "font-family", value: "'Source Code Pro'" }],
|
|
117
293
|
variable: "--font-reg-source-code-pro",
|
|
118
294
|
});
|
|
119
295
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/next-cjk.ts","../src/cjk-font-face.tsx"],"sourcesContent":["/**\n * @nebutra/fonts/next/cjk — the self-hosted brand faces (server-only).\n *\n * The Latin brand face lives here through `next/font/local` so first-party apps work\n * offline, in CI and in the network sandbox (`next/font/google` fails there):\n *\n * - MiSans — the Simplified-Chinese face — is CDN-hosted; see <CjkFontFace /> below.\n * - DM Sans — the Latin display/heading face (SIL OFL). Chosen the same day\n * from the same measurement (DeepSeek, Databricks). Body/UI Latin stays\n * Geist: its tabular figures are what dense dashboard tables need.\n *\n * WHY A SEPARATE ENTRY FROM `./next`: that module declares ~16\n * `next/font/google` faces for the theme / DESIGN.md registry; importing it for\n * the brand faces would drag those build-time downloads into every app. `./next`\n * re-exports this file, so an app already applying `fontRegistryClassName`\n * still needs one import.\n *\n * next/font is a compile-time transform: SWC statically analyses the call, so\n * the options object is spelled out as a literal.\n */\n\nimport localFont from \"next/font/local\";\n\n/**\n * MiSans is NOT loaded here: its licence forbids distributing the font on its\n * own and this repository is public (vivo Sans was removed for the same reason,\n * b5e73db35). The subsets are served from the deployment's asset CDN and\n * declared by <CjkFontFace />, re-exported below — render it in the root\n * layout next to this class name. The token stacks reference\n * var(--font-misans, \"MiSans\"): with no variable set, the literal family name\n * resolves to that @font-face.\n */\nexport { CjkFontFace, misansFontFaceCss } from \"./cjk-font-face\";\n\n/**\n * DM Sans — one variable file (opsz 9–40, wght 100–1000), Latin subset, 67KB.\n * Preloaded: headings render above the fold on most pages.\n */\nexport const dmSans = localFont({\n src: [{ path: \"../generated/dm-sans.woff2\", weight: \"100 1000\", style: \"normal\" }],\n display: \"swap\",\n variable: \"--font-dm-sans\",\n});\n\n/**\n * Apply to <html> next to the Geist loaders so `--font-dm-sans` exists:\n *\n * className={`${GeistSans.variable} ${GeistMono.variable} ${cjkFontClassName}`}\n *\n * The name predates DM Sans; it now carries both brand faces so every app that\n * already applies it picks them up without a layout change.\n */\nexport const brandFontClassName = dmSans.variable;\nexport const cjkFontClassName = brandFontClassName;\n","/**\n * MiSans @font-face, rendered at request time rather than shipped as CSS.\n *\n * The MiSans licence forbids distributing the font on its own and this\n * repository is public, so the subsets live in the deployment's public asset\n * bucket and only their keys are committed (../generated/index.ts). A static\n * stylesheet would have to spell out a host, and the host differs per\n * deployment: Nebutra's CDN for Nebutra, the scaffold's own for a template\n * user. publicAssetUrl() resolves it the way it resolves every other public\n * asset — NEXT_PUBLIC_R2_PUBLIC_URL, then R2_PUBLIC_URL, then the brand's cdn\n * origin.\n *\n * Before the subsets have been uploaded (a fresh scaffold, `pnpm subset:cjk\n * --upload` not yet run) the requests 404 and the token stacks fall through to\n * PingFang / YaHei. That is the intended degraded state, not an error.\n */\n\nimport { publicAssetUrl } from \"@nebutra/brand/metadata-helpers\";\nimport { MISANS_FAMILY, MISANS_FILES, MISANS_UNICODE_RANGE } from \"../generated/index\";\n\n/** One @font-face per weight, CJK-only unicode-range, `swap` so text is never invisible. */\nexport function misansFontFaceCss(origin?: string): string {\n return MISANS_FILES.map(\n (face) =>\n `@font-face{font-family:\"${MISANS_FAMILY}\";font-style:normal;font-weight:${face.weight};` +\n `font-display:swap;src:url(\"${publicAssetUrl(face.key, origin)}\") format(\"woff2\");` +\n `unicode-range:${MISANS_UNICODE_RANGE}}`,\n ).join(\"\\n\");\n}\n\nexport interface CjkFontFaceProps {\n /** Asset origin override; defaults to the publicAssetUrl() resolution. */\n origin?: string;\n /**\n * CSP nonce, for apps whose style-src allows inline styles only by nonce\n * (apps/web). Without it the whole @font-face block is refused. The page's\n * font-src must also allow publicAssetOrigin().\n */\n nonce?: string;\n}\n\n/**\n * Render once in each root layout, next to `cjkFontClassName` on <html>.\n * React 19 hoists a `<style>` carrying `href` + `precedence` into <head> and\n * dedupes it by `href`, so rendering it twice costs nothing.\n */\nexport function CjkFontFace({ origin, nonce }: CjkFontFaceProps) {\n return (\n <style href=\"nebutra-misans\" precedence=\"default\" nonce={nonce || undefined}>\n {misansFontFaceCss(origin)}\n </style>\n );\n}\n"],"mappings":";;;;;;;AAqBA,OAAO,eAAe;;;ACJtB,SAAS,sBAAsB;AA+B3B;AA3BG,SAAS,kBAAkB,QAAyB;AACzD,SAAO,aAAa;AAAA,IAClB,CAAC,SACC,2BAA2B,aAAa,mCAAmC,KAAK,MAAM,+BACxD,eAAe,KAAK,KAAK,MAAM,CAAC,oCAC7C,oBAAoB;AAAA,EACzC,EAAE,KAAK,IAAI;AACb;AAkBO,SAAS,YAAY,EAAE,QAAQ,MAAM,GAAqB;AAC/D,SACE,oBAAC,WAAM,MAAK,kBAAiB,YAAW,WAAU,OAAO,SAAS,QAC/D,4BAAkB,MAAM,GAC3B;AAEJ;;;ADdO,IAAM,SAAS,UAAU;AAAA,EAC9B,KAAK,CAAC,EAAE,MAAM,8BAA8B,QAAQ,YAAY,OAAO,SAAS,CAAC;AAAA,EACjF,SAAS;AAAA,EACT,UAAU;AACZ,CAAC;AAUM,IAAM,qBAAqB,OAAO;AAClC,IAAM,mBAAmB;","names":[]}
|