Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@
"tsx": "^4.21.0",
"typescript": "^6.0.2",
"vite": "^8.0.13",
"vite-font": "^0.3.2",
"wrangler": "^4.102.0"
},
"engines": {
Expand Down
78 changes: 78 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 14 additions & 7 deletions src/routes/__root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { QueryClient } from '@tanstack/react-query'
import { createThemeCss, type HighlightTheme } from '@tanstack/highlight/theme'
import { auroraXTheme } from '@tanstack/highlight/themes/aurora-x'
import { githubLightTheme } from '@tanstack/highlight/themes/github-light'
import { css as fontCss, preloads as fontPreloads } from 'virtual:vite-font'
import '~/styles/app.css'
import {
canonicalUrl,
Expand Down Expand Up @@ -180,13 +181,15 @@ export const Route = createRootRouteWithContext<{
],
links: [
...canonicalHeadTags.links,
{
rel: 'preload',
href: '/fonts/Inter-latin.woff2',
as: 'font',
type: 'font/woff2',
crossOrigin: 'anonymous',
},
...fontPreloads.map(
(font): React.LinkHTMLAttributes<HTMLLinkElement> => ({
rel: 'preload',
href: font.href,
as: 'font',
type: font.type,
crossOrigin: 'anonymous',
}),
),
{
rel: 'apple-touch-icon',
sizes: '180x180',
Expand All @@ -207,6 +210,10 @@ export const Route = createRootRouteWithContext<{
{ rel: 'manifest', href: '/site.webmanifest', color: '#fffff' },
{ rel: 'icon', href: '/favicon.ico' },
],
// The @font-face rules plus the metric-matched fallback, inlined so the
// first byte of HTML already carries them — no stylesheet round-trip
// before the browser can start the font.
styles: [{ children: fontCss }],
scripts: [{ children: GOOGLE_ANALYTICS_BOOTSTRAP }],
}
},
Expand Down
31 changes: 5 additions & 26 deletions src/styles/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@ html.theme-switching *::after {
--breakpoint-xs: 480px;

/* Fonts */
/* 'Inter Fallback' is generated by vite-font (INTER_FALLBACK_FAMILY in
vite.config.ts) and must be spelled identically here, or the metric
overrides that hold the layout steady never get applied. */
--font-sans:
'Inter', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji',
'Segoe UI Emoji', Segoe UI Symbol, 'Noto Color Emoji';
'Inter', 'Inter Fallback', ui-sans-serif, system-ui, sans-serif,
'Apple Color Emoji', 'Segoe UI Emoji', Segoe UI Symbol, 'Noto Color Emoji';
--font-shop-display: 'DM Sans', ui-sans-serif, system-ui, sans-serif;
--font-shop-mono: 'DM Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

Expand Down Expand Up @@ -1090,30 +1093,6 @@ mark {
@apply fill-gray-800/90 stroke-gray-500/20;
}

@font-face {
font-display: swap;
font-family: 'Inter';
font-style: normal;
font-weight: 100 900;
src: url('/fonts/Inter-latin-ext.woff2') format('woff2');
unicode-range:
U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
font-display: swap;
font-family: 'Inter';
font-style: normal;
font-weight: 100 900;
src: url('/fonts/Inter-latin.woff2') format('woff2');
unicode-range:
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212,
U+2215, U+FEFF, U+FFFD;
}

@keyframes fadeIn {
from {
opacity: 0;
Expand Down
1 change: 1 addition & 0 deletions src/tanstack-start.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/// <reference types="vite/client" />
/// <reference types="@tanstack/react-start" />
/// <reference types="vite-font/client" />

// This file ensures TanStack Start type augmentations are loaded
// The reference above should trigger the serverRoute module augmentation
Expand Down
46 changes: 46 additions & 0 deletions vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { tanstackStart } from '@tanstack/react-start/plugin/vite'
import tailwindcss from '@tailwindcss/vite'
import { cloudflare } from '@cloudflare/vite-plugin'
import { analyzer } from 'vite-bundle-analyzer'
import viteFont from 'vite-font'
import viteReact from '@vitejs/plugin-react'
import fs from 'node:fs'
import { createRequire } from 'node:module'
Expand All @@ -29,6 +30,20 @@ const shouldBuildSourcemaps =
shouldUseSentryPlugin || process.env.BUILD_SOURCEMAPS === 'true'
const SITE_URL = 'https://tanstack.com'

// vite-font measures the real Inter metrics and emits a fallback @font-face
// under this name. It has to be spelled the same way in the `--font-sans`
// stack in app.css, or the metric overrides never apply.
const INTER_FALLBACK_FAMILY = 'Inter Fallback'

// Inter ships pre-subset in public/fonts, so the ranges have to be declared
// by hand — they are what makes the browser skip latin-ext on a latin page.
const INTER_UNICODE_RANGE = {
latin:
'U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD',
latinExt:
'U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF',
}

const localEnvPath = path.resolve(__dirname, '.env.local')
const defaultCheckoutEnvDir = path.join(os.homedir(), 'GitHub/tanstack.com')
const envDir =
Expand Down Expand Up @@ -331,6 +346,37 @@ export default defineConfig({
: []),
contentCollections(),
tailwindcss(),
// Start renders its own <head>, so there is no HTML entry to transform.
// `__root.tsx` imports the generated tags from `virtual:vite-font` instead.
viteFont({
injectHtml: false,
config: [
{
name: 'Inter',
fallback: 'sans-serif',
fallbackName: INTER_FALLBACK_FAMILY,
display: 'swap',
src: [
{
path: 'public/fonts/Inter-latin.woff2',
style: 'normal',
weight: '100 900',
preload: true,
css: { 'unicode-range': INTER_UNICODE_RANGE.latin },
},
{
// Every page is latin; latin-ext is fetched on demand via
// unicode-range, so preloading it would just cost bandwidth.
path: 'public/fonts/Inter-latin-ext.woff2',
style: 'normal',
weight: '100 900',
preload: false,
css: { 'unicode-range': INTER_UNICODE_RANGE.latinExt },
},
],
},
],
}),
...(process.env.ANALYZE
? [
analyzer({
Expand Down