From df556f34d0149b06862692ac4a34ad5ec5c895e5 Mon Sep 17 00:00:00 2001 From: selezenart Date: Sun, 13 Sep 2026 16:17:27 +0200 Subject: [PATCH] docs: reduce the README banner to the brand lock-up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The full-width nav strip carried the network badge, token badge, theme control and workspace link into a README where none of them mean anything, and at README width the whole row rendered small. Keep only the brand lock-up — the commit-ring mark, the wordmark, and its SETTLEMENT ENGINE tag — on the same rounded panel, and scale every length by one factor so the proportions stay exactly those of `.top-nav`. The banner is still generated, not drawn: the palette comes from tokens.css, the mark geometry from CommitRing.tsx, and both labels from App.tsx. The canvas is 381x112 and sized to its own content, so the tag clears the panel edge with the fallback fonts GitHub renders. --- README.md | 9 ++- docs/assets/nav-panel-dark.svg | 21 ++---- docs/assets/nav-panel-light.svg | 21 ++---- scripts/render-nav-panel.mjs | 130 +++++++++++--------------------- 4 files changed, 61 insertions(+), 120 deletions(-) diff --git a/README.md b/README.md index cd0eed2..233ab4f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ - + OneShot settlement engine — Arc Testnet, USDC, open workspace diff --git a/docs/assets/nav-panel-dark.svg b/docs/assets/nav-panel-dark.svg index b02f412..70cd07e 100644 --- a/docs/assets/nav-panel-dark.svg +++ b/docs/assets/nav-panel-dark.svg @@ -1,7 +1,7 @@ - + OneShot — settlement engine - - + + @@ -11,17 +11,6 @@ - OneShot - SETTLEMENT ENGINE - - - - Arc Testnet - - USDC - - LIGHT THEME - - - Open workspace + OneShot + SETTLEMENT ENGINE diff --git a/docs/assets/nav-panel-light.svg b/docs/assets/nav-panel-light.svg index 51fcba5..9cace45 100644 --- a/docs/assets/nav-panel-light.svg +++ b/docs/assets/nav-panel-light.svg @@ -1,7 +1,7 @@ - + OneShot — settlement engine - - + + @@ -11,17 +11,6 @@ - OneShot - SETTLEMENT ENGINE - - - - Arc Testnet - - USDC - - LIGHT THEME - - - Open workspace + OneShot + SETTLEMENT ENGINE diff --git a/scripts/render-nav-panel.mjs b/scripts/render-nav-panel.mjs index 8a95905..fcbf077 100644 --- a/scripts/render-nav-panel.mjs +++ b/scripts/render-nav-panel.mjs @@ -1,15 +1,16 @@ /** * Renders the README banner from the product's own front-end source. * - * The banner is the `.top-nav` panel of `apps/web` — the commit-ring mark, the - * wordmark, the network and token badges, the theme control, and the workspace - * link. Nothing here is redrawn by hand: the palette is read from - * `packages/brand/src/tokens.css`, the ring geometry from - * `packages/brand/src/CommitRing.tsx`, and the nav labels from + * The banner is the brand lock-up of `apps/web`'s `.top-nav` — the commit-ring + * mark beside the wordmark and its `SETTLEMENT ENGINE` tag — on the same + * rounded panel, sized for a README rather than for a full-width nav. Nothing + * here is redrawn by hand: the palette comes from + * `packages/brand/src/tokens.css`, the mark geometry from + * `packages/brand/src/CommitRing.tsx`, and the labels from * `apps/web/src/App.tsx`, so the README cannot drift from the shipped UI - * without this script failing or producing a visibly different panel. + * without this script failing or producing a visibly different banner. * - * GitHub strips CSS from Markdown, so the panel ships as two static SVGs (one + * GitHub strips CSS from Markdown, so the banner ships as two static SVGs (one * per theme) selected by a `` element in the README. * * node scripts/render-nav-panel.mjs @@ -22,9 +23,15 @@ const root = new URL('../', import.meta.url); const read = (path) => readFile(new URL(path, root), 'utf8'); const out = (path) => fileURLToPath(new URL(path, root)); +/** + * The nav renders the mark at 36px. A banner that has to survive GitHub's + * column width needs it larger, so every length below is the CSS value scaled + * by this one factor — the proportions stay exactly those of `.top-nav`. + */ +const SCALE = 64 / 36; + /** Geometry mirrored from `apps/web/src/styles.css`, in px at a 16px root. */ -const NAV = { - width: 1000, +const CSS = { paddingX: 20, // .top-nav padding-inline 1.25rem paddingY: 13.6, // .top-nav padding-block 0.85rem radius: 22, // --os-radius-lg @@ -35,21 +42,10 @@ const NAV = { nameTracking: 1.6, // .brand-name letter-spacing 0.1em tagSize: 10.4, // .brand-tag 0.65rem tagTracking: 1.248, // .brand-tag letter-spacing 0.12em - statusGap: 9.6, // .nav-status-group gap 0.6rem - badgeSize: 12, // .status-badge 0.75rem - badgePadX: 12, // .status-badge padding-inline 0.75rem - badgeHeight: 24, // 0.3rem block padding around a 12px line - badgeContentGap: 7.2, // .status-badge gap 0.45rem - dot: 7, // .status-dot - toggleSize: 12.48, // .theme-toggle 0.78rem - toggleTracking: 1.248, // .theme-toggle letter-spacing 0.1em - togglePadX: 14.4, // .theme-toggle padding-inline 0.9rem - toggleHeight: 34, // .theme-toggle min-height - linkSize: 12.8, // .nav-console-link 0.8rem - linkPadX: 16, // .nav-console-link padding-inline 1rem - linkHeight: 30, // 0.45rem block padding around a 12.8px line }; +const B = Object.fromEntries(Object.entries(CSS).map(([key, value]) => [key, value * SCALE])); + /** * Pulls one selector's `--os-*` declarations out of the token sheet. Values * are kept verbatim, so `rgba()` lines survive alongside the hex ones. @@ -65,7 +61,7 @@ function readTheme(css, selector) { return tokens; } -/** Extracts a quoted string list or single literal from a source file. */ +/** Extracts the commit-ring arc geometry from the component source. */ function readArcs(source) { const attempts = [ ...(/const ATTEMPT_ARCS[\s\S]*?=\s*\[([\s\S]*?)\];/u.exec(source)?.[1] ?? '').matchAll( @@ -79,7 +75,7 @@ function readArcs(source) { return { attempts, committed }; } -/** Reads the nav labels from the landing page so the banner cannot drift. */ +/** Reads the wordmark and its tag from the landing nav. */ function readLabels(app) { const pick = (className) => new RegExp(`className="${className}">([^<]+)<`, 'u').exec(app)?.[1]?.trim(); @@ -88,22 +84,13 @@ function readLabels(app) { if (name === undefined || tag === undefined) { throw new Error('could not read brand labels from App.tsx'); } - const network = /className="status-badge network-badge">[\s\S]*?\/>\s*([A-Za-z ]+)\s*([^<]+)\s*([^<]+?)\s* text.replace(/&/gu, '&').replace(//gu, '>'); -function panel(theme, arcs, labels) { - const height = Math.round(NAV.paddingY * 2 + NAV.ring); - const middle = height / 2; - const sans = theme['font-primary']; - const mono = theme['font-mono']; - - // Brand group, left-aligned inside the panel padding. - const ringY = (height - NAV.ring) / 2; - const textX = NAV.paddingX + NAV.ring + NAV.brandGap; - const stack = NAV.nameSize * 1.2 + NAV.brandTextGap + NAV.tagSize * 1.2; +function banner(theme, arcs, labels) { + const height = Math.round(B.paddingY * 2 + B.ring); + const textX = B.paddingX + B.ring + B.brandGap; + const textWidth = Math.max( + advance(labels.name, B.nameSize, B.nameTracking), + advance(labels.tag, B.tagSize, B.tagTracking, true), + ); + const width = Math.round(textX + textWidth + B.paddingX); + + // `.brand-text` is a centred column of two lines. + const stack = B.nameSize * 1.2 + B.brandTextGap + B.tagSize * 1.2; const stackTop = (height - stack) / 2; - const nameBaseline = stackTop + NAV.nameSize * 0.95; - const tagBaseline = stackTop + NAV.nameSize * 1.2 + NAV.brandTextGap + NAV.tagSize * 0.95; - - // Status group, centred like the flex row that holds it. - const networkWidth = - NAV.badgePadX * 2 + NAV.dot + NAV.badgeContentGap + advance(labels.network, NAV.badgeSize); - const tokenWidth = NAV.badgePadX * 2 + advance(labels.token, NAV.badgeSize, 0, true); - const toggleLabel = 'Light theme'.toUpperCase(); - const toggleWidth = NAV.togglePadX * 2 + advance(toggleLabel, NAV.toggleSize, NAV.toggleTracking); - const statusWidth = networkWidth + tokenWidth + toggleWidth + NAV.statusGap * 2; - const statusX = (NAV.width - statusWidth) / 2; - const tokenX = statusX + networkWidth + NAV.statusGap; - const toggleX = tokenX + tokenWidth + NAV.statusGap; - - // Workspace link, right-aligned against the panel padding. - const linkWidth = NAV.linkPadX * 2 + advance(labels.link, NAV.linkSize); - const linkX = NAV.width - NAV.paddingX - linkWidth; - - const ringScale = NAV.ring / 64; + const nameBaseline = stackTop + B.nameSize * 0.95; + const tagBaseline = stackTop + B.nameSize * 1.2 + B.brandTextGap + B.tagSize * 0.95; + + const ringScale = B.ring / 64; const attempts = arcs.attempts .map( (d) => @@ -148,10 +121,10 @@ function panel(theme, arcs, labels) { ) .join('\n'); - return ` - OneShot — settlement engine - - + return ` + ${escape(labels.name)} — ${escape(labels.tag.toLowerCase())} + + ${attempts} @@ -159,19 +132,8 @@ ${attempts} - ${escape(labels.name)} - ${escape(labels.tag)} - - - - ${escape(labels.network)} - - ${escape(labels.token)} - - ${escape(toggleLabel)} - - - ${escape(labels.link)} + ${escape(labels.name)} + ${escape(labels.tag)} `; } @@ -182,6 +144,6 @@ const dark = { ...light, ...readTheme(tokensCss, ":root\\[data-theme='dark'\\]") const arcs = readArcs(await read('packages/brand/src/CommitRing.tsx')); const labels = readLabels(await read('apps/web/src/App.tsx')); -await writeFile(out('docs/assets/nav-panel-light.svg'), panel(light, arcs, labels), 'utf8'); -await writeFile(out('docs/assets/nav-panel-dark.svg'), panel(dark, arcs, labels), 'utf8'); +await writeFile(out('docs/assets/nav-panel-light.svg'), banner(light, arcs, labels), 'utf8'); +await writeFile(out('docs/assets/nav-panel-dark.svg'), banner(dark, arcs, labels), 'utf8'); console.log('wrote docs/assets/nav-panel-{light,dark}.svg');