From 2ae5b2b50941445efb58829b1b7582e7c97ff089 Mon Sep 17 00:00:00 2001 From: Zama Khan Mohammed Date: Sun, 31 Aug 2025 19:48:13 +0000 Subject: [PATCH 1/3] feat: add fontless to use custom fonts --- app/routes/_marketing+/index.tsx | 2 +- app/styles/tailwind.css | 4 + package-lock.json | 578 +++++++++++++++++++++++++++++-- package.json | 1 + server/index.ts | 4 + vite.config.ts | 2 + 6 files changed, 553 insertions(+), 38 deletions(-) diff --git a/app/routes/_marketing+/index.tsx b/app/routes/_marketing+/index.tsx index ebbb7a9d4..fe18d66a1 100644 --- a/app/routes/_marketing+/index.tsx +++ b/app/routes/_marketing+/index.tsx @@ -50,7 +50,7 @@ export default function Index() {

The Epic Stack

diff --git a/app/styles/tailwind.css b/app/styles/tailwind.css index d52030242..970eceb6c 100644 --- a/app/styles/tailwind.css +++ b/app/styles/tailwind.css @@ -3,6 +3,10 @@ @custom-variant dark (&:is(.dark *)); +.font-title { + font-family: "Panchang", sans-serif; +} + :root { --radius: 0.5rem; diff --git a/package-lock.json b/package-lock.json index 02c417d12..2d481f541 100644 --- a/package-lock.json +++ b/package-lock.json @@ -53,6 +53,7 @@ "execa": "^9.5.2", "express": "^4.21.2", "express-rate-limit": "^7.5.0", + "fontless": "^0.0.2", "get-port": "^7.1.0", "glob": "^11.0.2", "input-otp": "^1.4.2", @@ -786,6 +787,21 @@ "node": ">= 4.0.0" } }, + "node_modules/@capsizecss/metrics": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@capsizecss/metrics/-/metrics-3.5.0.tgz", + "integrity": "sha512-Ju2I/Qn3c1OaU8FgeW4Tc22D4C9NwyVfKzNmzst59bvxBjPoLYNZMqFYn+HvCtn4MpXwiaDtCE8fNuQLpdi9yA==" + }, + "node_modules/@capsizecss/unpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-2.4.0.tgz", + "integrity": "sha512-GrSU71meACqcmIUxPYOJvGKF0yryjN/L1aCuE9DViCTJI7bfkjgYDPD1zbNDcINJwSSP6UaBZY9GAbYDO7re0Q==", + "dependencies": { + "blob-to-buffer": "^1.2.8", + "cross-fetch": "^3.0.4", + "fontkit": "^2.0.2" + } + }, "node_modules/@conform-to/dom": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/@conform-to/dom/-/dom-1.7.2.tgz", @@ -1183,7 +1199,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1200,7 +1215,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1217,7 +1231,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1234,7 +1247,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1251,7 +1263,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1268,7 +1279,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1285,7 +1295,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1302,7 +1311,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1319,7 +1327,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1336,7 +1343,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1353,7 +1359,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1370,7 +1375,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1387,7 +1391,6 @@ "cpu": [ "mips64el" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1404,7 +1407,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1421,7 +1423,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1438,7 +1439,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1455,7 +1455,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1472,7 +1471,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1489,7 +1487,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1506,7 +1503,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1523,7 +1519,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1540,7 +1535,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1557,7 +1551,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1574,7 +1567,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1591,7 +1583,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2528,6 +2519,15 @@ "node": ">=6.0.0" } }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -2547,10 +2547,9 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "license": "MIT" + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", @@ -6081,6 +6080,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@swc/helpers": { + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", + "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", + "dependencies": { + "tslib": "^2.8.0" + } + }, "node_modules/@tailwindcss/node": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.11.tgz", @@ -6607,7 +6614,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, "license": "MIT" }, "node_modules/@types/express": { @@ -8257,7 +8263,6 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, "funding": [ { "type": "github", @@ -8378,6 +8383,25 @@ "node": ">= 6" } }, + "node_modules/blob-to-buffer": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/blob-to-buffer/-/blob-to-buffer-1.2.9.tgz", + "integrity": "sha512-BF033y5fN6OCofD3vgHmNtwZWRcq9NLyyxyILx9hfMy1sXYy4ojFl765hJ2lP0YaN2fuxPaLO2Vzzoxy0FLFFA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/body-parser": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", @@ -8445,6 +8469,14 @@ "node": ">=8" } }, + "node_modules/brotli": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", + "dependencies": { + "base64-js": "^1.1.2" + } + }, "node_modules/browserslist": { "version": "4.25.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", @@ -8669,7 +8701,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, "license": "MIT", "dependencies": { "readdirp": "^4.0.1" @@ -8826,7 +8857,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.8" @@ -8996,6 +9026,11 @@ "typedarray": "^0.0.6" } }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==" + }, "node_modules/config-chain": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", @@ -9007,6 +9042,14 @@ "proto-list": "~1.2.1" } }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -9043,6 +9086,11 @@ "node": ">=18" } }, + "node_modules/cookie-es": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.2.tgz", + "integrity": "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==" + }, "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", @@ -9101,6 +9149,14 @@ "yarn": ">=1" } }, + "node_modules/cross-fetch": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", + "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", + "dependencies": { + "node-fetch": "^2.7.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -9130,6 +9186,14 @@ "node": ">= 8" } }, + "node_modules/crossws": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz", + "integrity": "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==", + "dependencies": { + "uncrypto": "^0.1.3" + } + }, "node_modules/css-select": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", @@ -9147,6 +9211,18 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/css-tree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", + "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", + "dependencies": { + "mdn-data": "2.12.2", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, "node_modules/css-what": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", @@ -9556,6 +9632,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==" + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -9585,6 +9666,11 @@ "node": ">=6" } }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==" + }, "node_modules/destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", @@ -9610,6 +9696,11 @@ "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", "license": "MIT" }, + "node_modules/dfa": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", + "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==" + }, "node_modules/diff": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", @@ -10056,7 +10147,6 @@ "version": "0.25.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", - "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -10563,7 +10653,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, "license": "MIT", "dependencies": { "@types/estree": "^1.0.0" @@ -10726,7 +10815,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, "license": "MIT" }, "node_modules/fast-glob": { @@ -10915,6 +11003,115 @@ "dev": true, "license": "ISC" }, + "node_modules/fontaine": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/fontaine/-/fontaine-0.6.0.tgz", + "integrity": "sha512-cfKqzB62GmztJhwJ0YXtzNsmpqKAcFzTqsakJ//5COTzbou90LU7So18U+4D8z+lDXr4uztaAUZBonSoPDcj1w==", + "dependencies": { + "@capsizecss/metrics": "^3.5.0", + "@capsizecss/unpack": "^2.4.0", + "css-tree": "^3.1.0", + "magic-regexp": "^0.10.0", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "ufo": "^1.6.1", + "unplugin": "^2.3.2" + } + }, + "node_modules/fontaine/node_modules/magic-string": { + "version": "0.30.18", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz", + "integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/fontaine/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==" + }, + "node_modules/fontaine/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/fontaine/node_modules/unplugin": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.9.tgz", + "integrity": "sha512-2dcbZq6aprwXTkzptq3k5qm5B8cvpjG9ynPd5fyM2wDJuuF7PeUK64Sxf0d+X1ZyDOeGydbNzMqBSIVlH8GIfA==", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "acorn": "^8.15.0", + "picomatch": "^4.0.3", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/fontaine/node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==" + }, + "node_modules/fontkit": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-2.0.4.tgz", + "integrity": "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==", + "dependencies": { + "@swc/helpers": "^0.5.12", + "brotli": "^1.3.2", + "clone": "^2.1.2", + "dfa": "^1.2.0", + "fast-deep-equal": "^3.1.3", + "restructure": "^3.0.0", + "tiny-inflate": "^1.0.3", + "unicode-properties": "^1.4.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/fontless": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/fontless/-/fontless-0.0.2.tgz", + "integrity": "sha512-duDdbyLChMc7YGnBsO25kwqjWJOxr0Noy3NHJeYkQCUA4MlL+lp4eeNx4X4OLLOpkCVUMu1fN2IZuoGjJJAhXg==", + "dependencies": { + "consola": "^3.4.2", + "css-tree": "^3.1.0", + "defu": "^6.1.4", + "esbuild": "^0.25.4", + "fontaine": "0.6.0", + "jiti": "^2.4.2", + "magic-string": "^0.30.17", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "ufo": "^1.6.1", + "unifont": "^0.5.0", + "unstorage": "^1.16.0" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/fontless/node_modules/magic-string": { + "version": "0.30.18", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz", + "integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/fontless/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==" + }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -11353,6 +11550,22 @@ "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } }, + "node_modules/h3": { + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.4.tgz", + "integrity": "sha512-z5cFQWDffyOe4vQ9xIqNfCZdV4p//vy6fBnr8Q1AWnVZ0teurKMG66rLj++TKwKPUP3u7iMUvrvKaEUiQw2QWQ==", + "dependencies": { + "cookie-es": "^1.2.2", + "crossws": "^0.3.5", + "defu": "^6.1.4", + "destr": "^2.0.5", + "iron-webcrypto": "^1.2.1", + "node-mock-http": "^1.0.2", + "radix3": "^1.1.2", + "ufo": "^1.6.1", + "uncrypto": "^0.1.3" + } + }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -11778,6 +11991,14 @@ "node": ">= 0.10" } }, + "node_modules/iron-webcrypto": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", + "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==", + "funding": { + "url": "https://github.com/sponsors/brc-dd" + } + }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -13098,6 +13319,58 @@ "lz-string": "bin/bin.js" } }, + "node_modules/magic-regexp": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/magic-regexp/-/magic-regexp-0.10.0.tgz", + "integrity": "sha512-Uly1Bu4lO1hwHUW0CQeSWuRtzCMNO00CmXtS8N6fyvB3B979GOEEeAkiTUDsmbYLAbvpUS/Kt5c4ibosAzVyVg==", + "dependencies": { + "estree-walker": "^3.0.3", + "magic-string": "^0.30.12", + "mlly": "^1.7.2", + "regexp-tree": "^0.1.27", + "type-level-regexp": "~0.1.17", + "ufo": "^1.5.4", + "unplugin": "^2.0.0" + } + }, + "node_modules/magic-regexp/node_modules/magic-string": { + "version": "0.30.18", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz", + "integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magic-regexp/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/magic-regexp/node_modules/unplugin": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.9.tgz", + "integrity": "sha512-2dcbZq6aprwXTkzptq3k5qm5B8cvpjG9ynPd5fyM2wDJuuF7PeUK64Sxf0d+X1ZyDOeGydbNzMqBSIVlH8GIfA==", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "acorn": "^8.15.0", + "picomatch": "^4.0.3", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/magic-regexp/node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==" + }, "node_modules/magic-string": { "version": "0.30.8", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", @@ -13171,6 +13444,11 @@ "react": "^18.0 || ^19.0" } }, + "node_modules/mdn-data": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", + "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==" + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -13348,6 +13626,22 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/mlly": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz", + "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", + "dependencies": { + "acorn": "^8.15.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.1" + } + }, + "node_modules/mlly/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==" + }, "node_modules/module-details-from-path": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", @@ -13609,6 +13903,11 @@ } } }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==" + }, "node_modules/node-fetch/node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -13642,6 +13941,11 @@ "he": "1.2.0" } }, + "node_modules/node-mock-http": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.2.tgz", + "integrity": "sha512-zWaamgDUdo9SSLw47we78+zYw/bDr5gH8pH7oRRs8V3KmBtu8GLgGIbV2p/gRPd3LWpEOpjQj7X1FOU3VFMJ8g==" + }, "node_modules/node-releases": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", @@ -14114,6 +14418,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ofetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.4.1.tgz", + "integrity": "sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==", + "dependencies": { + "destr": "^2.0.3", + "node-fetch-native": "^1.6.4", + "ufo": "^1.5.4" + } + }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==" + }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -14302,6 +14621,11 @@ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "license": "BlueOak-1.0.0" }, + "node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -14566,6 +14890,21 @@ "node": ">=4" } }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/pkg-types/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==" + }, "node_modules/playwright": { "version": "1.53.1", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.53.1.tgz", @@ -15244,6 +15583,11 @@ ], "license": "MIT" }, + "node_modules/radix3": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", + "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==" + }, "node_modules/railroad-diagrams": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", @@ -15671,7 +16015,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 14.18.0" @@ -15718,6 +16061,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/regexp-tree": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", + "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", + "bin": { + "regexp-tree": "bin/regexp-tree" + } + }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", @@ -15997,6 +16348,11 @@ "dev": true, "license": "ISC" }, + "node_modules/restructure": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz", + "integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==" + }, "node_modules/ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", @@ -17263,6 +17619,11 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==" + }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -17542,6 +17903,11 @@ "node": ">= 0.6" } }, + "node_modules/type-level-regexp": { + "version": "0.1.17", + "resolved": "https://registry.npmjs.org/type-level-regexp/-/type-level-regexp-0.1.17.tgz", + "integrity": "sha512-wTk4DH3cxwk196uGLK/E9pE45aLfeKJacKmcEgEOA/q5dnPGNxXt0cfYdFxb57L+sEpf1oJH4Dnx/pnRcku9jg==" + }, "node_modules/typed-array-buffer": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", @@ -17664,6 +18030,11 @@ "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/ufo": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", + "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==" + }, "node_modules/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", @@ -17683,12 +18054,35 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/uncrypto": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", + "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==" + }, "node_modules/undici-types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "license": "MIT" }, + "node_modules/unicode-properties": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", + "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", + "dependencies": { + "base64-js": "^1.3.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/unicode-trie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", + "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", + "dependencies": { + "pako": "^0.2.5", + "tiny-inflate": "^1.0.0" + } + }, "node_modules/unicorn-magic": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", @@ -17701,6 +18095,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/unifont": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.5.2.tgz", + "integrity": "sha512-LzR4WUqzH9ILFvjLAUU7dK3Lnou/qd5kD+IakBtBK4S15/+x2y9VX+DcWQv6s551R6W+vzwgVS6tFg3XggGBgg==", + "dependencies": { + "css-tree": "^3.0.0", + "ofetch": "^1.4.1", + "ohash": "^2.0.0" + } + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -17815,6 +18219,106 @@ "@unrs/resolver-binding-win32-x64-msvc": "1.9.2" } }, + "node_modules/unstorage": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.0.tgz", + "integrity": "sha512-l9Z7lBiwtNp8ZmcoZ/dmPkFXFdtEdZtTZafCSnEIj3YvtkXeGAtL2rN8MQFy/0cs4eOLpuRJMp9ivdug7TCvww==", + "dependencies": { + "anymatch": "^3.1.3", + "chokidar": "^4.0.3", + "destr": "^2.0.5", + "h3": "^1.15.4", + "lru-cache": "^10.4.3", + "node-fetch-native": "^1.6.7", + "ofetch": "^1.4.1", + "ufo": "^1.6.1" + }, + "peerDependencies": { + "@azure/app-configuration": "^1.8.0", + "@azure/cosmos": "^4.2.0", + "@azure/data-tables": "^13.3.0", + "@azure/identity": "^4.6.0", + "@azure/keyvault-secrets": "^4.9.0", + "@azure/storage-blob": "^12.26.0", + "@capacitor/preferences": "^6.0.3 || ^7.0.0", + "@deno/kv": ">=0.9.0", + "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", + "@planetscale/database": "^1.19.0", + "@upstash/redis": "^1.34.3", + "@vercel/blob": ">=0.27.1", + "@vercel/functions": "^2.2.12", + "@vercel/kv": "^1.0.1", + "aws4fetch": "^1.0.20", + "db0": ">=0.2.1", + "idb-keyval": "^6.2.1", + "ioredis": "^5.4.2", + "uploadthing": "^7.4.4" + }, + "peerDependenciesMeta": { + "@azure/app-configuration": { + "optional": true + }, + "@azure/cosmos": { + "optional": true + }, + "@azure/data-tables": { + "optional": true + }, + "@azure/identity": { + "optional": true + }, + "@azure/keyvault-secrets": { + "optional": true + }, + "@azure/storage-blob": { + "optional": true + }, + "@capacitor/preferences": { + "optional": true + }, + "@deno/kv": { + "optional": true + }, + "@netlify/blobs": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@vercel/blob": { + "optional": true + }, + "@vercel/functions": { + "optional": true + }, + "@vercel/kv": { + "optional": true + }, + "aws4fetch": { + "optional": true + }, + "db0": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "uploadthing": { + "optional": true + } + } + }, + "node_modules/unstorage/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" + }, "node_modules/update-browserslist-db": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", diff --git a/package.json b/package.json index ef4672bd7..90c2ba604 100644 --- a/package.json +++ b/package.json @@ -84,6 +84,7 @@ "execa": "^9.5.2", "express": "^4.21.2", "express-rate-limit": "^7.5.0", + "fontless": "^0.0.2", "get-port": "^7.1.0", "glob": "^11.0.2", "input-otp": "^1.4.2", diff --git a/server/index.ts b/server/index.ts index b55f22cf1..f9444cfef 100644 --- a/server/index.ts +++ b/server/index.ts @@ -80,6 +80,10 @@ app.use((_, res, next) => { if (viteDevServer) { app.use(viteDevServer.middlewares) + app.use( + '/_fonts', + express.static('build/client/_fonts', { immutable: true, maxAge: '1y' }), + ) } else { // Remix fingerprints its assets so we can cache forever. app.use( diff --git a/vite.config.ts b/vite.config.ts index 1f10a67c9..586136ec2 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -3,6 +3,7 @@ import { type SentryReactRouterBuildOptions, sentryReactRouter, } from '@sentry/react-router' +import { fontless } from 'fontless' import tailwindcss from '@tailwindcss/vite' import { reactRouterDevTools } from 'react-router-devtools' import { defineConfig } from 'vite' @@ -55,6 +56,7 @@ export default defineConfig((config) => ({ MODE === 'production' && process.env.SENTRY_AUTH_TOKEN ? sentryReactRouter(sentryConfig, config) : null, + fontless(), ], test: { include: ['./app/**/*.test.{ts,tsx}'], From 889d124b69ee5ee0822b8eeb8662ed215a52189d Mon Sep 17 00:00:00 2001 From: Zama Khan Mohammed Date: Mon, 1 Sep 2025 14:59:02 +0000 Subject: [PATCH 2/3] update the docs for fonts --- app/styles/tailwind.css | 2 +- docs/fonts.md | 156 ++++++---------------------------------- 2 files changed, 21 insertions(+), 137 deletions(-) diff --git a/app/styles/tailwind.css b/app/styles/tailwind.css index 970eceb6c..523f0ed87 100644 --- a/app/styles/tailwind.css +++ b/app/styles/tailwind.css @@ -4,7 +4,7 @@ @custom-variant dark (&:is(.dark *)); .font-title { - font-family: "Panchang", sans-serif; + font-family: "Poppins", sans-serif; } :root { diff --git a/docs/fonts.md b/docs/fonts.md index a069f37fa..4af2472e5 100644 --- a/docs/fonts.md +++ b/docs/fonts.md @@ -6,147 +6,31 @@ easy to do so. ## Using Custom Fonts -You can use custom fonts by creating the `./public/fonts` directory (if it -doesn't already exist) and adding them to it. -[Google Fonts](https://fonts.google.com/) is a good place to find open source -fonts. You will also need to add the `css` file for the font to the -`./app/styles` directory, if your font doesn't come with one (Google Fonts -don't) you can generate one using a tool like -[Transfonter](https://transfonter.org/). Transfonter now has a fonts directory -setting. Set that to `fonts` to have the `url` preset. +For custom fonts, Epic Stack uses [`fontless`](https://github.com/unjs/fontaine/tree/main/packages/fontless) for automatic font optimization. This provides zero-runtime font loading with proper fallbacks to reduce Cumulative Layout Shift (CLS). -Verify the `url` in the `css` is relative to the `public` folder. So it should -look something like `url('/fonts/yourfont/yourfont-200.woff2')`. +## How it works -Now you've added your font, there's a few places you need to update to use it. +Simply use font families in your CSS and `fontless` handles the rest: -1. Add your font to the CSS variables. +```css +.font-title { + font-family: "Poppins", sans-serif; +} - ```css - /* tailwind.css */ - @layer base { - :root { - --font-sans: ; - } - } - ``` +.body-text { + font-family: "Inter", sans-serif; +} +``` -2. Import the default theme and add your font to the `fontFamily` property. +The plugin will: +- Detect font-family declarations in your CSS +- Resolve fonts from providers (Google Fonts, Bunny Fonts, etc.) +- Generate optimized `@font-face` declarations +- Add metric-based fallback fonts to reduce CLS +- Download and serve font assets from `/_fonts/` - ```ts - import defaultTheme from 'tailwindcss/defaultTheme.js' - // tailwind.config.ts - extend: { - ...extendedTheme, - fontFamily: { - sans: ['var(--font-sans)', ...defaultTheme.fontFamily.sans], - } - } +## Troubleshooting - ``` +### Fonts not loading in development +This is expected behavior. Fonts are only generated in the build files. To see fonts in development mode, build the application first and then run `npm run dev`. -3. Import your font stylesheet. - - ```tsx - // app/routes/root.tsx - import fontStyleSheetUrl from './styles/yourfont.css?url' - ``` - - Add the font stylesheet to the links array. - - ```tsx - // app/routes/root.tsx - ... - { rel: 'preload', href: fontStyleSheetUrl, as: 'style' }, - { rel: 'stylesheet', href: fontStyleSheetUrl }, - ``` - -4. Expose and cache your fonts folder. - - ```ts - // server/index.ts - ... - app.use( - '/fonts', - // Can aggressively cache fonts as they don't change often - express.static('public/fonts', { immutable: true, maxAge: '1y' }), - ) - ``` - -That's it! You can now use your custom font should now be available to use in -your site. - -## Font Metric Overrides - -When using custom fonts, your site elements may stretch or shrink to accommodate -the font. This is because the browser doesn't know the dimensions of the font -you're using until it arrives, which introduces Cumulative Layout Shift and -impact its web vitals. - -In Epic Stack, we fixed this by introducing -[Font Metric Overrides](https://github.com/epicweb-dev/epic-stack/pull/128/files). - -Follow the steps below to add Font Metric Overrides to your custom fonts. - -1. Generate the overrides for your font. - - You can use [fontpie](https://www.npmjs.com/package/fontpie) utility to - generate the overrides. For each of your fonts, write the following in your - terminal: - - ```bash - npx fontpie ./local/font/location.woff2 -w font-weight -s normal/italic -n YourFont - ``` - - #### Example - - ```sh - npx fontpie ./public/fonts/nunito-sans/nunito-sans-v12-latin_latin-ext-200.woff2 -w 200 -s normal -n NunitoSans - ``` - - ```css - @font-face { - font-family: 'NunitoSans Fallback'; - font-style: normal; - font-weight: 200; - src: local('Arial'); - ascent-override: 103.02%; - descent-override: 35.97%; - line-gap-override: 0%; - size-adjust: 98.13%; - } - ``` - - If you've got a lot of font files to override, you can use - [fontpie-from-css](https://github.com/matt-kinton/fontpie-from-css) to - generate the overrides from a CSS file. - - ```sh - npx fontpie-from-css ./public/fonts/yourfont/yourfont.css - ``` - - **_Note:_** _If you've been following the steps above, you might have to copy - your `yourfont.css` file temporarily to the `./public` folder as - `fontpie-from-css` loads fonts relative to the CSS file._ - -2. Add the overrides to your font stylesheet. - - Use fontpie for every custom font used (including variants) or - fontpie-from-css and add the metric overrides to `yourfont.css`. - - _Ensure the original font has the `font-display: swap` property or the - fallback wouldn't work!_ - -3. Add the font fallback to the stylesheet. - - ```css - /* tailwind.css */ - @layer base { - :root { - --font-sans: ; - } - } - ``` - -That's it! You can now use your custom font without worrying about Cumulative -Layout Shift! From b475a0a12530beb05c6e83284a3e66d7f0a32d87 Mon Sep 17 00:00:00 2001 From: "Kent C. Dodds" Date: Tue, 2 Sep 2025 21:49:30 -0700 Subject: [PATCH 3/3] format --- app/routes/_marketing+/index.tsx | 2 +- app/styles/tailwind.css | 2 +- docs/fonts.md | 14 ++++++++++---- server/index.ts | 2 +- vite.config.ts | 2 +- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/app/routes/_marketing+/index.tsx b/app/routes/_marketing+/index.tsx index fe18d66a1..30c987950 100644 --- a/app/routes/_marketing+/index.tsx +++ b/app/routes/_marketing+/index.tsx @@ -50,7 +50,7 @@ export default function Index() {

