npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sveltopia/fonts

v0.1.0

Published

Curated, typed Google Fonts with zero-CLS fallback calibration.

Readme

@sveltopia/fonts

Curated, typed Google Fonts with zero-CLS fallback calibration.

Status: v0.1 alpha. Library API + self-hosting (src config) shipped on npm as 0.1.0-alpha.1. CLI ergonomics for sv-ui consumers ship in v0.1.1+.

What it does

Three things, scoped narrowly:

  1. Curated typed font catalog. Pick from 15 popular Google Fonts by name (autocomplete, typo-proof) without maintaining a parallel JSON config.
  2. Zero-CLS fallback calibration. Emits @font-face rules for system fallback fonts (Arial, Times New Roman, Courier New) with Capsize-computed ascent-override, descent-override, and size-adjust values that make the fallback occupy the same layout space as your primary. When the primary loads, nothing visibly shifts.
  3. Sensible per-category defaults. Sans/serif/mono primaries each get a default calibrated fallback and a default generic tail (system-ui, sans-serif for sans, Georgia, serif for serif, ui-monospace, monospace for mono). Drop in and use; override when you need to.

What this library does not do by default: normalize a primary font's vertical alignment, fix the 1px sub-pixel offset some fonts exhibit at compact sizes, or apply layout trim. Those are browser-rendering or layout concerns outside the scope of @font-face calibration.

For select curated fonts where vertical alignment can be meaningfully improved via @font-face metric overrides, an opt-in tuneVerticalMetrics flag is available (off by default; see Tuned vertical metrics below). Currently scoped to Source Sans 3 — additional fonts get tuned values only after empirical validation, never speculatively.

Concepts

If you've never thought about font metrics before, here's the mental model in three short paragraphs.

The problem this library solves: Cumulative Layout Shift (CLS) on font swap. When a page loads, the browser doesn't have your custom font yet. It renders text using a fallback (usually a system font like Arial) while it fetches your font. Once your font finishes loading, the browser swaps it in. If your font's character widths or line height differ from the fallback's — and they almost always do — text reflows during the swap. Buttons jump. Headings shift. Layouts break briefly. That's CLS, and Google penalizes pages for it in Core Web Vitals.

Why a CSS-level fix exists. Each font file declares its own ascent / descent / line-gap / x-height in its OS/2 metric tables. Modern CSS lets you redeclare those values via @font-face descriptors (ascent-override, descent-override, line-gap-override, size-adjust). If you can compute the right values, you can make Arial visually occupy the same dimensions as Source Sans 3 — same x-height, same character width, same line-box. The swap becomes invisible.

What this library does with that. Computes those right values for you. Given any curated font (or a custom font with known metrics), it emits a calibrated @font-face block for a chosen system fallback. Drop the emitted CSS into your global stylesheet, set the font-family chain accordingly, and CLS on font load is gone. The math comes from Capsize; this library wraps Capsize with curated font data, sensible defaults, and a typed API.

Usage

import { createFontPreset } from "@sveltopia/fonts";

const { css, fontFamily } = createFontPreset({
  family: "Source Sans 3",
  weights: [400, 500, 600, 700],
});

// css        — @font-face blocks (primary + Arial calibrated fallback)
// fontFamily — '"Source Sans 3", "Source Sans 3 Fallback", Arial, system-ui, sans-serif'

Drop css once into your global stylesheet, set font-family: ... (or assign fontFamily to a CSS variable), and you have CLS-free font loading.

Per-category defaults

Omit fallbacks and tail to get sane defaults based on the primary's category (metrics.category from Capsize):

| Category | Default fallbacks | Default tail | | ---------- | --------------------- | ------------------------------- | | sans-serif | ['Arial'] | ['system-ui', 'sans-serif'] | | serif | ['Times New Roman'] | ['Georgia', 'serif'] | | monospace | ['Courier New'] | ['ui-monospace', 'monospace'] |

The tail default is opinion, not contract — pinned to the values above for v0.1 stability, but you should override tail if your design system expects a different generic chain.

Overriding defaults

Each category has the same override knobs. Use whichever applies:

// Sans: swap the calibrated fallback (Arial → Helvetica)
createFontPreset({
  family: "Source Sans 3",
  fallbacks: ["Helvetica"],
});

