Files
twenty/packages/twenty-front/index.html
T
Félix Malfait 3aeb2b0d5d Self-host Inter and DM Mono fonts (#22506)
Replaces the two Google Fonts stylesheets with self-hosted fonts via
`@fontsource`, imported in the app entry (and Storybook preview), and
removes the duplicate Inter that BlockNote was shipping.

## Why

- **The caching argument for Google Fonts is dead.** Browsers partition
the HTTP cache by top-level site (Chrome 86+, Firefox 85+, Safari even
earlier), so a font cached from another website is never reused on ours.
Every first-time visitor downloads the fonts either way — Google just
adds a detour.
- **Faster first paint.** This removes two render-blocking cross-origin
stylesheets from `index.html` (DNS + TLS to `fonts.googleapis.com`, then
a second connection to `fonts.gstatic.com`, with no preconnect today).
The fonts now ship from our own `/assets` alongside the rest of the app,
behind the same CDN and cache policy.
- **Privacy.** Visitor IPs are no longer sent to Google on every page
load. A German court ruled in 2022 that Google Fonts embedding violates
GDPR, and privacy-conscious self-hosters currently have no way to opt
out of the dependency.
- **Air-gapped / offline self-hosted instances** currently render
fallback system fonts; they now get the real ones.

## Font unification

We were actually loading Inter from two places: the Google stylesheet,
plus `@blocknote/core/fonts/inter.css` (8 weights, latin-only,
woff+woff2) imported by the two rich-text editors — whichever loaded
last won the cascade. Both are gone; `@fontsource` is now the single
source:

- Inter 400/500/600 (theme weights) + 700 (rich-text bold, previously
only covered by the BlockNote copy)
- DM Mono 400/500 (DM Mono has no 600 upstream; the old Google link
requested one anyway)

Fontsource ships the same `unicode-range` subsets as the Google CSS, so
browsers still only download the subset they need (~60KB of woff2 for
latin), and non-latin locales keep full coverage — which the latin-only
BlockNote copy didn't provide.

## Notes

- The BlockNote PDF export (`exportBlockNoteEditorToPdf.ts`) still
fetches Inter TTFs from `fonts.gstatic.com` at export time — react-pdf
needs TTF files, left unchanged here.


<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22506?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-07-03 10:12:27 +02:00

87 lines
2.6 KiB
HTML

<!doctype html>
<html lang="en" translate="no" class="light">
<head>
<meta charset="UTF-8" />
<link
rel="icon"
type="image/x-icon"
href="/images/icons/android/android-launchericon-48-48.png"
data-rh="true"
/>
<link rel="apple-touch-icon" href="/images/icons/ios/192.png" />
<link rel="manifest" href="/manifest.json" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="A modern open-source CRM" />
<meta
property="og:image"
content="https://raw.githubusercontent.com/twentyhq/twenty/main/docs/static/img/social-card.png"
/>
<meta property="og:description" content="A modern open-source CRM" />
<meta property="og:title" content="Twenty" />
<meta name="twitter:card" content="summary_large_image" />
<meta
name="twitter:image"
content="https://raw.githubusercontent.com/twentyhq/twenty/main/docs/static/img/social-card.png"
/>
<meta name="twitter:description" content="A modern open-source CRM" />
<meta name="twitter:title" content="Twenty" />
<title>Twenty</title>
<!-- BEGIN: Twenty Config -->
<script id="twenty-env-config">
window._env_ = {
// This will be overwritten
};
</script>
<!-- END: Twenty Config -->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script type="module">
const disableInputAutoZoom = () => {
const viewportMetadata = document.querySelector('meta[name=viewport]');
if (viewportMetadata !== null) {
viewportMetadata.setAttribute(
'content',
'width=device-width, initial-scale=1.0, maximum-scale=1.0',
);
}
};
const isIOS = /iPad|iPhone/.test(navigator.userAgent);
if (isIOS) {
disableInputAutoZoom();
}
</script>
<style>
:root {
/* THEME_DARK.background.tertiary */
--theme-dark-background-tertiary: #1d1d1d;
/* THEME_LIGHT.background.tertiary */
--theme-light-background-tertiary: #f1f1f1;
}
@media (prefers-color-scheme: dark) {
html,
body {
background-color: var(--theme-dark-background-tertiary);
}
}
@media (prefers-color-scheme: light) {
html,
body {
background-color: var(--theme-light-background-tertiary);
}
}
</style>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script type="module" src="/src/index.tsx"></script>
</body>
</html>