Repository navigation
Fix Open to Work badge export alignment - #146
Merged
Merged
Conversation
Use html2canvas-safe CSS markup for the Open to Work badge icon and text so downloaded Devémon and README banner assets match the live layout. Add a Playwright regression that exercises Devémon badge/card downloads with mocked GitHub data.\n\nCo-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes Open to Work badge alignment and PNG export compatibility.
Changes:
- Replaces fragile SVG briefcase icons with CSS-rendered icons.
- Stabilizes badge flex alignment and export-safe footer colors.
- Adds Playwright coverage for Devémon PNG downloads and badge geometry.
Show a summary per file
| File | Description |
|---|---|
tests/devemon-download-layout.spec.ts |
Tests badge alignment and PNG exports. |
src/components/ReadmeBanner.tsx |
Updates the banner badge icon and alignment. |
src/components/DevemonCard.tsx |
Updates card badges and footer styling. |
src/components/DevemonCard.module.css |
Defines export-safe icon, badge, and footer styles. |
Review details
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- Files reviewed: 4/4 changed files
- Comments generated: 0
- Review effort level: Balanced
Wrap Devémon card label text so html2canvas renders rarity, Open to Work, and stat labels on the same visual centerline as the browser preview. Relax PNG dimension assertions to account for CI font metric differences while still verifying valid downloads.\n\nCo-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Set the Available for Hire badge text/icon to the dark background token for readable contrast, increase the rarity pill text offset for html2canvas export centering, and add a regression assertion for the badge text color.\n\nCo-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Increase the Devémon card header gap so the rarity pill no longer overlaps the username in exported PNGs, and add a regression assertion that the name-to-rarity spacing stays non-overlapping.\n\nCo-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Downloads and social shares rasterize the same DOM the user is looking at, so an exported PNG should be a faithful copy of the on-screen render. It wasn't: labels like "LEGENDARY" and "OPEN TO WORK" sank to the bottom of their pills, stat labels sat low in their cells, and the rarity pill looked like it floated above the @login handle. Root cause: html2canvas reimplements text layout rather than deferring to the browser. It paints each run at `textRect.top + <its own baseline probe>`, and with the Monaspace Neon / Mona Sans webfonts that probe overshoots the real ascent by ~0.5em: element size h2c baseline real ascent drift Username 20px 29 19 +10 @login 14px 22 15 +7 RarityBadgeText 14px 21 13 +8 StatLabelText 11px 16 10 +6 Because *every* run sank while the pill boxes stayed put, the pill read as raised relative to the handle. Per-element translateY compensation only ever patched a few of the affected nodes, so it was replaced here. Serialize the node into an SVG <foreignObject> (modern-screenshot) and let the browser rasterize it. Layout, font metrics, filters and colors then come from the same engine that painted the preview, so export matches preview by construction. This also fixes the Tailwind v4 oklch() parse failure and restores the avatar's blur glow, which html2canvas silently dropped. Also define readable text on the brand-green "OPEN TO WORK" pill: --color-brand-on-primary was referenced but never declared anywhere, so the color inherited and rendered near-invisible in dark mode. All ten export call sites now share src/utils/domExport.ts. Fixes #59 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3a633ff9-3c7e-4499-825f-af5baab48b1c
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #59
Symptom
Downloaded PNGs didn't match what the browser rendered. Pill labels like LEGENDARY / MYTHICAL and OPEN TO WORK sank to the bottom of their pills, stat labels (
REPOSITORIES,FOLLOWERS,STARS…) sat low in their cells, and the rarity pill looked like it floated above the@loginhandle instead of sharing a center line with it.Root cause
html2canvasreimplements text layout rather than deferring to the browser. It paints each text run attextRect.top + <its own font baseline probe>. With the Monaspace Neon / Mona Sans webfonts this project uses, that probe overshoots the real content-box ascent by roughly half an em:So every text run sank, while the pill boxes — which are real layout boxes — stayed where they belonged. That's why the pill read as "raised" relative to the handle: the pill was correct and the text around it had dropped.
This is why per-element
translateYcompensation was the wrong tool. It only ever patched a handful of the affected nodes, and the constants were specific to one font size and one viewport.Fix
Serialize the node into an SVG
<foreignObject>and let the browser rasterize it (modern-screenshot, MIT). Layout, font metrics, CSS filters and colors are then produced by the same engine that painted the preview, so export matches preview by construction — no compensation constants anywhere.Two things fall out of this for free:
oklch()parse failure goes away, so the.CardFooterCSS-module workaround is no longer needed;filter: blur()glow renders again —html2canvashad been silently dropping it.All ten export call sites (5 in
DevemonCard.tsx, 5 inReadmeBanner.tsx) now sharesrc/utils/domExport.ts, which removes a good deal of duplicated boilerplate.ReadmeBannerkeeps its original 2x scale; the Devémon card keeps 3x.Separately: an actually-undefined custom property
--color-brand-on-primarywas referenced by.HireBadge/.BadgeHireBadgebut never declared — not inglobal.css, not anywhere. An undefined custom property makescolorinherit, which left near-invisible light text on the bright brand-green "OPEN TO WORK" pill in dark mode. Now usesvar(--color-bg-primary).Result
The downloaded card on its own:
Validation
npm run check— 0 errors, 0 warningsnpm run build— cleannpx playwright test— 4/4 passingcenterDeltabetween the rarity pill and@loginis now 0.tests/devemon-download-layout.spec.tsis rewritten to assert the property that actually matters: it triggers a real download, screenshots the same DOM node at the same 3x scale, and compares them pixel-by-pixel. That means it fails on any renderer that reinvents layout or text metrics, rather than asserting on incidental class names. Confirmed to have teeth — swapping the renderer back tohtml2canvasfails the test.Notes for reviewers
html2canvas, addsmodern-screenshot@^4.7.0(MIT, no transitive deps).mainis small (+143 lines, most of which is the new test).