// Serif: explicit Georgia override (instead of default Times New Roman)
createFontPreset({
  family: "Source Serif 4",
  fallbacks: ["Georgia"],
});

// Mono: stick with the default Courier New, customize the generic tail
createFontPreset({
  family: "JetBrains Mono",
  tail: ["Menlo", "Consolas", "monospace"],
});

// Opt out of fallbacks or tail entirely (empty array, not undefined)
createFontPreset({
  family: "Source Sans 3",
  fallbacks: [],
  tail: [],
});
// fontFamily — '"Source Sans 3"'

Multiple categories at once

For design systems with sans + serif + mono, use createFontTheme to set up all three in one call:

import { createFontTheme } from "@sveltopia/fonts";

const theme = createFontTheme({
  sans: "Source Sans 3",
  serif: "Source Serif 4",
  mono: "JetBrains Mono",
});

// theme.css                  — combined @font-face blocks for all three
// theme.sans.fontFamily      — '"Source Sans 3", "Source Sans 3 Fallback: Arial", Arial, system-ui, sans-serif'
// theme.serif.fontFamily     — serif chain
// theme.mono.fontFamily      — mono chain

Each entry can be a family string, a { name, metrics } custom-font input, or a full FontPresetConfig for fine-grained control:

createFontTheme({
  sans: { family: "Source Sans 3", weights: [400, 700] },
  serif: "Lora",
  // mono omitted — theme.mono will be undefined
});

Hosting

Two paths for actually loading the font files. Pick whichever fits your stack.

Remote (Google Fonts)

Every preset returns a googleFontsHref — a ready-to-use Google Fonts CSS URL. The canonical pattern pairs it with preconnect hints so the browser opens TCP/TLS connections to fonts.googleapis.com and fonts.gstatic.com before the stylesheet request fires:

<script>
  import { theme } from '$lib/sveltopia/fonts';
</script>

<svelte:head>
  <link rel="preconnect" href="https://fonts.googleapis.com" />
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
  <link href={theme.googleFontsHref} rel="stylesheet" />
</svelte:head>

createFontTheme returns a single combined URL for all three categories — one stylesheet <link> instead of three.

For tighter timing, you can also add <link rel="preload" href={theme.googleFontsHref} as="style" /> above the stylesheet. The marginal benefit is smaller than preconnect's, but it shaves a small amount off first-contentful-paint when the CSS file is the critical path.

v0.1 limitation: single-axis variable fonts only. googleFontsHref currently emits URLs that specify only the wght axis. For variable fonts with additional axes (opsz, ital, etc.) — including Source Serif 4, Lora, Merriweather, Playfair Display, and Noto Serif from the curated catalog — Google's CSS2 endpoint rejects the under-specified URL with HTTP 400. Multi-axis support is planned for v0.1.x; for now, prefer self-hosting via src for those fonts.

Self-hosted

Pass a src URL pointing at your hosted woff2 (or other supported format). The library emits a src: url(...) format(...) chain, with local() entries appended so returning visitors who already have the font installed skip the network round-trip:

createFontPreset({
  family: "Source Sans 3",
  src: "/fonts/source-sans-3.woff2",
});

// Emitted CSS:
// @font-face {
//   font-family: "Source Sans 3";
//   src: url('/fonts/source-sans-3.woff2') format('woff2'),
//        local('Source Sans 3 Regular'), local('SourceSans3-Regular'), local('Source Sans 3');
//   ...
// }

Format is inferred from the file extension. Supported: .woff2, .woff, .ttf, .otf. Any other extension throws a descriptive error so unsupported formats fail at build time, not at runtime.

For best loading performance, add a <link rel="preload"> for each self-hosted font in your layout <head>. This tells the browser to start fetching the woff2 file immediately rather than waiting until CSS parsing discovers the @font-face match:

<svelte:head>
  <link
    rel="preload"
    href="/fonts/source-sans-3.woff2"
    as="font"
    type="font/woff2"
    crossorigin
  />
</svelte:head>

You're responsible for placing the woff2 file in your project's static directory (CLI download automation is deferred to v0.1.1+).

SvelteKit integration

