Build the gallery ahead of the photographs - #10
Merged
Merged
Conversation
The client's priority, blocked only on a Drive folder that was empty at the review. Building it now means the day the photos land is a data file and an image run, not a build. - /gallery with a masonry grid and an accessible lightbox: Escape, arrow keys wrapping at both ends, backdrop click, body scroll locked while open, focus moved in on mount and back to the invoking tile on unmount. - Focus restore runs in the dialog's unmount cleanup rather than the close handler, so it runs however the dialog was dismissed, and deliberately not through requestAnimationFrame — rAF does not fire in a background tab, and a focus restore that silently does not happen is how a gallery becomes unusable by keyboard without anyone noticing. - GALLERY_IS_PLACEHOLDER is the only switch. It hides the nav entry, sets noindex, and keeps the page out of the sitemap — all three read it, so the page cannot go half-live. The tiles are the existing placeholder renders and say so, in the same terms the Projects page already uses. Also tightens three entries in the SEO table that were over the limits the file itself documents: the services title was 65 characters against a ~60 truncation, and the services and about descriptions were 160 and 161 against 158. src/data/seo.js now imports from ./gallery.js with the extension. Both files are loaded by vite.config.js in raw Node, which does not resolve extensionless specifiers even though Vite does. Verified by instrumenting HTMLElement.focus rather than reading document.activeElement: the pane had lost window focus, and Chrome will not move activeElement in an unfocused document, which made three earlier runs look like a regression that was not there.
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.
The client's stated priority, blocked only on a Drive folder that was empty at
the review. Building it now means the day the photos land is a data file and an
image run, not a build.
What's here
/gallerywith a masonry grid (span: 'tall' | 'wide'drives the rhythm; itis a layout hint, not a crop, so
<Picture>keeps each image's real intrinsicratio and CLS stays at zero).
click, body scroll locked while open, focus moved in on mount and back to the
invoking tile on unmount — not the tile currently shown, which is what
WAI-ARIA asks for and the only predictable place to land after arrowing.
One switch
GALLERY_IS_PLACEHOLDERhides the nav entry, setsnoindex, and keeps the pageout of the sitemap. All three read it, so the page cannot go half-live. Going
live is: drop photos in
public/assets/, addPLANentries,npm run images,replace
GALLERYwith real entries and alt text, flip the flag.Two things worth a second look
Focus restore avoids
requestAnimationFrameon purpose. rAF does not fire ina background tab, and a focus restore that silently does not happen is how a
gallery becomes unusable by keyboard without anyone noticing. It runs in the
dialog's unmount cleanup instead, so it fires however the dialog was dismissed.
src/data/seo.jsnow imports./gallery.jswith the extension. Both filesare loaded by
vite.config.jsin raw Node, which does not resolve extensionlessspecifiers even though Vite does. Without it the build fails outright.
Also
Tightens three entries in the SEO table that were over the limits the file
itself documents — services title 65 against a ~60 truncation, services and
about descriptions 160 and 161 against 158.
Verification
All four close paths (Escape, Escape-after-arrowing, close button, backdrop)
restore focus to the right tile. Verified by instrumenting
HTMLElement.focus, not by readingdocument.activeElement: the preview panehad lost window focus, and Chrome will not move
activeElementin an unfocuseddocument — which made three earlier runs look like a regression that was not
there.
Grid geometry asserted from the DOM (tall = 2 rows, wide = 2 columns, 3 columns
at desktop), no horizontal overflow,
npm run verify:seopasses at 8 routeswith 5 in the sitemap.