Skip to content

feat(brand): write the Sketchi wordmark in Excalifont#295

Merged
graphite-app[bot] merged 1 commit into
mainfrom
feat/excalifont-wordmark
Jul 25, 2026
Merged

feat(brand): write the Sketchi wordmark in Excalifont#295
graphite-app[bot] merged 1 commit into
mainfrom
feat/excalifont-wordmark

Conversation

@anandpant

@anandpant anandpant commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

Why

Dancing Script is the default Google handwriting face — it reads as picked from a dropdown. Excalifont is the exact typeface every Sketchi diagram renders in, so the wordmark stops being decoration and becomes an instance of the product: the name written in the same hand the product draws.

On the playground the wordmark now sits inches from GitHub push / Docker build / Run tests in the same face. That adjacency is the whole point of the change.

Licence finding

Excalifont is SIL Open Font License 1.1, and OFL explicitly permits this use. Verified rather than assumed:

  • The vendored files are byte-identical to excalidraw/excalidraw@master. The woff2 name table carries the copyright but no licence fields (nameID 13/14 absent), so the licence comes from upstream's fonts/Excalifont/index.ts metadata block ("This Font Software is licensed under the SIL Open Font License, Version 1.1") and from plus.excalidraw.com/excalifont ("Released under the OFL-1.1 license ... freely available for both personal and commercial use").
  • Wordmark use is squarely allowed. OFL clause 5 exempts "any document created using the Font Software", and the OFL FAQ 1.1/1.1.1 answers the logo question directly: "Can I use the fonts ... to create logos or other graphics ... Yes." and "You remain the author and copyright holder of that newly derived graphic." So the Sketchi wordmark stays ours; no copyleft reaches it.
  • No Reserved Font Name is declared, so even naming restrictions on derivatives don't bite. We make no derivative regardless.
  • Two obligations do apply, and both are now met:
    1. Clause 2 — the copyright notice and licence must travel with every redistributed copy. Serving woff2 from our origins is redistribution, so a LICENSE now sits beside the files in every public/fonts/Excalifont. Previously the playground served them with no notice at all.
    2. Trademark — upstream declares "Excalifont is a trademark of Excalidraw." We use the letterforms, never the name, as Sketchi branding. The CLI's THIRD_PARTY_NOTICES was missing this line and now carries it.

No blocker. Ship it.

Legibility pass

Checked in a real browser at every size the wordmark actually appears, on both paper and reversed out of dark ink.

One adjustment was needed, and it was the important one: every placement drops from font-weight: 700 to 400. Excalifont ships Regular only, so 600/700 was Chrome synthesising the weight — strokes thicken without the letters widening and the counters in e/c close up, exactly the "turns to mush" failure mode for a hand-drawn face. Regular is the real hand.

No size changes were needed. Measured against the outgoing face at 28px, Excalifont is ~12% wider with a taller cap (20 vs 18) but a slightly smaller x-height (12 vs 13) — so shrinking it to preserve the old footprint would have made it less legible, not more. At the three real sizes (28px header/playground, 26px dark footer, 25px icons narrow) the counters stay open and reversed strokes don't bloom.

The 404s on all three surfaces turned out to carry no wordmark at all — they're display-font headings only, so there was nothing to adjust there.

What changed

  • --font-script in theme.css drives everything, so the swap is one token plus the @font-face set beside it. All seven cn-font-split subsets are declared with their upstream unicode-range — they're subsets of one face, not seven faces, so declaring only the Latin one leaves every other codepoint silently falling back.
  • Fonts vendored per surface (each Worker serves its own public/), with diagram-ui holding the reference copy and serving it to Storybook via staticDirs. tools/vendored-excalifont.test.ts pins all six copies byte-identical and derives the surface list from the theme imports, so a new theme consumer fails the test instead of shipping a 404 for its own brand face.
  • Surfaces that draw the wordmark preload the Latin subset, so the lockup paints in Excalifont rather than reflowing through the sans.
  • The og:card — the most-seen wordmark instance of all — is a static PNG a token can't reach. Its wordmark and yes/no tags are now Excalifont outlines rather than <text>, so it doesn't depend on a webfont reaching whichever renderer rasterises it (and outlines are the cleaner licence position: a document, not the font).
  • Dancing Script is now unreferenced and leaves every Google Fonts URL.

Kept off site-header.tsx and all copy for the parallel declutter agent.

Proof

  • pnpm nx run-many -t typecheck,test,build — green (52 tasks)
  • pnpm nx build-storybook diagram-ui — green
  • pnpm test:tools — green (82 tests; run-many does not cover this)
  • pnpm lint — green
  • Verified in Chrome on web, icons, playground, excalidraw and Storybook: font 200s, 0 errored faces, wordmark computed at 400, plus the three 404s and the icons narrow breakpoint. Build outputs confirmed to ship the subsets on all five surfaces.
  • codex review --base main — clean on the third pass. It found two real defects I'd missed: 404ing font URLs on the two theme consumers that don't draw a wordmark, and the stale Dancing Script social card. Both fixed here.

