Skip to content

Build the gallery ahead of the photographs - #10

Merged
laveshparyani merged 1 commit into
mainfrom
feat/gallery-scaffold
Oct 1, 2026
Merged

laveshparyani merged 1 commit into
mainfrom
feat/gallery-scaffold

Conversation

@laveshparyani

Copy link
Copy Markdown
Collaborator

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

  • /gallery with a masonry grid (span: 'tall' | 'wide' drives the rhythm; it
    is a layout hint, not a crop, so <Picture> keeps each image's real intrinsic
    ratio and CLS stays at zero).
  • 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 — 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_PLACEHOLDER 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. Going
live is: drop photos in public/assets/, add PLAN entries, npm run images,
replace GALLERY with real entries and alt text, flip the flag.

Two things worth a second look

Focus restore avoids requestAnimationFrame on purpose. 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. It runs in the
dialog's unmount cleanup instead, so it fires however the dialog was dismissed.

src/data/seo.js now imports ./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. 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 reading document.activeElement: the preview 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.

Grid geometry asserted from the DOM (tall = 2 rows, wide = 2 columns, 3 columns
at desktop), no horizontal overflow, npm run verify:seo passes at 8 routes
with 5 in the sitemap.

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.
@laveshparyani
laveshparyani merged commit 28ce188 into main Oct 1, 2026
3 checks passed
@laveshparyani
laveshparyani deleted the feat/gallery-scaffold branch October 1, 2026 08:06
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