Docs/THEMING/Custom Fonts

Custom Fonts

GwenUI ships three self-hosted fonts via @gwenui/themes — no Google Fonts requests, zero layout shift.

Included fonts#

GwenUI ships three fonts, all self-hosted in @gwenui/themes:

FontVariableUsage
Plus Jakarta Sans--font-sansBody text, UI labels, buttons
Lora--font-serifDisplay headings, hero sections
Space Mono--font-monoCode blocks, terminals, CLI output

Fonts are self-hosted — no external network requests to Google Fonts. This improves privacy, performance, and works fully offline.

Setup#

Fonts are automatically configured when you run gwen-ui init. They're imported via @gwenui/themes/fonts in your globals.css:

@import "@gwenui/themes/fonts";

This injects @font-face declarations for all three fonts with proper font-display: swap to prevent layout shift.

Using fonts in your code#

Via CSS custom properties

.my-heading {
  font-family: var(--font-serif);   /* Lora */
}
 
.my-body {
  font-family: var(--font-sans);    /* Plus Jakarta Sans */
}
 
.my-code {
  font-family: var(--font-mono);    /* Space Mono */
}

Via Tailwind classes

<h1 className="font-serif">Display heading in Lora</h1>
<p className="font-sans">Body text in Plus Jakarta Sans</p>
<code className="font-mono">Code in Space Mono</code>

Via inline style (for SVG/canvas contexts)

// SVG text elements cannot use CSS custom properties directly
<text fontFamily="Lora, serif" fontWeight="700">
  GwenUI
</text>

Using a custom font#

To replace one of the default fonts, override the token in globals.css:

@import "@gwenui/themes/fonts";   /* keep this for the other fonts */
 
/* Override serif with your own font */
@font-face {
  font-family: 'MyFont';
  src: url('/fonts/myfont.woff2') format('woff2');
  font-display: swap;
}
 
:root {
  --font-serif: 'MyFont', serif;
}

Font weights#

FontAvailable weights
Plus Jakarta Sans300, 400, 500, 600, 700, 800
Lora400, 500, 600, 700
Space Mono400, 700

Performance#

Self-hosted fonts load from the same origin as your app — no DNS lookup, no third-party connection. Combined with font-display: swap, fonts load progressively without blocking rendering.

For further optimization, preload critical fonts in your <head>:

// app/layout.tsx
export default function RootLayout({ children }) {
  return (
    <html>
      <head>
        <link
          rel="preload"
          href="/fonts/plus-jakarta-sans-variable.woff2"
          as="font"
          type="font/woff2"
          crossOrigin="anonymous"
        />
      </head>
      <body>{children}</body>
    </html>
  )
}