Rebase onto #294

Rebased onto ca6b91d (Product tab dropped, npm mark added, copy cut) and the legibility pass was re-run against the header that will actually ship, not the stale one: wordmark computed 28px/400, 0 errored faces, no overflow at 390px, dark footer and 404 re-checked, and the fresh preview confirms the new three-item nav. The looser nav actually gives the lockup more room than before.

On the three visual languages now in one bar: the wordmark is not the friction — it's ink-toned text that belongs to the same layer as the nav, and the tile+wordmark lockup is separated from the utility cluster by the whole nav. The friction is the npm mark, for three reasons: it is the only filled container in the bar besides the Playground CTA (so it reads as a competing button immediately left of the real one), it is the highest-contrast, only-square-cornered element in a system that is otherwise rounded ink-on-paper, and it is optically much heavier than the Octocat sitting next to it, so two same-tier utility links read at different volumes. Recommendation is to render npm as a bare monochrome wordmark in --ink-2, matched to the Octocat's cap height, with no filled block — that makes GitHub+npm a coherent quiet pair and gives the CTA back its status as the only filled element. Not changed here: those marks landed in #294 and are not this PR's scope.

Copy link
Copy Markdown
Collaborator Author

How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • merge - adds this PR to the back of the merge queue
  • fast - for urgent changes, fast-track this PR to the front of the merge queue

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@anandpant anandpant changed the title feat(brand): write the wordmark in Excalifont, the hand the product draws feat(brand): write the Sketchi wordmark in Excalifont Jul 25, 2026
@anandpant
anandpant marked this pull request as ready for review July 25, 2026 17:30
@github-actions

github-actions Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Sketchi Web Preview

Status: deleted

Preview Worker cleanup has completed.

@github-actions

github-actions Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Sketchi Icons Preview

Status: deleted

Preview Worker cleanup has completed.

@github-actions

github-actions Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Sketchi Excalidraw Workspace Preview

Status: deleted

  • Surface: internal preview; not linked from public navigation
  • Project: excalidraw
  • Worker identity: sketchi-excalidraw
  • Route policy: internal canvas workspace; no public product domain
  • Preview Worker: sketchi-excalidraw-pr-295
  • Commit: 80dc9f45f2e0
  • Workflow run: https://github.com/shpitdev/sketchi/actions/runs/30168666040

Preview Worker cleanup has completed.

@github-actions

github-actions Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Sketchi Eval Harness Preview

Status: deleted

  • Surface: internal preview; not linked from public navigation
  • Project: eval-harness
  • Worker identity: sketchi-playground
  • Route policy: internal eval harness; no public product domain
  • Preview Worker: sketchi-playground-pr-295
  • Commit: 80dc9f45f2e0
  • Workflow run: https://github.com/shpitdev/sketchi/actions/runs/30168666040

Preview Worker cleanup has completed.

@github-actions

github-actions Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Sketchi Playground / Studio Preview

Status: deleted

  • Surface: public product preview
  • Project: playground
  • Worker identity: sketchi-studio
  • Route policy: playground.sketchi.app product surface; authenticated Studio remains unexposed
  • Preview Worker: sketchi-studio-pr-295
  • Commit: 80dc9f45f2e0
  • Workflow run: https://github.com/shpitdev/sketchi/actions/runs/30168666040

Preview Worker cleanup has completed.

@graphite-app

graphite-app Bot commented Jul 25, 2026

Copy link
Copy Markdown

Merge activity

## Why

Dancing Script is the default Google handwriting face — it reads as picked from a dropdown. Excalifont is the exact typeface every Sketchi diagram renders in, so the wordmark stops being decoration and becomes an instance of the product: the name written in the same hand the product draws.

On the playground the wordmark now sits inches from `GitHub push` / `Docker build` / `Run tests` in the same face. That adjacency is the whole point of the change.

## Licence finding

**Excalifont is SIL Open Font License 1.1, and OFL explicitly permits this use.** Verified rather than assumed:

- The vendored files are byte-identical to `excalidraw/excalidraw@master`. The `woff2` name table carries the copyright but **no** licence fields (nameID 13/14 absent), so the licence comes from upstream's `fonts/Excalifont/index.ts` metadata block (`"This Font Software is licensed under the SIL Open Font License, Version 1.1"`) and from `plus.excalidraw.com/excalifont` ("Released under the OFL-1.1 license ... freely available for both personal and commercial use").
- Wordmark use is squarely allowed. OFL clause 5 exempts "any document created using the Font Software", and the OFL FAQ 1.1/1.1.1 answers the logo question directly: *"Can I use the fonts ... to create logos or other graphics ... Yes."* and *"You remain the author and copyright holder of that newly derived graphic."* So the Sketchi wordmark stays ours; no copyleft reaches it.
- **No Reserved Font Name is declared**, so even naming restrictions on derivatives don't bite. We make no derivative regardless.
- Two obligations do apply, and both are now met:
  1. Clause 2 — the copyright notice and licence must travel with every redistributed copy. Serving `woff2` from our origins is redistribution, so a `LICENSE` now sits beside the files in every `public/fonts/Excalifont`. Previously the playground served them with no notice at all.
  2. Trademark — upstream declares *"Excalifont is a trademark of Excalidraw."* We use the letterforms, never the name, as Sketchi branding. The CLI's `THIRD_PARTY_NOTICES` was missing this line and now carries it.

