From 37bec6550026e44ce01d943d30d87e57c3b9cc94 Mon Sep 17 00:00:00 2001 From: Joe Blau Date: Wed, 30 Sep 2026 09:12:36 -0700 Subject: [PATCH] style(docs): dark Open Graph cards matching bloxwap/sfx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Social cards switch to the site's dark theme and the bloxwap/sfx card structure: Bloxwap black canvas, gray "bloxwap/" with a Nunito Black repo name, bold muted description, 200px mark, weighted brand bar - Home card shows a stats row with icons: built-in indicator and drawing-tool counts read from the library, plus dependency count and version from packages/chart/package.json - Page cards show "bloxwap/chart ·
" along the bottom - Drop the Nunito Regular TTF only the old card used; README updates Co-Authored-By: Claude Opus 5.5 --- apps/docs/README.md | 11 ++- apps/docs/fonts/Nunito-Regular.ttf | Bin 39008 -> 0 bytes apps/docs/fonts/README.md | 3 - apps/docs/lib/og-card.tsx | 119 ++++++++++++++++------------- 4 files changed, 72 insertions(+), 61 deletions(-) delete mode 100644 apps/docs/fonts/Nunito-Regular.ttf diff --git a/apps/docs/README.md b/apps/docs/README.md index 74e6e57..9d14172 100644 --- a/apps/docs/README.md +++ b/apps/docs/README.md @@ -47,9 +47,14 @@ monorepo checkout. ## Social cards The homepage and every documentation page have a 1200×630 PNG for Open Graph -and Twitter/X large-image previews. The design follows `monorepo/packages/og`: -black surfaces, a colored edge, the supplied Bloxwap wordmark, Nunito headlines, -and Space Grotesk labels. Fonts and artwork are local assets. +and Twitter/X large-image previews. They are laid out like GitHub's repository +cards, in the site's dark theme and matching the bloxwap/sfx cards: a Bloxwap +black (`#0a0a0a`) canvas, Nunito Bold and Black text in the `--foreground` and +`--muted-foreground` grays, and the green Bloxwap mark. The home card shows a +stats row (built-in indicator and drawing-tool counts from the library, plus the +dependency count and version from `packages/chart/package.json`); page cards +show the section instead. A weighted brand-color bar runs along the bottom. +Fonts are local assets. `lib/og-card.tsx` renders the cards with Next's `ImageResponse`. The static route in `app/og/[...slug]/route.tsx` generates `/og/home.png` and diff --git a/apps/docs/fonts/Nunito-Regular.ttf b/apps/docs/fonts/Nunito-Regular.ttf deleted file mode 100644 index 3bf5dddeb855d86f6145407aab2b1a79775b5120..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39008 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 Ox54sAjweAXHT?sq;6nKT diff --git a/apps/docs/fonts/README.md b/apps/docs/fonts/README.md index 41d6027..0e52a63 100644 --- a/apps/docs/fonts/README.md +++ b/apps/docs/fonts/README.md @@ -10,9 +10,6 @@ self-hosted, so documentation builds and visitors do not need Google Fonts reque `monorepo/packages/og/assets/` for Open Graph rendering (weights 700 and 900). They share the adjacent `Nunito-OFL.txt` license; the image renderer needs TTF rather than the site's WOFF2 font. -- **Nunito Regular**: a static weight-400 TTF instanced from `Nunito-Latin.woff2` - with fontTools (`instancer.instantiateVariableFont(font, {"wght": 400})`) for the - Open Graph descriptions. Same Latin subset and `Nunito-OFL.txt` license. - **Space Grotesk Bold**: copied with its OFL license from `monorepo/workers/www/public/fonts/`. - **Maple Mono**: variable subset, weights 100–800, copied with its OFL license from diff --git a/apps/docs/lib/og-card.tsx b/apps/docs/lib/og-card.tsx index c06449f..7c7457a 100644 --- a/apps/docs/lib/og-card.tsx +++ b/apps/docs/lib/og-card.tsx @@ -1,32 +1,46 @@ import { readFile } from 'node:fs/promises'; import { join } from 'node:path'; import { ImageResponse } from 'next/og'; +import { BUILTIN_DRAWINGS } from '@bloxwap/chart/drawings'; +import { BUILTIN_INDICATORS } from '@bloxwap/chart/indicators'; import { socialImageSize } from './social'; -// A GitHub-style card: white canvas, title and description top left, the -// Bloxwap mark where GitHub puts the owner avatar, package facts along the -// bottom, and the brand palette as the language bar. -const palette = ['#00ff3f', '#35b5ff', '#b300ff', '#ff479c', '#fffb38']; -const ink = '#1f2328'; -const muted = '#59636e'; +// Modeled on GitHub's repository cards, in the site's dark theme (the same card as bloxwap/sfx): the Bloxwap black +// canvas, owner/repo title, muted description, the logo tile top right, a stats row, and a color bar along the +// bottom (the Bloxwap brand palette). +const ink = '#fafafa'; // --foreground +const muted = '#a1a1a1'; // --muted-foreground +const bar: [color: string, share: number][] = [['#00ff3f', 62], ['#35b5ff', 14], ['#b300ff', 10], ['#ff479c', 8], ['#fffb38', 6]]; const assets = Promise.all([ - readFile(join(process.cwd(), 'fonts/Nunito-Regular.ttf')), readFile(join(process.cwd(), 'fonts/Nunito-Bold.ttf')), - readFile(join(process.cwd(), 'public/logos/bloxwap-icon-green.svg'), 'utf8'), + readFile(join(process.cwd(), 'fonts/Nunito-Black.ttf')), readFile(join(process.cwd(), '../../packages/chart/package.json'), 'utf8'), ]); +/** Lucide-style 24px stroke icons, inlined so the renderer needs no icon font or component. */ +const icons: Record = { + indicators: ['M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2'], + drawings: [ + 'M13 7 8.7 2.7a2.41 2.41 0 0 0-3.4 0L2.7 5.3a2.41 2.41 0 0 0 0 3.4L7 13', 'm8 6 2-2', 'm18 16 2-2', + 'm17 11 4.3 4.3c.94.94.94 2.46 0 3.4l-2.6 2.6c-.94.94-2.46.94-3.4 0L11 17', + 'M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z', 'm15 5 4 4', + ], + dependencies: ['M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z', 'm3.3 7 8.7 5 8.7-5', 'M12 22V12'], + version: ['M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z', 'M7.5 7.5h.01'], + docs: ['M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H19a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20'], +}; + +function Icon({ name }: { name: string }) { + return + {icons[name].map((d) => )} + ; +} + function clamp(text: string, max: number): string { const points = [...text.trim()]; return points.length > max ? `${points.slice(0, max - 1).join('')}…` : points.join(''); } -/** The mark as a plain SVG data URI; the renderer ignores the file's C2PA metadata and P3 styles. */ -function markUri(svg: string): string { - const plain = svg.replace(/[\s\S]*?<\/metadata>/, '').replace(/\sstyle="[^"]*"/g, ''); - return `data:image/svg+xml;base64,${Buffer.from(plain).toString('base64')}`; -} - /** Sentence-cases the route's category label, e.g. `API REFERENCE` → `API reference`. */ function categoryLabel(category: string): string { return category.startsWith('API') ? `API${category.slice(3).toLowerCase()}` : category.charAt(0) + category.slice(1).toLowerCase(); @@ -38,60 +52,55 @@ export async function renderSocialCard(options: { category: string; home?: boolean; }): Promise { - const [regular, bold, mark, manifest] = await assets; - const pkg = JSON.parse(manifest) as { version: string; license: string; dependencies?: Record }; - const facts: [string, string][] = [ - [`v${pkg.version}`, 'Latest'], - [pkg.license, 'License'], - [String(Object.keys(pkg.dependencies ?? {}).length), 'Dependencies'], + const [bold, black, manifest] = await assets; + const pkg = JSON.parse(manifest) as { version: string; dependencies?: Record }; + const stats: [icon: string, value: string, label: string][] = [ + ['indicators', String(BUILTIN_INDICATORS.length), 'Indicators'], + ['drawings', String(BUILTIN_DRAWINGS.length), 'Drawing tools'], + ['dependencies', String(Object.keys(pkg.dependencies ?? {}).length), 'Dependencies'], + ['version', `v${pkg.version}`, 'Latest'], ]; - const title = clamp(options.title, 80); + const title = clamp(options.title, 60); const description = clamp(options.description, 150); - const fontSize = title.length <= 24 ? 72 : title.length <= 48 ? 60 : 52; + const fontSize = options.home || title.length <= 20 ? 84 : title.length <= 34 ? 72 : 60; return new ImageResponse( -
-
-
-
- {options.home ? ( -
- bloxwap/ - chart -
- ) : ( -
-
- bloxwap/ - chart - · - {categoryLabel(options.category)} -
-
{title}
-
- )} -
{description}
+
+
+
+
+ {options.home + ? <>bloxwap/chart + : {title}}
- Bloxwap -
-
- {facts.map(([value, label]) => ( -
- {value} - {label} -
- ))} +
{description}
+ + + + + + +
+
+ {options.home + ? stats.map(([icon, value, label]) =>
+
{value}
+
{label}
+
) + :
+ bloxwap/chart·{categoryLabel(options.category)} +
}
-
- {palette.map((color) =>
)} +
+ {bar.map(([color, share]) =>
)}
, { ...socialImageSize, fonts: [ - { name: 'Nunito', data: regular, weight: 400, style: 'normal' }, { name: 'Nunito', data: bold, weight: 700, style: 'normal' }, + { name: 'Nunito', data: black, weight: 900, style: 'normal' }, ], }, );