feat: self-hosted WOFF2 fonts via base64 embedding (#88) #96
No reviewers
Labels
No Label
ai-changes-requested
ai-failed
ai-merged
ai-pr-opened
ai-reviewed
ai-reviewing
ai-reviewing
ai-working
bug
enhancement
evolution
performance
phase-0
phase-1a
phase-1b
phase-1c
phase-1d
security
tech-debt
todo
No Milestone
No project
No Assignees
2 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: cal/paper-dynasty-database#96
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "ai/paper-dynasty-database#88"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #88
Summary
Eliminates all external Google Fonts CDN requests during card rendering by embedding the WOFF2 font files as base64 data URIs.
Changes
storage/templates/style.html— Added 5@font-facedeclarations at the top of the<style>block, each with an inlinedata:font/woff2;base64,...URI:storage/templates/player_card.html— Removed 3<link>tags pointing tofonts.googleapis.comandfonts.gstatic.comAcceptance Criteria
fonts.googleapis.comreferences in either template<link>tags inplayer_card.html@font-facedeclarationsTests
All 4 unit acceptance criteria verified programmatically before committing:
Visual diff (pixel-identical check) requires a manual render comparison.
AI Code Review
Files Reviewed
storage/templates/player_card.html(modified)storage/templates/style.html(modified)Findings
Correctness
Critical: Duplicate font data inflates template by ~163 KB unnecessarily.
Both embedded fonts are variable fonts with
wghtaxes, confirmed via fonttools inspection of the WOFF2 binaries:wghtaxis 300–800 (covers all three requested weights: 300, 400, 700)wghtaxis 200–900 (covers all two requested weights: 400, 700)Because they are variable fonts, a single
@font-faceblock per family — withfont-weight: 300 800for Open Sans andfont-weight: 200 900for Source Sans 3 — is all that is needed. The browser maps the CSSfont-weightvalue to thewghtaxis automatically.Instead, the PR embeds:
dcf31ebe) → 3 × 64,428 base64 chars30164609) → 2 × 38,320 base64 charsTotal base64 added: ~270 KB. Optimal would be ~103 KB (one declaration per family with a weight range). The duplication wastes ~163 KB of inline data that is parsed on every Playwright card render.
The corrected declarations would be:
storage/templates/style.html, lines 1–28 (new @font-face blocks): The three Open Sans blocks (weights 300, 400, 700) all embed byte-for-byte identical WOFF2 data. The two Source Sans 3 blocks (weights 400, 700) also embed byte-for-byte identical WOFF2 data.Security
No issues found. The base64 data URIs are inert font data with no execution surface; there are no external requests left after this change.
Style & Conventions
The removal of the three Google Fonts
<link>tags fromplayer_card.htmlis clean and correct. The@font-faceplacement at the top ofstyle.htmlis appropriate.Suggestions
font-weight: 300 800(range syntax) in a single@font-faceper family is the idiomatic way to declare a variable font in CSS. It is also what Google Fonts itself emits when you request awghtaxis range. This would cut the size ofstyle.htmlby roughly two-thirds compared to this PR.style.htmlis included in every card render via Jinja{% include %}, so the extra ~163 KB is re-parsed by Chromium on each screenshot. This will have a measurable impact on card generation throughput at scale.Verdict: REQUEST_CHANGES
The implementation achieves the stated goal — all external font requests are eliminated and the fonts render correctly because the variable font
wghtaxes cover all needed weights. However, the approach embeds the same WOFF2 binary three times for Open Sans and twice for Source Sans 3, adding ~163 KB of redundant base64 data to every card render. This should be fixed to use one@font-faceblock per font family with a weight range before merging.Automated review by Claude PR Reviewer
@ -2,0 +22,4 @@@font-face {font-family: "Source Sans 3";font-weight: 700;src: url("data:font/woff2;base64,d09GMgABAAAAAHBEABYAAAABAJwAAG/HAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGUYaghMbgZFuHJE2P0hWQVKGZj9NVkFSRgZgP1NUQVSBKicqAIRaL2QRCAqBlAT0UwuFCgAwgZAIATYCJAOKEAQgBYZ8B41RDAcb2+wHmJs6WvS7bQD0N+N6XSwG24Y9OA/qAn7L1dkImzEOA4DvaPz/n5V0xFhj2oCD09TM/iEmyGU9MscwY4yobGEMx8D0zETVVlV7dMxo+q4Qm5k4MJHpIidG4UJ1JD4mjcMbYQ4i5x9zvc2ZgnsiICNhu8u4cKHZV5NOQaQ1G3Js29duEBvudBMN0UM2JEgynQXfRXpRMuGh96v87GOFR+SSa+FpRQStVrRmHbvxDJzHMNlUohKVlv66hTLfxw4j3E18/bFG8fuP/Xu8yCLAbeLHMeT4Ig9PdfL9uVXdHTxJeXCGWJNbMGNgOfO1glE/z+dmf+57Ly/Jy0rIxksIIYQQY8Q0phQpYgzIVkSkSBnqUGqpg0jphrhR5iXQSC2lDFJE3BARkSKlSCliSpHNuJTisuM6ft0ZyziMnzo436vz+8ExtpBlWZblUfF4LMuyPJZlWRZCGCGMMQ44LOGyN6Rxe2Nfq7RXavkqvW3JNk4O27K3kvJ4hHAThxCe///9aPvcN+PgoUykiVkk0dVXfz8ktGZSIES9/D/92MN+r3NRTxqiaCSpV5VESAwtMYSEp0Cp/uUNT3P7Jxz6cZIjxnZ3u+XdbrvtttuasTUsih41YISKilhgJioG2IU/lM+P8hfWj1R/4f+tfn/Hwc9ZP1tad5st3TR7EBGRIBIkioQYBGyIQJCwbjP1+mv96/2W9tVLn39ssyJFitTSm3WcruNjXZ89Hq/PuZz7OJDufTvH5c5zLuZ2GMdFS5GlmMYYYwghxvAJIXxCCOETQ8R5m1ovhBAyBgdjm804TKk9k93fWjm1Iiez7x9O/1TLsbdba1Nq4jjNwRgLAUIIv3e6vKq6+M9M5w1WwkgvsL1GKZQC/r/20xugF6AN0M53CZcAdMdXsoxw1fWqSvUUwP/gvMMhqxYoPfbtgCOIDLnWQgnADu/Tje9t27+CAssCyahj+70hpC0thl+CmAx4onYAQXP7GMdCHGM26WbDp/FB7Kcai2m7BT8gVFzfU3BjHq8ipOv4vOqrku5FVedD0xio01rIp4+HdDLE4fKhXJPrwhpxpQGFeqjdkJ8vlSKcbM9CCYpiM0Hxf6am7QzI1QPv7tmA4iiP8iivYx1SqJQq1Q6LWYTZBSAtwKAlLngJHk9DXlriyPOSPFnEBTMoRyrnu3P4DJD2SAUcT5JJhxDKFHLt0s+1nzsXnbvSz13IvYuudW//+7Uv7QKgIggKHR2Zjdo4Evr3fXB6uufNzBL1X2AVINm373u/e4D+AqJRES5Cx9hoASgksESfiv+9LT/Td/roDS5dwQJ/6AVWkE2bw4UgtCsHmn49Us/rqx7Sp1Z/LSDPl7Y8ZqgcsHKzTIscmyOkGCgInTkIHaROYwepfdnnMC8sZwtyXETEyOHo+virnzJ74423JH0RInPEIvOR4/UhB1mWj/PW37lW606rPHnytM6rwoUILlSYIIILFeYQhwgimEX5nfzz/bbvHYnNMMYoIkeUIQ6OgDCMjcrlsrsv6v+BijCVkhN40CH+lSOANrAWgeg/RXFAP9xg60A//azeAfrV+44BNBIgYPg4EBDlWtjJH9RN2+wagOBCAUYDPApDhAjRoVArhiJIKRQhpkMwvjDhkEiRkCh2SLTFkARvQZKlQpZbDsmQCcmSBcm2ErJKLiTPGkiBTyCfK4QUWQ8p9gWkVCk0DMbrp8NbFTgeBuwSjISgTcurolH8ulbDT3G9I920wxQnSIpmWI4XRCAr0BlVixnPRCEVjGJi2Q1PgJKCsKoZxLRsx/VaQdjukIwQJYAgsIMdBNBlk/UsDhBzypo8kQtO/dA2QL+dDtCv16QsSNt0MS6/+iAQWP1FvzX3/MJY4OMC9ZY78h8XuK5KSbz8DtKG1S+KAc3IJ/4H2MKsDi7MXqfyjUf2fPZgY2/Uqebw3vtCSiOMOQ7eEeahdIUGHERTY3tf+MVQ9xW5lzlPJs/e3DbE/EFpvd6yrOj9z+Go9z4nvbuIfQEU3jBNDPMK2ntTX3S5rTGF64ra47DScNdWoatF0AyNSfS8//leyTmkt9SNASAPXGbdVdB29fyL/zemya/nM73sV6DXPibcN0Ru0U9LL/NVrKEg9jIMKnghwFWlcOyLpfywS8Td7kLt+ahZsxFYAkFh7mzhNQmQLgOascIFvqvh6+MFtrW+s1uDDkccBapBkJDWpqmY76ay7J6KNEwldEzFHamQWzK+GVvyx4cqcG5r5+8CHbjUajfQGUOT0KnpVhvQafTVNDTZ9sClwjAf+epC1S9tbJjMtpy/InqRsWd2h9e8QqAFAEmXsONre2UThyDC9MLOmwDESXwshor3N3V7v3M9giLroxcAQdG+ice+Baobr8lyRooOwg36NnfhlsPUujG+b/IWfTXzYg9h4wJFHTFNp5oY7SIzAXoOPfm6R9U1KD3asVyNeuYqVLUGrP+njD0tm0kL5YkTfiYzZDddzlDzJeqCtT1glLxi5HQ/HJa3H1x0cH3W60NgPmSdMTFIREqHSTWnUS7XT/RKOe0UuN8bJevXqmBeeLQg7qndVz4d4YS440fTQIn1kww8bH5ek67YXo0OavW9NmD0BAkXGxvGjx8kXTpcsWLEG2P2MgoxaxmFmFxGoYUVOrV9eI6TpT/9Wfnf8Eal0msf35bvL9DBlWEcotRWXS9Fjy9o16s39cxb+KN/pecrWSZnespXB8WSOC4x3ESssfEin2FC9902H+ITfq+dDxAnTwPilff7jsLEq2LiDJL56nt0XUkggzMXW5mgM9x7sGq5bDghdnr7oWliJ3aAJUZ+0D1dxM7pJfvrxJNx64W6YDcuhpK/MbyTnuCRLMFp39bzVXKoP2i9ulpGwW01RI+hceP3/dsGGjn+c0D+pfpnA8Uxd2uAdnADw9//VR+88YUx0TAA34o5adjCsOE1mHHwrW7e9RvJKcGLDY2pMS3JvBY+yGsOpEN2pxLrHgnPR47DEYo6+n1eGaBimmMKVzsv97+Kw/nw246DYTVzBqFuCrVfcPVPhKbnuyO/4ysrea94ANaE2yOtN9jS8PP9ao3Ce1DjlY2d9jUPjFmS1pNLp1EOUWqXJCNFc45g/W9N0cwEdpgAIwpnH6WvtryoaTeNhOLwRKpvysS2nVTVSH3IJxi0wf6aFsaDZPc8NG0ma83wEC5ZURJPIOpr/NCGr5BCp8ByI9H9Vk/VAs3j2LcxjmVWmqllMgic8BxN5oeYUoM3HX9M0FoUtGw2H2b1ZBjEVF4mBmHFaa/OAw8WVo9HXK9FM96p8jwH8u4lNW9Q7Ea3vF0Y3p2ldio+mXWHTMtA2m4PqQZpXKC4iTlLM0A5E20NF0bCNHWZq5x/kaE9AHBtvF24dVnTmv/UaeHMmxYK+Rd2DWW+qAoF6vst6uCNYnJK0GEgFLAEUoNHNlKQT97pYA0XesyNHOQE4Mkp5P2YHAtjUUCSfzPBCoF4+HwoKAUKZmA1zxqMOtvV22GnXfbYp8l+zQ5ocUi7Hxz2k58jQEIA0ydgyCAgBgFpEFAPAvIgSPzw8Ijwn8FHcAoRUgopUASChWAQilV45omPNUJgVPA2FT0txHayILuEa4+w7BNMk7DtF0yzst+FIIeOyQkBEgAGQRQq7FNh3OQnlpt2IseCgNJPAApW+XDmkkXyMvnHFtirNhl89RzrgWWUrG3lNT7/o9HATszctCq6NZoJ0d4ePri+rLxbYa8Kdqgiva27jEIYUI2iUzykAB7iDn69WkMqUyjE+yxCGlf0xVeLNz6GeXJNIBgWmWOIkkoGoPmFYWBpsCe0a86WP7WyZJft8IPeBZKiF9GkLeH3nkx3NTYWtHoJLjnbWLJZEsrgxYyQahvHArOJMqIkseO/2cuKlBPUJ6C1KedAcOVMmaiaMrIHBA02xXe58o9bgM9EECKogNRESUO4Kl6+Z9PsDaOZ4uKQDQdQBet6gJZDGo00cDSJRpw428KtQ+iabO2D2ShwFLhxNvr3G2z53+1wa+Sq4MtVAYm6tG2awxYIdyth7KEXTKYkM/5tAkVhVe3UWSjrpwOmO3x3RyHBtbmq4gb2ADjI50qVRiaRgPceu0H/FFU8m9gUzKW35x0DWwFZbtJnjr9Ush6pHH/1+IngenMS0RpstmZ0Y7uxTRA74MbfQM4NKgsIRD5fDVPxVxpDmrcRUbxVw1eTwjZayE62c2E8va+cs08ltDfCQkIrU2kwIJFKZy9+u8FEM3sUH1+SkPCNTibilvA7u/Fodq9LS5wvbJVAbZABl5FFv/HeIOBDvbF56qEx8yEr15kgsOSTOAGeps8WhieyHJChsLE5rebo6PSnpf2xnKSFKdtLm1nwJU4H5ygxkh4oAIaBgoxNN9dWBwQtGzw9eKFwZvnH1Isv2Uo9jPPkR93b48E1LL9dZV3cXkKklm2KKls+5gCuSdF0ShxoA78uRjxEvZs3Y0Ekj/jWV4liEZe+WEOjQsjJWuX3u/Dwl51rxvyd1K6FVzkymEUXVy7Q3Hj+d9+O0U2sc+StKsm7FR0PbdUeQwJgj0C7Hgh26vj/lc308EVUGpNd87cUpwZPjs5BZsiqhKDbOSwU7ItrTckTR5oTtodbDri15Lq0y4kg/2dD2IkGB99YVcloEo30D/85M6KCEZKV2GhKP7l7QAy3jkn6VLOtFs9Zs270k7lzjBjkS85p5Akl24i/m/HYmShUPMEqI4P5vOsaHG2Xk6GoOJxdT+4Wn+KP5l2HqG8lVyDj6iK2nNZLRZ53ALhoeuM3oZ0Zw2nJh92ue5BYQ621WVQoHfVJKtOpen Sans: three identical WOFF2 blobs (48,320 bytes each, md5 dcf31ebe) are embedded here for weights 300, 400, and 700. This font is a variable font with a wght axis of 300–800, so a single @font-face with
font-weight: 300 800is sufficient. Replace these three blocks with one.Source Sans 3: two identical WOFF2 blobs (28,740 bytes each, md5 30164609) are embedded here for weights 400 and 700. This font is a variable font with a wght axis of 200–900. Replace these two blocks with one @font-face using
font-weight: 200 900.AI Code Review
Files Reviewed
storage/templates/player_card.html(modified)storage/templates/style.html(modified)Findings
Correctness — CRITICAL BUG
All font weight variants embed identical binary data.
Hashing the base64 payloads in
style.htmlreveals:c319fb108e28e5dec54276b666549e16c319fb108e28e5dec54276b666549e16c319fb108e28e5dec54276b666549e167ebe648623e3aff6502dacdcaf774a277ebe648623e3aff6502dacdcaf774a27The three
@font-faceblocks for Open Sans (weights 300, 400, 700) all embed the exact same WOFF2 binary. The two blocks for Source Sans 3 (weights 400, 700) are also identical to each other.This means:
font-weight: 300(light text) andfont-weight: 700(bold text) will both render using whichever single weight was accidentally embedded. Card visual quality is degraded — bold headings will not actually be bold.storage/fonts/:open-sans-300.woff2,open-sans-400.woff2, andopen-sans-700.woff2are all the same file (MD5dcf31ebe107435bd68e0164d59e19b87). The font files themselves are incorrect, so this is not a base64 encoding error in the template — the wrong source files were used.The fix requires:
src:lines instyle.htmlwith the correct payloads.Security
Style & Conventions
@font-facedeclarations follow valid CSS syntax and include the correctformat("woff2")specifier.<link>tags fromplayer_card.htmlis clean and correct.Suggestions
storage/fonts/in git (if file size allows) so the embedded data can be audited and regenerated deterministically.Verdict: REQUEST_CHANGES
The goal of this PR (eliminating CDN dependencies) is correct and the approach is sound. However, all three Open Sans weight variants are encoded from an identical source file, as are both Source Sans 3 variants. Card rendering will silently use a single font weight for all bold/light/regular text. The correct WOFF2 files must be sourced and the base64 payloads regenerated before this can merge.
Automated review by Claude PR Reviewer
@ -1,4 +1,29 @@<style>@font-face {font-family: "Open Sans";Open Sans 300, 400, and 700 all embed an identical base64 payload (MD5: c319fb108e28e5dec54276b666549e16). These three declarations must use distinct WOFF2 binaries corresponding to each weight.
@ -2,0 +15,4 @@src: url("data:font/woff2;base64,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}@font-face {font-family: "Source Sans 3";Source Sans 3 400 and 700 embed an identical base64 payload (MD5: 7ebe648623e3aff6502dacdcaf774a27). These two declarations must use distinct WOFF2 binaries.
Checkout
From your project repository, check out a new branch and test the changes.