The Epic Stack

diff --git a/app/styles/tailwind.css b/app/styles/tailwind.css index 523f0ed87..8056abc17 100644 --- a/app/styles/tailwind.css +++ b/app/styles/tailwind.css @@ -4,7 +4,7 @@ @custom-variant dark (&:is(.dark *)); .font-title { - font-family: "Poppins", sans-serif; + font-family: 'Poppins', sans-serif; } :root { diff --git a/docs/fonts.md b/docs/fonts.md index 4af2472e5..38d39ee89 100644 --- a/docs/fonts.md +++ b/docs/fonts.md @@ -6,7 +6,10 @@ easy to do so. ## Using Custom Fonts -For custom fonts, Epic Stack uses [`fontless`](https://github.com/unjs/fontaine/tree/main/packages/fontless) for automatic font optimization. This provides zero-runtime font loading with proper fallbacks to reduce Cumulative Layout Shift (CLS). +For custom fonts, Epic Stack uses +[`fontless`](https://github.com/unjs/fontaine/tree/main/packages/fontless) for +automatic font optimization. This provides zero-runtime font loading with proper +fallbacks to reduce Cumulative Layout Shift (CLS). ## How it works @@ -14,15 +17,16 @@ Simply use font families in your CSS and `fontless` handles the rest: ```css .font-title { - font-family: "Poppins", sans-serif; + font-family: 'Poppins', sans-serif; } .body-text { - font-family: "Inter", sans-serif; + font-family: 'Inter', sans-serif; } ``` The plugin will: + - Detect font-family declarations in your CSS - Resolve fonts from providers (Google Fonts, Bunny Fonts, etc.) - Generate optimized `@font-face` declarations @@ -32,5 +36,7 @@ The plugin will: ## Troubleshooting ### Fonts not loading in development -This is expected behavior. Fonts are only generated in the build files. To see fonts in development mode, build the application first and then run `npm run dev`. +This is expected behavior. Fonts are only generated in the build files. To see +fonts in development mode, build the application first and then run +`npm run dev`. diff --git a/server/index.ts b/server/index.ts index f9444cfef..9d56ecc6b 100644 --- a/server/index.ts +++ b/server/index.ts @@ -28,7 +28,7 @@ const viteDevServer = IS_PROD server: { middlewareMode: true, }, - // We tell Vite we are running a custom app instead of + // We tell Vite we are running a custom app instead of // the SPA default so it doesn't run HTML middleware appType: 'custom', }), diff --git a/vite.config.ts b/vite.config.ts index 586136ec2..0dab07e39 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -3,8 +3,8 @@ import { type SentryReactRouterBuildOptions, sentryReactRouter, } from '@sentry/react-router' -import { fontless } from 'fontless' import tailwindcss from '@tailwindcss/vite' +import { fontless } from 'fontless' import { reactRouterDevTools } from 'react-router-devtools' import { defineConfig } from 'vite' import { envOnlyMacros } from 'vite-env-only'