Reference → Custom Fonts — Reference

Custom Fonts — Reference

The Custom Fonts surface is the self-hosted typeface registry — the place where an operator uploads a foundry-licensed font, a variable-axis family, or a non-Google web font that the styles-and-layouts font pickers should expose. It complements the platform's built-in Google Fonts discovery: Google fonts come in for free via the discovery list, and anything outside that list comes in through here.

This page is a reference for platform engineers and integrators who need to understand the upload pipeline, the file-format normalization, and how custom fonts surface into the SG-Builder editor and the global styles. Customer-facing walkthroughs for uploading a font and applying it live in the customer docs set.


Overview

Custom Fonts lives under the Custom module group in SG-Admin, alongside its design-system siblings Custom CSS and Custom Codes, and the operator-data siblings Custom Fields and Custom Objects.

The module is paired by convention: one half renders the list, create, edit, manage-font views; the other half handles writes — upload, table refresh, soft delete, restore. The list view shows each font family with its uploaded weights and styles, a preview rendered in the font itself, and the destination registry — whether it appears in the styles-and-layouts font picker, the SG-Builder typography picker, or both.

A font record carries a family name, a display name, the set of uploaded files (one per weight × style), a fallback stack, a load strategy (swap, block, fallback, optional), an enabled flag, and a unicode-range hint. On render, the platform emits an @font-face rule per file into the active stylesheet and offers the family in the relevant typography pickers.

Where it lives in SG-Admin:

  • Sidebar: SG-Admin → Custom → Fonts
  • URL prefix: /sg-admin/custom-fonts/
  • View templates: application/views/Admin/CustomFonts/
Naming requirement — must be unique. The platform indexes fonts by the family name. If an operator uploads a custom font with the same family name as a Google font already discovered or another custom font already uploaded, the typography pickers cannot disambiguate. The convention is to give each custom upload a uniquely-suffixed family name — for example, Inter Site instead of plain Inter — so the picker shows both clearly. The surface returns a structured rejection on duplicate family names.

Self-hosted vs Google route. For Google's catalog, the recommended path is the Google Fonts URL field in the styles-and-layouts editor — fonts come in via the Google CDN with no upload required. Use Custom Fonts when the foundry is not Google, when the typeface needs to be self-hosted for compliance or performance reasons, or when a variable-axis font is needed (Google's basic URL field does not expose the full axis range).

┌──────────────────────────────────────────────────────────────────────┐│ SG-Admin → Custom → Fonts [+ Upload] │├──────────────────────────────────────────────────────────────────────┤│ Family Weights / Styles Format Status ││ ────────────────── ──────────────────────── ────── ─────── ││ Söhne (licensed) 400, 500, 600, 700 · i WOFF2 active ││ Inter Variable 100-900 axis WOFF2 active ││ Brand Serif Pro 300, 400, 700 WOFF2, active ││ WOFF ││ Old Display (paused) 400 WOFF2 paused ││ ││ ┌──────────────────────────────────────┐ ││ │ Preview: │ ││ │ Söhne — The quick brown fox jumps │ ││ │ Inter Variable — The quick brown fox │ ││ │ Brand Serif Pro — The quick brown │ ││ └──────────────────────────────────────┘ │└──────────────────────────────────────────────────────────────────────┘

Actions

The Custom Fonts surface exposes the following operations.

List and search

Returns the font records visible to the operator, paginated, with family, uploaded weight/style coverage, format, status, and last-modified columns. Includes a live preview block that renders the family in itself for visual confirmation. Supports sort, free-text filter on family name, and filter by status or format.

Upload font (create)

