Add the Northline template family: Purple Pop and Citrus Split

Purple Pop (cream page, purple pills, asterisk and dome decor, opt-in
logo) and Citrus Split (grey split panel, halftone chevrons, icon
contacts, logo in the measured slot) are rebuilt from the Canva PDFs on
the engine with A4/Letter support, GST slots and zero audit errors. Adds
static DM Sans, Jost and Poppins 800 as look-alikes for the commercial
fonts. The picker now lists ten templates.
This commit is contained in:
2026-10-04 09:56:56 +05:30
parent 06976df55c
commit 5ea62a9be8
36 changed files with 3033 additions and 56 deletions
+21 -3
View File
@@ -6,7 +6,19 @@
* TYPE_ROLES holds Classic's values. Every other template declares its own role table (see
* defineRoles) from the faces below and passes those tokens wherever a role is accepted.
*/
export type FaceKey = "IBM Plex Sans" | "IBM Plex Mono" | "Montserrat" | "Open Sans" | "Poppins" | "Inter";
export type FaceKey =
| "IBM Plex Sans"
| "IBM Plex Mono"
| "Montserrat"
| "Open Sans"
| "Poppins"
| "Inter"
| "DM Sans"
| "Jost"
// Licensed faces of the Canva designs; SUBSTITUTES maps each to the bundled look-alike.
| "Now"
| "Gotham"
| "Open Sauce One";
export interface TypeToken {
face: FaceKey;
@@ -55,9 +67,15 @@ export function defineRoles<const T extends Record<string, TypeToken>>(table: T)
/**
* Licensed template faces (Now, Gotham, Open Sauce One, ...) map to the OFL look-alike that is
* actually bundled. Empty until those families are registered.
* actually bundled. A user-imported font replaces the licensed name through `faceOverrides`.
* Gotham runs about 4% narrower than Montserrat, so tokens that use it carry sizes already scaled by
* GOTHAM_SIZE (see templates/northline); a real Gotham import would need them back at 1.
*/
export const SUBSTITUTES: Readonly<Record<string, string>> = {};
export const SUBSTITUTES: Readonly<Record<string, string>> = {
Now: "Jost",
Gotham: "Montserrat",
"Open Sauce One": "Poppins",
};
/** The family name to register/look up for a face: user override first, then substitute, then itself. */
export function resolveFace(face: string, overrides?: Readonly<Record<string, string>>): string {