The library is framework-agnostic, but here's the pattern that works cleanly in a SvelteKit app. The same shape applies to React/Vue/etc. with their respective head-element conventions.

1. Place self-hosted font files in static/fonts/ (or your equivalent). SvelteKit serves everything in static/ from the site root, so static/fonts/source-sans-3.woff2 is reachable at /fonts/source-sans-3.woff2.

2. Generate fonts.css once via a build-time script (e.g., scripts/generate-fonts.ts):

import { createFontTheme } from "@sveltopia/fonts";
import { writeFileSync } from "node:fs";

const theme = createFontTheme({
  sans: {
    family: "Source Sans 3",
    weights: [200, 900],
    src: "/fonts/source-sans-3.woff2",
  },
  serif: {
    family: "Source Serif 4",
    weights: [200, 900],
    src: "/fonts/source-serif-4.woff2",
  },
});

writeFileSync("src/lib/styles/fonts.css", theme.css);

Run via npx tsx scripts/generate-fonts.ts and commit the emitted fonts.css. Generation is a one-time/manual step, not a build hook — re-run only when your font choices, weights, or src paths change.

3. Import the generated CSS at the top of your global stylesheet (src/app.css):

@import "./lib/styles/fonts.css";

:root {
  --fonts-sans:
    "Source Sans 3", "Source Sans 3 Fallback: Arial", Arial, system-ui,
    sans-serif;
  --fonts-serif:
    "Source Serif 4", "Source Serif 4 Fallback: Times New Roman",
    "Times New Roman", Georgia, serif;
}

The fallback alias values come from theme.sans.fontFamily / theme.serif.fontFamily — the script logs them to stdout so you can copy them into your CSS variables.

4. Add preload <link>s for the self-hosted woff2 files in your root +layout.svelte:

<svelte:head>
  <link
    rel="preload"
    href="/fonts/source-sans-3.woff2"
    as="font"
    type="font/woff2"
    crossorigin="anonymous"
  />
  <link
    rel="preload"
    href="/fonts/source-serif-4.woff2"
    as="font"
    type="font/woff2"
    crossorigin="anonymous"
  />
</svelte:head>

Plays well with @sveltopia/colors. Both libraries are framework-agnostic, both emit CSS variables, and they don't conflict. Run their generators independently; import the resulting CSS files at the top of your global stylesheet.

Tuned vertical metrics (opt-in)

Some fonts ship with asymmetric OS/2 metrics that cause text to render visually offset from the geometric center of its container — most noticeable at compact density on small components like badges and checkbox labels (the "1px shift" problem). For curated fonts where this matters, the library ships hand-tuned @font-face metric overrides that you can opt into per preset:

createFontPreset({
  family: "Source Sans 3",
  src: "/fonts/source-sans-3.woff2",
  tuneVerticalMetrics: true,
});

Defaults to false — opt in explicitly. When true, the primary @font-face block emits ascent-override, descent-override, and line-gap-override descriptors targeted at correcting the font's specific asymmetry. Glyphs render at the same size (no size-adjust involved); only the line-box geometry shifts.

Currently tuned fonts:

  • Source Sans 3 — descent reduced from native 40% to 36.4%. Empirically validated: visible improvement at compact density and prose contexts, no regression on flex-centered components (buttons, etc.) at any density. Measurement methodology: capture identical components in different page positions, overlay screenshots, verify text shifts toward geometric center without introducing drift on uppercase or large sizes.

If you pass tuneVerticalMetrics: true for a font without curated tuning (e.g., Inter, DM Sans, or any custom font), the library throws a build-time error with the list of currently-tuned fonts. Tuning is per-font work that requires empirical validation; the library doesn't speculatively tune.

Why opt-in? Tuned overrides are a deliberate departure from the font's declared metrics. Most consumers who pick Source Sans 3 want what Source Sans 3 says it is. The flag exists for consumers (like Sveltopia itself) who explicitly want the alignment improvement and have validated it works for their layouts.

Custom fonts

Pass pre-resolved metrics (the CLI will handle unpacking woff2 for non-curated fonts):

