@nebutra/fonts 3.0.0 → 4.0.0
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 +7 -0
- package/NOTICE-FONTS.md +14 -2
- 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/package.json +21 -2
- package/src/index.ts +1 -1
- package/src/next-cjk.ts +4 -4
- package/src/next.test.ts +68 -0
- package/src/next.ts +236 -71
- package/dist/chunk-JHYTIKKY.js.map +0 -1
package/CHANGELOG.md
CHANGED
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,6 +17,18 @@ 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 `@fontsource-variable/*` npm packages
|
|
23
|
+
(Fontsource, version 5.3.0), each licensed under the **SIL Open Font License
|
|
24
|
+
1.1**, which permits use, embedding and redistribution, including bundling with
|
|
25
|
+
software: Inter, Inter Tight, Space Grotesk, Playfair Display, Source Serif 4,
|
|
26
|
+
Fraunces, JetBrains Mono, Manrope, Sora, Work Sans, DM Sans, Plus Jakarta Sans,
|
|
27
|
+
Outfit, Figtree, Montserrat, Lexend, Fira Code, Roboto Mono, Source Code Pro.
|
|
28
|
+
Each package carries its licence text (`LICENSE`) and copyright notice; the
|
|
29
|
+
font binaries are not copied into this package — they are installed as
|
|
30
|
+
dependencies and emitted into the app's build by `next/font/local`.
|
|
31
|
+
|
|
20
32
|
## Distribution
|
|
21
33
|
|
|
22
34
|
The DM Sans subset (`generated/dm-sans.woff2`) is committed so
|
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: "../node_modules/@fontsource-variable/inter/files/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: "../node_modules/@fontsource-variable/inter-tight/files/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: "../node_modules/@fontsource-variable/space-grotesk/files/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: "../node_modules/@fontsource-variable/playfair-display/files/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: "../node_modules/@fontsource-variable/fraunces/files/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: "../node_modules/@fontsource-variable/jetbrains-mono/files/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: "../node_modules/@fontsource-variable/manrope/files/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: "../node_modules/@fontsource-variable/sora/files/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: "../node_modules/@fontsource-variable/work-sans/files/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: "../node_modules/@fontsource-variable/dm-sans/files/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: "../node_modules/@fontsource-variable/plus-jakarta-sans/files/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: "../node_modules/@fontsource-variable/outfit/files/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: "../node_modules/@fontsource-variable/figtree/files/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: "../node_modules/@fontsource-variable/montserrat/files/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: "../node_modules/@fontsource-variable/lexend/files/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: "../node_modules/@fontsource-variable/fira-code/files/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: "../node_modules/@fontsource-variable/roboto-mono/files/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: "../node_modules/@fontsource-variable/source-serif-4/files/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: "../node_modules/@fontsource-variable/source-code-pro/files/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 an `@fontsource-variable/*`\n * package that arrives with `pnpm install` — the build and the dev server make\n * NO request to fonts.googleapis.com / fonts.gstatic.com. That is the point:\n * with `next/font/google`, a machine that cannot reach Google (mainland China,\n * a corporate proxy, a plane) failed `next build` outright and answered 500 from\n * the Turbopack dev server (\"Can't resolve '@vercel/turbopack-next/internal/font/google/font'\"),\n * so a scaffold's preview site was dead on arrival. npm packages are mirrored\n * everywhere (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. The path\n * is relative to this file and goes through the package's own node_modules\n * (pnpm links every dependency there), so it resolves the same from `src/` and\n * `dist/`. `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: \"../node_modules/@fontsource-variable/inter/files/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: \"../node_modules/@fontsource-variable/inter-tight/files/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: \"../node_modules/@fontsource-variable/space-grotesk/files/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: \"../node_modules/@fontsource-variable/playfair-display/files/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: \"../node_modules/@fontsource-variable/fraunces/files/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: \"../node_modules/@fontsource-variable/jetbrains-mono/files/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: \"../node_modules/@fontsource-variable/manrope/files/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: \"../node_modules/@fontsource-variable/sora/files/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: \"../node_modules/@fontsource-variable/work-sans/files/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: \"../node_modules/@fontsource-variable/dm-sans/files/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: \"../node_modules/@fontsource-variable/plus-jakarta-sans/files/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: \"../node_modules/@fontsource-variable/outfit/files/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: \"../node_modules/@fontsource-variable/figtree/files/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: \"../node_modules/@fontsource-variable/montserrat/files/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: \"../node_modules/@fontsource-variable/lexend/files/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: \"../node_modules/@fontsource-variable/fira-code/files/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: \"../node_modules/@fontsource-variable/roboto-mono/files/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: \"../node_modules/@fontsource-variable/source-serif-4/files/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: \"../node_modules/@fontsource-variable/source-code-pro/files/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":"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","names":["dmSans"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nebutra/fonts",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
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,
|
|
@@ -69,7 +69,26 @@
|
|
|
69
69
|
}
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
|
-
"@
|
|
72
|
+
"@fontsource-variable/dm-sans": "5.3.0",
|
|
73
|
+
"@fontsource-variable/figtree": "5.3.0",
|
|
74
|
+
"@fontsource-variable/fira-code": "5.3.0",
|
|
75
|
+
"@fontsource-variable/fraunces": "5.3.0",
|
|
76
|
+
"@fontsource-variable/inter": "5.3.0",
|
|
77
|
+
"@fontsource-variable/inter-tight": "5.3.0",
|
|
78
|
+
"@fontsource-variable/jetbrains-mono": "5.3.0",
|
|
79
|
+
"@fontsource-variable/lexend": "5.3.0",
|
|
80
|
+
"@fontsource-variable/manrope": "5.3.0",
|
|
81
|
+
"@fontsource-variable/montserrat": "5.3.0",
|
|
82
|
+
"@fontsource-variable/outfit": "5.3.0",
|
|
83
|
+
"@fontsource-variable/playfair-display": "5.3.0",
|
|
84
|
+
"@fontsource-variable/plus-jakarta-sans": "5.3.0",
|
|
85
|
+
"@fontsource-variable/roboto-mono": "5.3.0",
|
|
86
|
+
"@fontsource-variable/sora": "5.3.0",
|
|
87
|
+
"@fontsource-variable/source-code-pro": "5.3.0",
|
|
88
|
+
"@fontsource-variable/source-serif-4": "5.3.0",
|
|
89
|
+
"@fontsource-variable/space-grotesk": "5.3.0",
|
|
90
|
+
"@fontsource-variable/work-sans": "5.3.0",
|
|
91
|
+
"@nebutra/brand": "4.0.0"
|
|
73
92
|
},
|
|
74
93
|
"devDependencies": {
|
|
75
94
|
"@types/react": "^19.2.14",
|
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.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,68 @@
|
|
|
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 an installed @fontsource-variable package (Latin, upright, wght)", () => {
|
|
41
|
+
for (const { path } of faces) {
|
|
42
|
+
expect(path).toMatch(
|
|
43
|
+
/^\.\.\/node_modules\/@fontsource-variable\/([a-z0-9-]+)\/files\/\1-latin-wght-normal\.woff2$/,
|
|
44
|
+
);
|
|
45
|
+
expect(existsSync(here(path)), path).toBe(true);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("depends on every package it loads from, pinned", () => {
|
|
50
|
+
const pkg = JSON.parse(readFileSync(here("../package.json"), "utf8")) as {
|
|
51
|
+
dependencies: Record<string, string>;
|
|
52
|
+
};
|
|
53
|
+
for (const { path } of faces) {
|
|
54
|
+
const name = /@fontsource-variable\/[a-z0-9-]+/.exec(path)?.[0] ?? "";
|
|
55
|
+
expect(pkg.dependencies[name], name).toMatch(/^\d+\.\d+\.\d+$/);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("keeps the variables FONT_REGISTRY resolves to, and distinct family names", () => {
|
|
60
|
+
const registryVars = new Set(Object.values(FONT_REGISTRY));
|
|
61
|
+
for (const { variable } of faces) expect(registryVars.has(variable), variable).toBe(true);
|
|
62
|
+
const families = faces.map((f) => f.family);
|
|
63
|
+
expect(new Set(families).size).toBe(families.length);
|
|
64
|
+
// The brand heading face in ./next-cjk is family "dmSans"; the registry's
|
|
65
|
+
// must not share it, or one @font-face family serves two files.
|
|
66
|
+
expect(families).not.toContain("dmSans");
|
|
67
|
+
});
|
|
68
|
+
});
|
package/src/next.ts
CHANGED
|
@@ -1,119 +1,284 @@
|
|
|
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 an `@fontsource-variable/*`
|
|
5
|
+
* package that arrives with `pnpm install` — the build and the dev server make
|
|
6
|
+
* NO request to fonts.googleapis.com / fonts.gstatic.com. That is the point:
|
|
7
|
+
* with `next/font/google`, a machine that cannot reach Google (mainland China,
|
|
8
|
+
* a corporate proxy, a plane) failed `next build` outright and answered 500 from
|
|
9
|
+
* the Turbopack dev server ("Can't resolve '@vercel/turbopack-next/internal/font/google/font'"),
|
|
10
|
+
* so a scaffold's preview site was dead on arrival. npm packages are mirrored
|
|
11
|
+
* everywhere (npmmirror in China); Google Fonts is not.
|
|
10
12
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
13
|
+
* The files are the same Google Fonts builds (same font version, same `wght`
|
|
14
|
+
* axis, identical shaping — checked glyph by glyph against what
|
|
15
|
+
* next/font/google downloaded): Fontsource republishes them under the SIL OFL
|
|
16
|
+
* 1.1, which permits redistribution. Each face is the Latin subset of the
|
|
17
|
+
* upright variable font, the subset the registry declared (`subsets: ["latin"]`)
|
|
18
|
+
* and the only one it preloaded. Weight ranges are the ones Google served.
|
|
19
|
+
* Serif faces take Times New Roman as the metric-matched fallback, as
|
|
20
|
+
* next/font/google did; the rest take Arial. Each face keeps its Google family
|
|
21
|
+
* name through `declarations` — without it next/font/local names the family
|
|
22
|
+
* after the const, and this file's `dmSans` collided with the brand `dmSans` in
|
|
23
|
+
* ./next-cjk: two @font-face rules under one family, and the heading face
|
|
24
|
+
* rendered from the wrong file.
|
|
14
25
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
26
|
+
* Each face exposes a CSS variable; the browser only fetches a given file when
|
|
27
|
+
* an element resolves to that variable, so declaring the whole registry is
|
|
28
|
+
* cheap. Apply `fontRegistryClassName` to <html> so the `--font-*` variables
|
|
29
|
+
* exist; the appearance layer then prepends the matching `var(--font-*)` (see
|
|
30
|
+
* the client-safe map in `@nebutra/fonts`) when a theme / DESIGN.md font
|
|
31
|
+
* matches. Keep the `variable` names in sync with FONT_REGISTRY in `../index.ts`.
|
|
18
32
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
33
|
+
* next/font is a compile-time transform: SWC statically analyses each call, so
|
|
34
|
+
* every options object is a literal and every `path` a string literal. The path
|
|
35
|
+
* is relative to this file and goes through the package's own node_modules
|
|
36
|
+
* (pnpm links every dependency there), so it resolves the same from `src/` and
|
|
37
|
+
* `dist/`. `scripts/lint-no-google-fonts.mjs` keeps `next/font/google` out of
|
|
38
|
+
* template-shipped code.
|
|
39
|
+
*
|
|
40
|
+
* The brand faces live in `./next-cjk` — DM Sans (next/font/local) and
|
|
41
|
+
* <CjkFontFace /> for the CDN-hosted MiSans — and are re-exported here for
|
|
42
|
+
* discoverability. They are NOT folded into `fontRegistryClassName`: they are
|
|
43
|
+
* core typography, not optional theme faces, and every app wires them the same
|
|
44
|
+
* way — `${cjkFontClassName}` on <html> beside the Geist loaders and
|
|
24
45
|
* <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
|
|
46
|
+
* `@nebutra/fonts/next/cjk` directly so they don't pull in the registry below.
|
|
26
47
|
*/
|
|
27
48
|
|
|
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";
|
|
49
|
+
import localFont from "next/font/local";
|
|
49
50
|
|
|
50
51
|
export { brandFontClassName, CjkFontFace, cjkFontClassName, dmSans } from "./next-cjk";
|
|
51
52
|
|
|
52
|
-
const inter =
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
const inter = localFont({
|
|
54
|
+
src: [
|
|
55
|
+
{
|
|
56
|
+
path: "../node_modules/@fontsource-variable/inter/files/inter-latin-wght-normal.woff2",
|
|
57
|
+
weight: "100 900",
|
|
58
|
+
style: "normal",
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
display: "swap",
|
|
62
|
+
declarations: [{ prop: "font-family", value: "'Inter'" }],
|
|
63
|
+
variable: "--font-inter",
|
|
64
|
+
});
|
|
65
|
+
const interTight = localFont({
|
|
66
|
+
src: [
|
|
67
|
+
{
|
|
68
|
+
path: "../node_modules/@fontsource-variable/inter-tight/files/inter-tight-latin-wght-normal.woff2",
|
|
69
|
+
weight: "100 900",
|
|
70
|
+
style: "normal",
|
|
71
|
+
},
|
|
72
|
+
],
|
|
55
73
|
display: "swap",
|
|
74
|
+
declarations: [{ prop: "font-family", value: "'Inter Tight'" }],
|
|
56
75
|
variable: "--font-reg-inter-tight",
|
|
57
76
|
});
|
|
58
|
-
const spaceGrotesk =
|
|
59
|
-
|
|
77
|
+
const spaceGrotesk = localFont({
|
|
78
|
+
src: [
|
|
79
|
+
{
|
|
80
|
+
path: "../node_modules/@fontsource-variable/space-grotesk/files/space-grotesk-latin-wght-normal.woff2",
|
|
81
|
+
weight: "300 700",
|
|
82
|
+
style: "normal",
|
|
83
|
+
},
|
|
84
|
+
],
|
|
60
85
|
display: "swap",
|
|
86
|
+
declarations: [{ prop: "font-family", value: "'Space Grotesk'" }],
|
|
61
87
|
variable: "--font-space-grotesk",
|
|
62
88
|
});
|
|
63
|
-
const playfairDisplay =
|
|
64
|
-
|
|
89
|
+
const playfairDisplay = localFont({
|
|
90
|
+
src: [
|
|
91
|
+
{
|
|
92
|
+
path: "../node_modules/@fontsource-variable/playfair-display/files/playfair-display-latin-wght-normal.woff2",
|
|
93
|
+
weight: "400 900",
|
|
94
|
+
style: "normal",
|
|
95
|
+
},
|
|
96
|
+
],
|
|
65
97
|
display: "swap",
|
|
98
|
+
declarations: [{ prop: "font-family", value: "'Playfair Display'" }],
|
|
99
|
+
adjustFontFallback: "Times New Roman",
|
|
66
100
|
variable: "--font-playfair-display",
|
|
67
101
|
});
|
|
68
|
-
const fraunces =
|
|
69
|
-
|
|
102
|
+
const fraunces = localFont({
|
|
103
|
+
src: [
|
|
104
|
+
{
|
|
105
|
+
path: "../node_modules/@fontsource-variable/fraunces/files/fraunces-latin-wght-normal.woff2",
|
|
106
|
+
weight: "100 900",
|
|
107
|
+
style: "normal",
|
|
108
|
+
},
|
|
109
|
+
],
|
|
70
110
|
display: "swap",
|
|
111
|
+
declarations: [{ prop: "font-family", value: "'Fraunces'" }],
|
|
112
|
+
adjustFontFallback: "Times New Roman",
|
|
71
113
|
variable: "--font-reg-fraunces",
|
|
72
114
|
});
|
|
73
|
-
const jetbrainsMono =
|
|
74
|
-
|
|
115
|
+
const jetbrainsMono = localFont({
|
|
116
|
+
src: [
|
|
117
|
+
{
|
|
118
|
+
path: "../node_modules/@fontsource-variable/jetbrains-mono/files/jetbrains-mono-latin-wght-normal.woff2",
|
|
119
|
+
weight: "100 800",
|
|
120
|
+
style: "normal",
|
|
121
|
+
},
|
|
122
|
+
],
|
|
75
123
|
display: "swap",
|
|
124
|
+
declarations: [{ prop: "font-family", value: "'JetBrains Mono'" }],
|
|
76
125
|
variable: "--font-jetbrains-mono",
|
|
77
126
|
});
|
|
78
|
-
const manrope =
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
127
|
+
const manrope = localFont({
|
|
128
|
+
src: [
|
|
129
|
+
{
|
|
130
|
+
path: "../node_modules/@fontsource-variable/manrope/files/manrope-latin-wght-normal.woff2",
|
|
131
|
+
weight: "200 800",
|
|
132
|
+
style: "normal",
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
display: "swap",
|
|
136
|
+
declarations: [{ prop: "font-family", value: "'Manrope'" }],
|
|
137
|
+
variable: "--font-reg-manrope",
|
|
138
|
+
});
|
|
139
|
+
const sora = localFont({
|
|
140
|
+
src: [
|
|
141
|
+
{
|
|
142
|
+
path: "../node_modules/@fontsource-variable/sora/files/sora-latin-wght-normal.woff2",
|
|
143
|
+
weight: "100 800",
|
|
144
|
+
style: "normal",
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
display: "swap",
|
|
148
|
+
declarations: [{ prop: "font-family", value: "'Sora'" }],
|
|
149
|
+
variable: "--font-reg-sora",
|
|
150
|
+
});
|
|
151
|
+
const workSans = localFont({
|
|
152
|
+
src: [
|
|
153
|
+
{
|
|
154
|
+
path: "../node_modules/@fontsource-variable/work-sans/files/work-sans-latin-wght-normal.woff2",
|
|
155
|
+
weight: "100 900",
|
|
156
|
+
style: "normal",
|
|
157
|
+
},
|
|
158
|
+
],
|
|
82
159
|
display: "swap",
|
|
160
|
+
declarations: [{ prop: "font-family", value: "'Work Sans'" }],
|
|
83
161
|
variable: "--font-reg-work-sans",
|
|
84
162
|
});
|
|
85
|
-
const dmSans =
|
|
86
|
-
|
|
87
|
-
|
|
163
|
+
const dmSans = localFont({
|
|
164
|
+
src: [
|
|
165
|
+
{
|
|
166
|
+
path: "../node_modules/@fontsource-variable/dm-sans/files/dm-sans-latin-wght-normal.woff2",
|
|
167
|
+
weight: "100 1000",
|
|
168
|
+
style: "normal",
|
|
169
|
+
},
|
|
170
|
+
],
|
|
88
171
|
display: "swap",
|
|
172
|
+
declarations: [{ prop: "font-family", value: "'DM Sans'" }],
|
|
173
|
+
variable: "--font-reg-dm-sans",
|
|
174
|
+
});
|
|
175
|
+
const plusJakartaSans = localFont({
|
|
176
|
+
src: [
|
|
177
|
+
{
|
|
178
|
+
path: "../node_modules/@fontsource-variable/plus-jakarta-sans/files/plus-jakarta-sans-latin-wght-normal.woff2",
|
|
179
|
+
weight: "200 800",
|
|
180
|
+
style: "normal",
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
display: "swap",
|
|
184
|
+
declarations: [{ prop: "font-family", value: "'Plus Jakarta Sans'" }],
|
|
89
185
|
variable: "--font-reg-plus-jakarta-sans",
|
|
90
186
|
});
|
|
91
|
-
const outfit =
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
187
|
+
const outfit = localFont({
|
|
188
|
+
src: [
|
|
189
|
+
{
|
|
190
|
+
path: "../node_modules/@fontsource-variable/outfit/files/outfit-latin-wght-normal.woff2",
|
|
191
|
+
weight: "100 900",
|
|
192
|
+
style: "normal",
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
display: "swap",
|
|
196
|
+
declarations: [{ prop: "font-family", value: "'Outfit'" }],
|
|
197
|
+
variable: "--font-reg-outfit",
|
|
198
|
+
});
|
|
199
|
+
const figtree = localFont({
|
|
200
|
+
src: [
|
|
201
|
+
{
|
|
202
|
+
path: "../node_modules/@fontsource-variable/figtree/files/figtree-latin-wght-normal.woff2",
|
|
203
|
+
weight: "300 900",
|
|
204
|
+
style: "normal",
|
|
205
|
+
},
|
|
206
|
+
],
|
|
95
207
|
display: "swap",
|
|
208
|
+
declarations: [{ prop: "font-family", value: "'Figtree'" }],
|
|
209
|
+
variable: "--font-reg-figtree",
|
|
210
|
+
});
|
|
211
|
+
const montserrat = localFont({
|
|
212
|
+
src: [
|
|
213
|
+
{
|
|
214
|
+
path: "../node_modules/@fontsource-variable/montserrat/files/montserrat-latin-wght-normal.woff2",
|
|
215
|
+
weight: "100 900",
|
|
216
|
+
style: "normal",
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
display: "swap",
|
|
220
|
+
declarations: [{ prop: "font-family", value: "'Montserrat'" }],
|
|
96
221
|
variable: "--font-reg-montserrat",
|
|
97
222
|
});
|
|
98
|
-
const lexend =
|
|
99
|
-
|
|
100
|
-
|
|
223
|
+
const lexend = localFont({
|
|
224
|
+
src: [
|
|
225
|
+
{
|
|
226
|
+
path: "../node_modules/@fontsource-variable/lexend/files/lexend-latin-wght-normal.woff2",
|
|
227
|
+
weight: "100 900",
|
|
228
|
+
style: "normal",
|
|
229
|
+
},
|
|
230
|
+
],
|
|
231
|
+
display: "swap",
|
|
232
|
+
declarations: [{ prop: "font-family", value: "'Lexend'" }],
|
|
233
|
+
variable: "--font-reg-lexend",
|
|
234
|
+
});
|
|
235
|
+
const firaCode = localFont({
|
|
236
|
+
src: [
|
|
237
|
+
{
|
|
238
|
+
path: "../node_modules/@fontsource-variable/fira-code/files/fira-code-latin-wght-normal.woff2",
|
|
239
|
+
weight: "300 700",
|
|
240
|
+
style: "normal",
|
|
241
|
+
},
|
|
242
|
+
],
|
|
101
243
|
display: "swap",
|
|
244
|
+
declarations: [{ prop: "font-family", value: "'Fira Code'" }],
|
|
102
245
|
variable: "--font-reg-fira-code",
|
|
103
246
|
});
|
|
104
|
-
const robotoMono =
|
|
105
|
-
|
|
247
|
+
const robotoMono = localFont({
|
|
248
|
+
src: [
|
|
249
|
+
{
|
|
250
|
+
path: "../node_modules/@fontsource-variable/roboto-mono/files/roboto-mono-latin-wght-normal.woff2",
|
|
251
|
+
weight: "100 700",
|
|
252
|
+
style: "normal",
|
|
253
|
+
},
|
|
254
|
+
],
|
|
106
255
|
display: "swap",
|
|
256
|
+
declarations: [{ prop: "font-family", value: "'Roboto Mono'" }],
|
|
107
257
|
variable: "--font-reg-roboto-mono",
|
|
108
258
|
});
|
|
109
|
-
const sourceSerif4 =
|
|
110
|
-
|
|
259
|
+
const sourceSerif4 = localFont({
|
|
260
|
+
src: [
|
|
261
|
+
{
|
|
262
|
+
path: "../node_modules/@fontsource-variable/source-serif-4/files/source-serif-4-latin-wght-normal.woff2",
|
|
263
|
+
weight: "200 900",
|
|
264
|
+
style: "normal",
|
|
265
|
+
},
|
|
266
|
+
],
|
|
111
267
|
display: "swap",
|
|
268
|
+
declarations: [{ prop: "font-family", value: "'Source Serif 4'" }],
|
|
269
|
+
adjustFontFallback: "Times New Roman",
|
|
112
270
|
variable: "--font-reg-source-serif-4",
|
|
113
271
|
});
|
|
114
|
-
const sourceCodePro =
|
|
115
|
-
|
|
272
|
+
const sourceCodePro = localFont({
|
|
273
|
+
src: [
|
|
274
|
+
{
|
|
275
|
+
path: "../node_modules/@fontsource-variable/source-code-pro/files/source-code-pro-latin-wght-normal.woff2",
|
|
276
|
+
weight: "200 900",
|
|
277
|
+
style: "normal",
|
|
278
|
+
},
|
|
279
|
+
],
|
|
116
280
|
display: "swap",
|
|
281
|
+
declarations: [{ prop: "font-family", value: "'Source Code Pro'" }],
|
|
117
282
|
variable: "--font-reg-source-code-pro",
|
|
118
283
|
});
|
|
119
284
|
|
|
@@ -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":[]}
|