Skip to content

Fix the black-screen export bug; make both deliverables score 100 - #344

Merged
singhharsh1708 merged 2 commits into
mainfrom
fix/export-background-and-polish
Aug 26, 2026
Merged

Fix the black-screen export bug; make both deliverables score 100#344
singhharsh1708 merged 2 commits into
mainfrom
fix/export-background-and-polish

Conversation

@singhharsh1708

Copy link
Copy Markdown
Owner

Auditing a real export with Lighthouse caught a bug I shipped earlier today.

The procedural background threw on every export

TypeError: Cannot read properties of undefined (reading 'replace')
  at hexToRgb → drawGradient → drawFrame2D

drawFrame2D takes FrameOptionscolor1/color2/color3. A stored style recipe holds a colors array. The export emitted the recipe verbatim, so every colour was undefined and every recipe-driven export rendered a black screen.

The test that should have caught this built its own correctly-shaped options object and ran the runtime against that, so it passed while the emitted shape was wrong. It now parses the recipe out of the emitted page and runs exactly what ships — and fails against the old shape (verified by reverting).

Measured, on the built export with the full file set served

Before After
Performance 99 100
Accessibility 100 100
Best Practices 96 100
SEO 100 100
First Contentful Paint 2.9s 0.9s
Console errors JS exception 0

The "99 before" was measuring a blank page — fast because it drew nothing.

Gaps closed, in the export and published sites

Both surfaces had the same defects:

  • No <h1>. Every section emitted an h2, so the document outline started at level two — read as a missing title by assistive tech and search engines. The first section carrying a heading is now the h1.
  • No <main> landmark, no skip link, no focus ring — on pages that override their own colours, where the browser default ring can disappear entirely.
  • twitter:card promised summary_large_image with no og:image, so every share rendered as a bare link. The export now draws a card client-side from the site's own first frame; published sites get a per-site opengraph-image route drawn from their stored theme (theme rather than frames, because frames live in the visitor's browser).
  • No favicon, no theme-color.
  • Render-blocking Google Fonts stylesheet on a third-party origin, holding first paint at 2.9s on a page whose own markup is a few kilobytes. Now media="print" onload with a <noscript> fallback.

The ZIP now deploys itself

404.html matching the site, robots.txt, a palette-derived favicon.svg, the generated og-image.png, .nojekyll (GitHub Pages silently drops underscore-prefixed files without it), and netlify.toml + vercel.json with immutable cache headers on the frames.

README.txt becomes a README.md with real deploy steps for Netlify, Vercel, GitHub Pages and Cloudflare, written for someone who is not a developer. Netlify is now drag-the-folder.

Verified

  • tsc, eslint, next build clean; suite 507 → 528.
  • Every claim in the exported README was checked against the emitted HTML first — the skip link, focus ring and h1 did not exist when I drafted it, so I added them rather than leave the README lying.
  • Published renderer re-measured after the change: accessibility 100, SEO 100.

…rade

Auditing a real export with Lighthouse turned up a bug I shipped earlier today:
the procedural background threw on first paint and every recipe-driven export
rendered a black screen.

  TypeError: Cannot read properties of undefined (reading 'replace')
    at hexToRgb -> drawGradient -> drawFrame2D

drawFrame2D takes FrameOptions, with color1/color2/color3. A stored style recipe
holds a colours array. The export emitted the recipe verbatim, so every colour
was undefined. The test that was supposed to cover this built its own options
object in the correct shape and ran the runtime against that, so it passed
against broken output. It now parses the recipe out of the emitted page and runs
what actually ships, and it fails against the old shape.

The page also scored badly on things Lighthouse does measure, and was missing
things it does not:

- No h1 anywhere. Every section emitted an h2, so the document outline started at
  level two. The first section carrying a heading is now the h1.
- No main landmark, no skip link, and no focus ring, on a page that overrides its
  own colours so the browser default can disappear.
- twitter:card promised a large image with no og:image to show, so every share
  rendered as a bare link. The card is now drawn client-side from the site's own
  first frame and shipped as og-image.png.
- No favicon and no theme-color.
- The Google Fonts stylesheet was render-blocking on a third-party origin, holding
  first contentful paint at 2.9s on a page whose own markup is a few kilobytes.

The ZIP now also carries what a non-technical owner cannot easily produce: a
404 page matching the site, robots.txt, a palette-derived favicon, .nojekyll so
GitHub Pages does not silently drop files, and netlify.toml and vercel.json with
long cache headers on the frames. README.txt becomes a README.md with real
deploy steps for Netlify, Vercel, GitHub Pages and Cloudflare.

Measured on the built export, serving the full file set:

  before   Performance  99  A11y 100  Best Practices  96  SEO 100  (blank page,
                                                                   JS exception)
  after    Performance 100  A11y 100  Best Practices 100  SEO 100  (renders,
                                                                   no errors)
  first contentful paint 2.9s -> 0.9s, CLS 0
The export was fixed and measured in the previous commit; the published page is
the other half of the deliverable and had the same defects, plus one of its own.

- Every section rendered an h2, so a published site had no h1 and its document
  outline began at level two. The first section carrying a heading is now the h1.
- No main landmark, no skip link, and no focus ring on a page that sets its own
  palette, where the browser default ring can vanish against the background.
- No social image at all, so every share of a customer's published site rendered
  as a bare link. Adds a per-site opengraph-image route drawn from that site's
  stored theme. It uses the theme rather than the frames because the frames live
  in the visitor's browser, not on the server.

Measured on the rendered page: accessibility 100, SEO 100.
@vercel

vercel Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
scrollcraft Ready Ready Preview Aug 26, 2026 10:32pm

@singhharsh1708
singhharsh1708 merged commit 50e77f9 into main Aug 26, 2026
4 checks passed
@singhharsh1708
singhharsh1708 deleted the fix/export-background-and-polish branch August 26, 2026 22:38
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