No blocker. Ship it.

## Legibility pass

Checked in a real browser at every size the wordmark actually appears, on both paper and reversed out of dark ink.

**One adjustment was needed, and it was the important one: every placement drops from `font-weight: 700` to `400`.** Excalifont ships Regular only, so 600/700 was Chrome synthesising the weight — strokes thicken without the letters widening and the counters in `e`/`c` close up, exactly the "turns to mush" failure mode for a hand-drawn face. Regular is the real hand.

**No size changes were needed.** Measured against the outgoing face at 28px, Excalifont is ~12% wider with a taller cap (20 vs 18) but a slightly *smaller* x-height (12 vs 13) — so shrinking it to preserve the old footprint would have made it less legible, not more. At the three real sizes (28px header/playground, 26px dark footer, 25px icons narrow) the counters stay open and reversed strokes don't bloom.

The 404s on all three surfaces turned out to carry no wordmark at all — they're display-font headings only, so there was nothing to adjust there.

## What changed

- `--font-script` in `theme.css` drives everything, so the swap is one token plus the `@font-face` set beside it. All seven cn-font-split subsets are declared with their upstream `unicode-range` — they're subsets of one face, not seven faces, so declaring only the Latin one leaves every other codepoint silently falling back.
- Fonts vendored per surface (each Worker serves its own `public/`), with `diagram-ui` holding the reference copy and serving it to Storybook via `staticDirs`. `tools/vendored-excalifont.test.ts` pins all six copies byte-identical and derives the surface list from the theme imports, so a new theme consumer fails the test instead of shipping a 404 for its own brand face.
- Surfaces that draw the wordmark preload the Latin subset, so the lockup paints in Excalifont rather than reflowing through the sans.
- The og:card — the most-seen wordmark instance of all — is a static PNG a token can't reach. Its wordmark and `yes`/`no` tags are now Excalifont outlines rather than `<text>`, so it doesn't depend on a webfont reaching whichever renderer rasterises it (and outlines are the cleaner licence position: a document, not the font).
- Dancing Script is now unreferenced and leaves every Google Fonts URL.

Kept off `site-header.tsx` and all copy for the parallel declutter agent.

## Proof

- `pnpm nx run-many -t typecheck,test,build` — green (52 tasks)
- `pnpm nx build-storybook diagram-ui` — green
- `pnpm test:tools` — green (82 tests; `run-many` does not cover this)
- `pnpm lint` — green
- Verified in Chrome on web, icons, playground, excalidraw and Storybook: font 200s, `0` errored faces, wordmark computed at `400`, plus the three 404s and the icons narrow breakpoint. Build outputs confirmed to ship the subsets on all five surfaces.
- `codex review --base main` — clean on the third pass. It found two real defects I'd missed: 404ing font URLs on the two theme consumers that don't draw a wordmark, and the stale Dancing Script social card. Both fixed here.

## Rebase onto #294

Rebased onto `ca6b91d` (Product tab dropped, npm mark added, copy cut) and the legibility pass was re-run against the header that will actually ship, not the stale one: wordmark computed `28px`/`400`, `0` errored faces, no overflow at 390px, dark footer and 404 re-checked, and the fresh preview confirms the new three-item nav. The looser nav actually gives the lockup more room than before.

On the three visual languages now in one bar: the wordmark is not the friction — it's ink-toned text that belongs to the same layer as the nav, and the tile+wordmark lockup is separated from the utility cluster by the whole nav. The friction is the **npm mark**, for three reasons: it is the only filled container in the bar besides the Playground CTA (so it reads as a competing button immediately left of the real one), it is the highest-contrast, only-square-cornered element in a system that is otherwise rounded ink-on-paper, and it is optically much heavier than the Octocat sitting next to it, so two same-tier utility links read at different volumes. Recommendation is to render npm as a bare monochrome wordmark in `--ink-2`, matched to the Octocat's cap height, with no filled block — that makes GitHub+npm a coherent quiet pair and gives the CTA back its status as the only filled element. Not changed here: those marks landed in #294 and are not this PR's scope.
@graphite-app
graphite-app Bot force-pushed the feat/excalifont-wordmark branch from c8ea774 to 80dc9f4 Compare July 25, 2026 17:55
@graphite-app
graphite-app Bot merged commit 80dc9f4 into main Jul 25, 2026
10 checks passed
@graphite-app
graphite-app Bot deleted the feat/excalifont-wordmark branch July 25, 2026 17:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant