synthesisui 0.4.7 → 0.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,8 @@
1
1
  import { mkdir, readFile, writeFile } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
3
  import { syncClaudeMd } from "../claude-md.js";
4
- import { resolveRegistry } from "../config.js";
5
- import { customFontFamilies, googleFontsHref } from "../fonts.js";
4
+ import { readProjectConfig, resolveRegistry } from "../config.js";
5
+ import { customFontFamilies, googleFontsHref, nextFontSnippet, } from "../fonts.js";
6
6
  import { buildGuide } from "../guide.js";
7
7
  import { body as line, section, snippet } from "../output.js";
8
8
  import { fetchDesignSystem } from "../registry.js";
@@ -131,10 +131,30 @@ export async function add(slug, opts) {
131
131
  console.log("");
132
132
  console.log(snippet([`<body data-ds="${payload.slug}">{children}</body>`]));
133
133
  // 3. Load the type - the DS ships token NAMES, not the fonts themselves.
134
- // Without this, display/body silently fall back and the identity is lost.
134
+ // Next apps get the next/font recipe (self-hosted + preloaded + adjusted
135
+ // fallback = no FOUT "blink" on refresh); the Google Fonts <link> stays
136
+ // as the framework-agnostic path.
135
137
  const families = payload.document.foundations.typography.families;
136
138
  const fontsHref = googleFontsHref(families);
137
- if (fontsHref) {
139
+ const projectConfig = await readProjectConfig(projectRoot);
140
+ const nextFonts = projectConfig.target === "next"
141
+ ? nextFontSnippet(families, payload.slug)
142
+ : null;
143
+ if (nextFonts) {
144
+ console.log("");
145
+ console.log(line("3. Load the type via next/font (recommended: self-hosted, preloaded, no font flash on refresh). Three small blocks:"));
146
+ console.log("");
147
+ console.log(snippet(nextFonts.fontsFile));
148
+ console.log("");
149
+ console.log(snippet(nextFonts.layout));
150
+ console.log("");
151
+ console.log(snippet(nextFonts.css));
152
+ if (fontsHref) {
153
+ console.log("");
154
+ console.log(line(` Quick alternative (works anywhere, may flash on cold loads): <link rel="stylesheet" href="${fontsHref}" /> in the <head>.`));
155
+ }
156
+ }
157
+ else if (fontsHref) {
138
158
  console.log("");
139
159
  console.log(line("3. Load the type - this system ships font NAMES, not the fonts. Add to your app's <head> (e.g. app/layout.tsx) so the families resolve (else they fall back and the look is lost):"));
140
160
  console.log("");
package/dist/fonts.js CHANGED
@@ -38,3 +38,52 @@ export function googleFontsHref(families) {
38
38
  .join("&");
39
39
  return `https://fonts.googleapis.com/css2?${query}&display=swap`;
40
40
  }
41
+ /**
42
+ * The RECOMMENDED wiring for Next apps: `next/font` self-hosts the families
43
+ * (preloaded, size-adjusted fallbacks - no FOUT "blink" on refresh), and a
44
+ * small CSS block re-points the DS's family tokens to next/font's variables.
45
+ * Returns null when the doc only uses generic families. The naive
46
+ * `<link href="fonts.googleapis.com...">` stays as the framework-agnostic
47
+ * fallback - it works everywhere but swaps visibly on cold loads.
48
+ */
49
+ export function nextFontSnippet(families, slug) {
50
+ const roles = ["display", "body", "mono"].filter((role) => {
51
+ const name = families[role]?.trim();
52
+ return name && !GENERIC_FAMILIES.has(name.toLowerCase());
53
+ });
54
+ if (roles.length === 0)
55
+ return null;
56
+ const importName = (name) => name.trim().replace(/ /g, "_");
57
+ const seen = new Map(); // family name -> const name
58
+ const importNames = [];
59
+ const consts = [];
60
+ for (const role of roles) {
61
+ const name = families[role].trim();
62
+ if (!seen.has(name)) {
63
+ seen.set(name, role);
64
+ importNames.push(importName(name));
65
+ consts.push(`export const ${seen.get(name)} = ${importName(name)}({`, ` subsets: ["latin"],`, ` variable: "--font-ds-${seen.get(name)}",`, `});`);
66
+ }
67
+ }
68
+ const fontsFile = [
69
+ `// app/fonts.ts`,
70
+ `import { ${importNames.join(", ")} } from "next/font/google";`,
71
+ ...consts,
72
+ ];
73
+ const roleVar = (role) => {
74
+ const name = families[role].trim();
75
+ return `--font-ds-${seen.get(name)}`;
76
+ };
77
+ const layout = [
78
+ `// app/layout.tsx`,
79
+ `import { ${[...new Set(roles.map((r) => seen.get(families[r].trim())))].join(", ")} } from "./fonts";`,
80
+ `<body data-ds="${slug}" className={\`${[...new Set(roles.map((r) => `\${${seen.get(families[r].trim())}.variable}`))].join(" ")}\`}>`,
81
+ ];
82
+ const css = [
83
+ `/* app/globals.css - AFTER the tokens.css import */`,
84
+ `[data-ds="${slug}"] {`,
85
+ ...roles.map((role) => ` --ds-typography-families-${role}: var(${roleVar(role)});`),
86
+ `}`,
87
+ ];
88
+ return { fontsFile, layout, css };
89
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.4.7",
3
+ "version": "0.4.8",
4
4
  "description": "Traz design systems do SynthesisUI para qualquer projeto (materializa em _local/ds/).",
5
5
  "type": "module",
6
6
  "bin": {