Summary
The library loads fonts itself. packages/raystack/styles/typography.css starts with four Google Fonts @import calls:
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500&display=swap");
@import url("https://fonts.googleapis.com/css2?family=Lora:wght@400;500&display=swap");
@import url("https://fonts.googleapis.com/css2?family=Josefin+Sans:wght@400;500&display=swap");
@import url("https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital@0;1&display=swap");
A component library should not decide how or where fonts load. That belongs to the app. We should remove these imports and keep only the --rs-font-* variables (the font-family names), then let each app load the fonts it needs.
Why this is a problem
- Consumers download fonts they never use. Any app that pulls in Apsara fetches Inter, Lora, Josefin Sans, and JetBrains Mono — even if it only uses Inter. Lora and Josefin Sans only apply under
[data-style="traditional"], so most apps pay for them for nothing.
@import blocks render. Fonts inside a CSS @import are found late by the browser, which delays paint and causes a flash of unstyled text (FOUT).
- Extra connections. Each
@import needs DNS + TLS to Google's domains before the font even downloads.
- No control for the app. The app can't
preload the font, can't set font-display, and can't self-host. All of that is decided inside the library.
- Blocks other improvements. We can't cleanly add a 600 weight or use Inter's optical-sizing (
opsz) axis, because the library pins the exact Google Fonts URL and weights.
Proposal
- Remove the four
@import lines from packages/raystack/styles/typography.css.
- Keep the
--rs-font-* variables (Inter, Lora, Josefin Sans, JetBrains Mono, Menlo) so component styles stay unchanged.
- Document how consuming apps load the fonts, for example:
- Self-host with Fontsource —
@fontsource-variable/inter (variable font, all weights from one file).
next/font for Next.js apps.
- A plain
@font-face or Google Fonts <link> for apps that want it.
- Note the optional families (Lora, Josefin Sans) are only needed for the
traditional style.
Breaking change
This is a breaking change. Apps that rely on Apsara loading fonts will render in a fallback font until they load the fonts themselves. The migration is small — install the fonts and import them once at the app entry — but it must be called out in release notes with a clear before/after.
Acceptance criteria
Summary
The library loads fonts itself.
packages/raystack/styles/typography.cssstarts with four Google Fonts@importcalls:A component library should not decide how or where fonts load. That belongs to the app. We should remove these imports and keep only the
--rs-font-*variables (the font-family names), then let each app load the fonts it needs.Why this is a problem
[data-style="traditional"], so most apps pay for them for nothing.@importblocks render. Fonts inside a CSS@importare found late by the browser, which delays paint and causes a flash of unstyled text (FOUT).@importneeds DNS + TLS to Google's domains before the font even downloads.preloadthe font, can't setfont-display, and can't self-host. All of that is decided inside the library.opsz) axis, because the library pins the exact Google Fonts URL and weights.Proposal
@importlines frompackages/raystack/styles/typography.css.--rs-font-*variables (Inter, Lora, Josefin Sans, JetBrains Mono, Menlo) so component styles stay unchanged.@fontsource-variable/inter(variable font, all weights from one file).next/fontfor Next.js apps.@font-faceor Google Fonts<link>for apps that want it.traditionalstyle.Breaking change
This is a breaking change. Apps that rely on Apsara loading fonts will render in a fallback font until they load the fonts themselves. The migration is small — install the fonts and import them once at the app entry — but it must be called out in release notes with a clear before/after.
Acceptance criteria
@importof font files in the library CSS.--rs-font-*variables still present; components render identically once the app loads Inter.