stnd.buildSTANDARD MANUAL2026-07-15

@stnd/fonts

@stnd/fonts

This package contains all the typographies used in the Utopie design system (the Astro framework) as well as the Obsidian Vault.

All fonts follow a strict standard: the Artisan Standard. No more blind automation scripts breaking variable or static fonts; everything is structured with care and precision.


Every typeface Francis has bothered to self-host, one folder each — .woff2 for the web, a matching .md note (fonts as base64, so the note is self-contained) for the Obsidian vault. Using one in a site is a one-liner; adding a new one properly (the “Artisan Standard” below) is the careful part.

Use an existing font (in astro.config.mjs):

standard({ moduleLoad: ["@stnd/fonts/fraunces"] });

Add a new font: follow the 4-file recipe below — fonts/*.woff2<name>.css<name>.mdindex.module.js — then register it so themes can find it (see “Registering the Font in the Web Ecosystem”).


🛠 The “Artisan” Standard

Each font family has its own folder, named in kebab-case (e.g., atkinson-hyperlegible-mono).

Here is the exact structure that a valid font must respect:

packages/fonts/font-name/
├── fonts/
│   ├── font-name-regular.woff2
│   └── font-name-italic.woff2
├── font-name.css
├── font-name.md
└── index.module.js

1. The fonts/ folder (Binaries)

  • Contains ONLY useful files.
  • Preferred format: .woff2 (lightest and most modern). For special cases, .otf is accepted.
  • No duplicates (remove old .ttf.zip, or redundant .woff files if .woff2 exists).
  • Filenames must be clean, without weird numeric identifiers or generated symbols (e.g., no FontUI[MODE,wght].woff2).

2. The .css file (Astro Framework)

  • Named exactly like the directory (e.g., font-name.css).

  • Must start with a clear descriptive comment:

    /* 
     * Font Name
     * Description: Geometric sans-serif typeface...
     * Why: Ideal for modern titles and theme X.
     * Range: 100-900 (Variable) or Regular (400)
     */
    
  • The @font-face declarations must have a clean font-family (e.g., "Inter" and not "Inter Variable").

  • The font-weight values must be numeric. Never use normal or bold.

    • Static Font: Declare each weight individually (400500700900, etc.).
    • Variable Font: Specify the exact range, e.g., 100 900.

3. The .md file (Obsidian Vault)

  • Named exactly like the directory (e.g., font-name.md).
  • Contains the standard YAML Frontmatter of a font.
  • Includes the header description.
  • Contains a ```css block with the @font-face declarations where relative paths are replaced by Base64 Data URIs.
  • This is the file synced to vault/Atelier/Kernel/Fonts.

4. The index.module.js file (Plugin System)

  • Exposes the font for the framework’s theme loader.

  • The stylesheet path must point to the kebab-case CSS.

    export default {
      id: "font-font-name",
      name: "Stnd :: Font :: Font Name",
      description: "Registers Font Name font family.",
      meta: { type: "font", label: "Font Name" },
      styles: ["./font-name.css"],
    };
    

📝 Procedure: Adding a New Font

To add a font properly following the “Artisan” standard, use these steps:

Step 1: File Preparation

  1. Create a folder packages/fonts/font-name (in kebab-case).
  2. Create a fonts/ subfolder inside.
  3. Place your .woff2 files there. Clean their names (remove random numbers or special characters).

Step 2: The CSS File

Create the font-name.css file and write the @font-face declarations.

⚠️ WARNING: Variable vs Static
If the font is static, declare each file with its exact weight (e.g., font-weight: 700; for bold).

If the font is variable, declare the range (e.g., font-weight: 100 900;).

Step 3: The JS Module

Create index.module.js copying the template above and adapting IDs and labels.

Step 4: Generating the Obsidian Note (Base64)

Create a temporary script generate_md.cjs in the font’s folder to convert CSS to base64:

const fs = require('fs');
const path = require('path');

const cssContent = fs.readFileSync('packages/fonts/font-name/font-name.css', 'utf8');
const regex = /url\("\\.\\/fonts\\/(.*?)"\\)/g;
let newCss = cssContent;
let match;

while ((match = regex.exec(cssContent)) !== null) {
  const filename = match[1];
  const filePath = path.join('packages/fonts/font-name/fonts', filename);
  const base64 = fs.readFileSync(filePath).toString('base64');
  const dataUrl = `data:font/woff2;base64,${base64}`;
  newCss = newCss.replace(match[0], `url("${dataUrl}")`);
}

const mdHeader = `---
aliases: []
created: 2026-06-09 16:33
modified: 2026-06-09 16:33
cssclasses: []
publish: false
snippet: true
tags:
  - design
type: font
visibility: private
---

# Font Name

**Description**: Short description.
**Why use it**: Why we use it.

\`\`\`css
`;

fs.writeFileSync('packages/fonts/font-name/font-name.md', mdHeader + newCss + '\n\`\`\`\n');

Run it (node packages/fonts/font-name/generate_md.cjs) and then delete it.


🔄 Synchronizing with the Obsidian Vault

Once the .md note is successfully generated in packages/fonts/, it needs to be sent to Obsidian.

Quick command to copy a font:

cp packages/fonts/font-name/font-name.md vault/Atelier/Kernel/Fonts/

Command to sync everything (if you updated multiple fonts):

# From the Utopie project root
find packages/fonts -mindepth 2 -maxdepth 2 -name "*.md" -exec cp {} vault/Atelier/Kernel/Fonts/ \;

🔄 Registering the Font in the Web Ecosystem

To make the font package available to themes and the Astro application, you must add it to the dependencies list of the central fonts module in [packages/modules/fonts/index.module.js](file:///Users/francisfontaine/Documents/GitHub/utopie/packages/modules/fonts/index.module.js):

  dependencies: [
    ...
    "@stnd/fonts/font-name",
  ]

⚠️ Obsidian Theme Configuration (The static vs variable font trap)

When you use a font in an Obsidian theme (in vault/Atelier/Kernel/Themes/my-theme.md), make sure you configure it properly:

For a STATIC font (e.g., Forrest, Avant Garde Pro):

  • Use specific weights (e.g., 400 for normal, 700 for bold). Do not use 500 if you want a true bold.
  • Empty the variation attributes.
    --font-weight: 400;
    --bold-weight: 700;
    --font-variation: "";
    --font-header-variation: "";

For a VARIABLE font (e.g., Inter, MonoLisa):

  • You can use the wght variation property for very fine adjustments (e.g., 425).
    --font-variation: "wght" 400;
    --font-header-variation: "wght" 700;

Notes / Observations

(jot down anything noticed here — quirks, gotchas, ideas)

Todo

  • [ ] Nothing tracked yet.