Opens an upload form. Required fields: family name (must be unique), display name, at least one font file. Optional: fallback stack, load strategy, unicode-range hint, weight and style metadata per file (auto-detected when the file's internal name table is well-formed, manually entered otherwise).

Files accepted: WOFF2 (preferred), WOFF, TTF, OTF. On upload, the platform normalizes each file — converts TTF and OTF to WOFF2, generates a WOFF fallback, computes file size, extracts the internal name table for metadata, and writes the files to the platform CDN with cache-control headers tuned for fonts.

Edit font

Loads an existing record into the same form shape used for upload, pre-populated. Operators can add new weight/style files to an existing family, remove individual files, update the fallback stack, or change the load strategy. Submit replaces the stored record; removed files are scheduled for CDN purge.

Manage font (per-family detail)

A detail view per family. Shows every uploaded file with weight, style, format, file size, the resolved @font-face source URL, and a per-file enabled flag. Useful for partial activation — for example, uploading 12 weights but exposing only 4 in the typography pickers.

Toggle enabled

Pauses or resumes a font without deleting it. A paused family is omitted from the typography pickers and the @font-face emission.

Soft delete

Marks the family as trashed. Trashed families disappear from the default list and stop rendering immediately. Pages that referenced the font fall through to the next item in their fallback stack until the reference is updated.

Restore

Returns a trashed family to its prior enabled state. CDN files are reactivated; no re-upload is required.

Permanent delete

Hard-removes a trashed family and purges its CDN files. Irreversible.


Data model

A font family record carries the following fields, with one nested file record per uploaded weight × style.

FieldTypeNotes
idintegerPrimary key. Stable across edits.
family_namestringUnique. Picker index key.
display_namestringList-view label. Not unique.
files[]arrayOne per uploaded weight × style. See below.
fallback_stackstringCSS-style stack — "system-ui, sans-serif" etc.
load_strategyenumswap, block, fallback, optional. Renders as font-display in @font-face.
unicode_rangestringOptional. Renders as unicode-range in @font-face.
surface_destinationenumpicker-only, styles-and-layouts, sg-builder, both.
enabledbooleantrue emits @font-face and exposes in pickers.
statusenumactive or trash.
created_attimestampImmutable.
updated_attimestampTouched on edit.
created_byintegerForeign key to Users.
File record (nested under files[]):
FieldTypeNotes
idintegerPrimary key on the file row.
weightinteger100-900. Or 1-1000 for variable-axis.
styleenumnormal or italic.
formatenumwoff2, woff, ttf, otf.
urlstringCDN-served URL. Stable across edits.
byte_sizeintegerPost-normalization size.
file_enabledbooleanPer-file toggle, independent of family-level.
Format normalization. Uploaded TTF and OTF files are converted to WOFF2 on the platform side, and a WOFF fallback is generated for older browser coverage. Operators uploading WOFF2 directly skip the conversion step. The original uploaded file is retained on the CDN under the same family for licensing audit, but the rendered @font-face uses the normalized WOFF2 + WOFF pair.

Variable axis fonts. A variable font occupies a single file with a weight range. The weight field on the file record carries the range (100-900) rather than a single value. Typography pickers expose the full range to operators selecting weight.

FONT FAMILY RECORD FILES (nested array)├── id ┌─── per weight × style ───┐├── family_name (unique) │ │├── display_name │ ├── weight 400 │├── fallback_stack │ ├── style normal │├── load_strategy │ ├── format woff2 │├── unicode_range │ ├── url <CDN> │├── surface_destination │ ├── byte_size │├── enabled │ └── file_enabled │├── status │ │└── timestamps │ ── repeated for each ── ││ weight / style / │on render: │ format combination │emit @font-face per └──────────────────────────┘enabled fileexpose family inconfigured pickers

Permissions

Access to the Custom Fonts surface is gated at two layers.

Layer 1 — admin gate. Standard admin access check at request entry.

Layer 2 — per-action capability. The default capability map ships as:

CapabilityAdministratorEditorDesignerViewer
List fonts
Upload font
Edit font
Manage font (per-file)
Toggle enabled
Soft delete
Restore
Permanent delete
The Designer role holds the upload and management capabilities by default — type-system work is the most common Designer-tier task. The Editor role defaults to read; some custom configurations lift Editor to write where contributors need to add a temporary campaign font without escalating.

Self-protection rules. The surface returns a structured rejection on duplicate family names, on file uploads that fail the format-normalization step (corrupt or unsupported source), and on a permanent delete attempt while any active CSS rule still references the family (use a search-and-replace pass first).

License audit. The original uploaded file is retained on the CDN under the family for the lifetime of the record. Operators uploading licensed fonts should attach the license document or invoice to the family's notes field — there is no automated license enforcement, only the audit trail.

Audit. Every upload, edit, manage-font change, toggle, soft delete, restore, and permanent delete emits an Activity Log entry. The diff includes the file set before and after — useful for tracking when a weight was added or removed.

UPLOAD → /sg-admin/custom-fonts/...│▼┌──────────────────────┐│ Admin gate │└──────────┬───────────┘│▼┌──────────────────────┐│ Capability check ││ Designer / Admin │└──────────┬───────────┘│▼┌──────────────────────┐│ Family-name unique? │ duplicate → reject└──────────┬───────────┘│▼┌──────────────────────┐│ Format normalize │ corrupt/unsupported → reject│ TTF/OTF → WOFF2 ││ generate WOFF │└──────────┬───────────┘│▼┌──────────────────────┐│ CDN upload ││ cache-control set │└──────────┬───────────┘│▼record persists│▼Activity Log entry│▼emit @font-face on next renderexpose in configured pickers

Related references

  • Themes — Reference. Styles-and-layouts is one of the destination pickers. Custom Fonts surface into the heading, body, and accent role pickers there.
  • Custom CSS — Reference. Operator rules can reference uploaded families by their family_name directly. The platform-emitted @font-face block is global; no extra import is needed.
  • Custom Codes — Reference. Not the right surface for font hosting. Use this module — the load-strategy and CDN caching are tuned for fonts in a way that ad-hoc Custom Code uploads are not.
  • Pages — Reference. Pages that override the global font stack pick from the same registry exposed by this module.
  • SG-Builder — Reference. The Builder editor's typography picker surfaces fonts registered with surface_destination set to sg-builder or both.
  • Settings — Reference. CDN configuration, license-audit retention, font upload byte cap.
For the corresponding customer-facing walkthrough — uploading a foundry font, mixing self-hosted and Google fonts, applying a font to a Builder component — see the Custom Fonts section of the customer docs at /docs/custom-fonts.
On this page