mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-10-30 00:27:11 +09:00
Phase 0 of the ServerKit UI redesign (see docs/REDESIGN_MAP.md): - Remap design tokens to the new infra-console palette behind existing names; add prototype token names (surface ramp, cyan/violet, accent-bright/-bg). Dark/light/system. - Self-host IBM Plex (woff2 vendored via scripts/fetch-fonts.mjs); no Google Fonts CDN. - Runtime-derive the full accent ramp in ThemeContext. - Add shared design-system primitives in components/ds (Pill, EnvTag, SegControl, MetricCard, Gauge, ServiceTile, Sparkline, AreaChart, ScoreGauge, Feed, Drawer). - Restyle the sidebar (gradient rail, mono section headers, left accent bar) and switch the brand to the demo's gradient-tile logo. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
64 lines
2.8 KiB
JavaScript
64 lines
2.8 KiB
JavaScript
#!/usr/bin/env node
|
|
// ============================================================
|
|
// fetch-fonts.mjs — vendor IBM Plex woff2 into frontend/public/fonts/
|
|
// ============================================================
|
|
// ServerKit self-hosts its fonts for privacy (no Google Fonts CDN — that would
|
|
// leak every visitor's IP + usage to a third party). This script downloads the
|
|
// OFL-licensed IBM Plex faces once, from the @fontsource jsDelivr mirror, into
|
|
// the repo's public/fonts/ folder so they're served from our own origin.
|
|
//
|
|
// node frontend/scripts/fetch-fonts.mjs
|
|
//
|
|
// Re-run only when changing weights. Commit the resulting .woff2 files.
|
|
// Requires Node 18+ (global fetch). No network at build/runtime — only here.
|
|
// ============================================================
|
|
|
|
import { mkdir, writeFile, access } from 'node:fs/promises';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { dirname, join } from 'node:path';
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const OUT = join(__dirname, '..', 'public', 'fonts');
|
|
const BASE = 'https://cdn.jsdelivr.net/npm';
|
|
|
|
// local filename -> @fontsource source path
|
|
const FONTS = {
|
|
'ibm-plex-sans-400.woff2': '@fontsource/ibm-plex-sans/files/ibm-plex-sans-latin-400-normal.woff2',
|
|
'ibm-plex-sans-500.woff2': '@fontsource/ibm-plex-sans/files/ibm-plex-sans-latin-500-normal.woff2',
|
|
'ibm-plex-sans-600.woff2': '@fontsource/ibm-plex-sans/files/ibm-plex-sans-latin-600-normal.woff2',
|
|
'ibm-plex-sans-700.woff2': '@fontsource/ibm-plex-sans/files/ibm-plex-sans-latin-700-normal.woff2',
|
|
'ibm-plex-mono-400.woff2': '@fontsource/ibm-plex-mono/files/ibm-plex-mono-latin-400-normal.woff2',
|
|
'ibm-plex-mono-500.woff2': '@fontsource/ibm-plex-mono/files/ibm-plex-mono-latin-500-normal.woff2',
|
|
'ibm-plex-mono-600.woff2': '@fontsource/ibm-plex-mono/files/ibm-plex-mono-latin-600-normal.woff2',
|
|
};
|
|
|
|
async function main() {
|
|
await mkdir(OUT, { recursive: true });
|
|
let ok = 0;
|
|
for (const [name, path] of Object.entries(FONTS)) {
|
|
const dest = join(OUT, name);
|
|
try {
|
|
await access(dest);
|
|
console.log(`✓ ${name} (already present)`);
|
|
ok++;
|
|
continue;
|
|
} catch { /* not present — download */ }
|
|
|
|
const url = `${BASE}/${path}`;
|
|
try {
|
|
const res = await fetch(url);
|
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
const buf = Buffer.from(await res.arrayBuffer());
|
|
await writeFile(dest, buf);
|
|
console.log(`↓ ${name} (${(buf.length / 1024).toFixed(1)} KB)`);
|
|
ok++;
|
|
} catch (err) {
|
|
console.error(`✗ ${name} — ${err.message}\n ${url}`);
|
|
}
|
|
}
|
|
console.log(`\n${ok}/${Object.keys(FONTS).length} fonts in ${OUT}`);
|
|
if (ok < Object.keys(FONTS).length) process.exitCode = 1;
|
|
}
|
|
|
|
main();
|