createFontPreset({
  family: {
    name: "Gheist",
    metrics: {
      familyName: "Gheist",
      ascent: 1050,
      descent: -350,
      lineGap: 0,
      unitsPerEm: 1000,
      category: "sans-serif",
      // ...full FontMetrics shape from @capsizecss/core
    },
  },
});

Curated fonts

15 popular Google Fonts (8 sans / 5 serif / 2 mono) with verified Capsize metrics:

| Family | Category | | ---------------- | ---------- | | Source Sans 3 | sans-serif | | Inter | sans-serif | | DM Sans | sans-serif | | Outfit | sans-serif | | Roboto | sans-serif | | Lato | sans-serif | | Nunito Sans | sans-serif | | Noto Sans | sans-serif | | Source Serif 4 | serif | | Lora | serif | | Merriweather | serif | | Playfair Display | serif | | Noto Serif | serif | | IBM Plex Mono | monospace | | JetBrains Mono | monospace |

Output shape

/* Primary @font-face — minimal, no overrides on the primary */
@font-face {
  font-family: "Source Sans 3";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src:
    local("Source Sans 3 Regular"), local("SourceSans3-Regular"),
    local("Source Sans 3");
}

/* Calibrated fallback @font-face — overrides match primary's layout dimensions */
@font-face {
  font-family: "Source Sans 3 Fallback: Arial";
  src: local("Arial"), local("ArialMT");
  ascent-override: 109.2105%;
  descent-override: 42.6604%;
  line-gap-override: 0%;
  size-adjust: 93.7639%;
}

The primary face has no metric overrides — those would be a no-op since they'd just re-declare the font's own OS/2 values back to the browser. The calibrated fallback face is where the real work happens. The fallback alias is always emitted in the explicit "<Primary> Fallback: <Specific>" form so developers reading the CSS can see which font is being used as the fallback.

Browser support

@font-face metric overrides (ascent-override, descent-override, line-gap-override, size-adjust) are universally supported in modern browsers:

  • Chrome / Edge: 87+ (November 2020)
  • Firefox: 89+ (June 2021)
  • Safari: 14+ (September 2020)

For users on older browsers the calibrated fallback @font-face rule still parses and uses the underlying system font (Arial / Times / Courier) — they just don't get the CLS-prevention benefit during font load. No worse than not using the library at all.

Troubleshooting

Cannot infer font format from src "..." — The src URL doesn't end in a recognized extension. Supported: .woff2, .woff, .ttf, .otf. If your URL has query strings or fragments after the extension, the library handles those — but make sure the actual filename ends correctly.

Unrecognized font extension ".eot" (or similar) — You're passing a format the library doesn't support. WOFF2 has universal modern support and is recommended. Convert legacy formats first.

Font not loading in production / CLS still visible on swap — Verify the <link rel="preload"> tag uses crossorigin="anonymous". Without it, the preload doesn't dedupe with the actual @font-face request and you may end up loading the font twice (or not at all). Also verify the file is reachable at the URL you're declaring (open it in a browser tab).

Fallback @font-face block doesn't activate — Check that the family name in your font-family chain matches the alias the library emitted (e.g., "Source Sans 3 Fallback: Arial", with the colon and the specific font name). The library always emits the explicit form regardless of fallback count; consumers sometimes truncate to "Source Sans 3 Fallback" based on older docs.

Google Fonts URL returns HTTP 400 — Likely cause: you're using theme.googleFontsHref for a multi-axis variable font (Source Serif 4, Lora, Merriweather, Playfair Display, Noto Serif). The current v0.1 implementation specifies only the wght axis, which Google rejects for multi-axis fonts. Multi-axis support is planned for v0.1.x; for now, self-host those fonts via the src config.

Source Sans 3 (or other font) still renders 1px above center on small components — That's a known property of browser sub-pixel rendering interacting with proportional metric asymmetry, not something CSS-level metric overrides can fix without unacceptable cost. Major UI libraries (shadcn, park-ui, Chakra) accept the same baseline. An opt-in primary-font tuning mechanism is being investigated for v0.1.x.

Acknowledgments

The fallback CLS calibration math is computed by Capsize (@capsizecss/core). Font metric data — including the curated and fallback metrics — comes from @capsizecss/metrics. Without Capsize this library would not exist; major credit to the SEEK team for their work on font metric tooling.

License

MIT