Thirteen shipped apps and every scaffold in packages/create-app pulled their
face from Google at request time — Inter through next/font/google in the
examples and the four Next templates, Geist and Geist_Mono in the apps, and in
the seven react-router roots a bare <link> to fonts.googleapis.com. That last
one never even applied what it fetched: nothing in those trees set a family, so
the browser downloaded Inter, spent two preconnects on it, and rendered
system-ui. So `create-app` handed a customer a new docs site whose first act was
a third-party font request for a face that is not ours.
The apps read the family back through --font-sans and --font-mono, which are
next/font's handles rather than names of anything, so those follow the binding
over to --font-zen-sans and --font-zen-mono. Eight of them had only ever bound
the mono and were leaning on Tailwind's default for the text face; they now name
both, or swapping the binding would have dropped the body copy to system-ui
without a word.
Zen and ZenMono are built faces, not constructors — subsetting, display: swap
and fallback metrics are already in them — so the call and its options go away.
Twelve example layouts imported `ZenSans` from `@hanzo/font/sans`, a name
the package stopped exporting at 1.9.0 — the family is called Zen, so the
binding is `Zen`. Every one of those layouts would fail to resolve the
import against a current install, and the `^1.8.x` ranges they pinned kept
resolving to the old export instead of surfacing it.
Twelve example apps opened with next/font/google Inter, so every developer who
copied one started their project on a face that is not ours and a request to a
font host we do not control. They use @hanzo/font's ZenSans (and ZenMono where
the example wanted a second family), with the dependency declared so it
resolves. examples/proposals bound --font-sans to var(--font-inter), a variable
nothing sets any more; it reads --font-zen-sans.
Follow-up to 903b1c38: the package-name normalization covered JS/TS imports +
package.json but missed four other surfaces that name the same phantom packages,
relocating the breakage from 'pnpm install' to 'next/vite/astro build':
- H1: 46 CSS @import strings (@hanzo/docs-{base-ui,ui,openapi}/css/*) → real pkgs
(preserved legit @hanzo/docs-{twoslash,story} CSS)
- H2: examples/astro .astro imports (base-ui/mdx→fumadocs-ui/mdx, docs-core/toc→fumadocs-core/toc)
- L1: .changeset/config.json fixed-array lockstep names
- L2: removed apps/docs/next.config.mts dead phantom→phantom self-alias + dup keys
Found by adversarial red review of 903b1c38.
The fumadocs-fork rebrand left ~13 apps + examples referencing packages under
names that don't exist in the workspace (in BOTH directions: some kept
fumadocs-*, some became @hanzo/docs-*). pnpm install failed outright. Mapped
every reference (deps + imports, 607 source + 61 package.json) to the package
that actually exists, by suffix:
@hanzo/docs-{core,mdx,ui,base-ui} → fumadocs-{core,mdx,ui} / @fumadocs/base-ui
@fumadocs/{story,cli,mdx-remote,tailwind} → @hanzo/docs-{story,cli,...}
fumadocs-{docgen,epub,twoslash,typescript} → @hanzo/docs-{...}
drop phantom eslint-config-custom
Guarded @hanzo/docs-mdx vs -mdx-remote. Also fixes a pre-existing cli bug
(createConfigSchema referenced undefined isSrc) that blocked the package build.
Verified: pnpm install resolves clean (46.8s, was broken); package dist
regenerates phantom-free from the corrected source (hanzo-docs 430→0). Full
app build-green is blocked only by a node v26 toolchain bug in fumadocs-mdx's
config loader (repo targets node >=24.14; CI node builds it).
Renames the public doc-collection method across runtime, types, examples
and create-app templates so the API carries no upstream brand. The
internal helper and all call sites move together; fumadocs-core package
imports are untouched. Pairs with the .docs default outDir change.
BREAKING CHANGE: docs.toFumadocsSource() is now docs.toDocsSource().
The plugin default outDir was 'docs' while every template and example
tsconfig aliased collections/* -> ./.source/*, so a fresh fork could not
resolve `import { docs } from 'collections/server'` without manually
passing createMDX({ outDir }). Standardize on .docs everywhere:
- packages/mdx/src/core.ts: _Defaults.outDir 'docs' -> '.docs'
- 31 template/example tsconfig: collections/* -> ./.docs/*
- align generated-dir .gitignore entries to .docs
- changeset (patch)
Upstream changes include:
- UI: improved TOC detection, clerk variant, slot support
- Base UI: redesigned home layout navigation menu
- CLI: slot support in customise command
- OpenAPI: unsetting auth fields in playground
- Multiple version package releases
All fumadocs-* imports renamed to @hanzo/docs-* namespace.
- Replace docs.hanzo.ai with hanzoai.github.io/docs across 65+ files
(metadata, package.json homepages, READMEs, examples, templates, content)
- Update twitter creator from @fuma_nama to @hanzoai
- Update baseUrl to use GitHub Pages URL instead of Vercel env var
- Update RSS feed base URL