feat(ui): image lightbox - click an inline image to view it enlarged (LC-262) #309

Merged
longjacksonle merged 1 commit from feat/lc-262-image-lightbox into main 2026-06-14 04:49:16 +02:00

What

Inline image attachments now open in an in-app lightbox (LC-262) instead of navigating to the raw file in a new tab. Click an image to view it enlarged over a dark backdrop with the filename as a caption; ArrowLeft/ArrowRight page through every image on the page (wrapping), and Esc / backdrop click / the close button dismiss it.

How

  • partials/attachment.html: the image anchor gains a data-lc-lightbox marker but keeps its href + target="_blank", so with JS disabled the click still opens the raw image in a new tab (progressive enhancement - nothing hidden from no-JS clients).
  • templates/lightbox_modal.html: a singleton overlay in the persistent shell, mirroring the shortcuts/switcher modal lifecycle (__lcDialogTrap focus trap, opener focus restore). A delegated click on a[data-lc-lightbox] snapshots all such anchors on the page, opens at the clicked index, and shows the full-size href. Plain left-click only - modified clicks (open-in-tab) keep native behavior. Covers images rendered after load (live arrivals, thread panel, edits) for free via delegation.
  • main.css: .lc-lightbox-img constrains the image to the viewport (90vw / 85vh, object-fit: contain); sizing lives here rather than as Tailwind arbitrary values so it is reliable without a CSS rebuild.
  • Strings localized in en + es.

No new route, DB, or env var. Not operator-visible. No LC-77 fixture impact (those carry no attachments).

Tests

routes_drafts::image_attachment_ships_lightbox_markup seeds an image attachment (insert_upload + link_upload_to_message) and asserts the anchor is lightbox-enabled and the page ships the modal. just test and just test-saas pass.

QA note

The viewer is JS-driven, so worth a quick manual pass before merge: click an image (opens enlarged with caption); with 2+ images, Arrow keys + prev/next page through and wrap; with one image, prev/next are hidden; Esc / backdrop / close dismiss and focus returns; Ctrl/Cmd-click still opens the raw file in a new tab; confirm the no-JS fallback (anchor still has href/target).

## What Inline image attachments now open in an in-app lightbox (LC-262) instead of navigating to the raw file in a new tab. Click an image to view it enlarged over a dark backdrop with the filename as a caption; ArrowLeft/ArrowRight page through every image on the page (wrapping), and Esc / backdrop click / the close button dismiss it. ## How - `partials/attachment.html`: the image anchor gains a `data-lc-lightbox` marker but keeps its `href` + `target="_blank"`, so with JS disabled the click still opens the raw image in a new tab (progressive enhancement - nothing hidden from no-JS clients). - `templates/lightbox_modal.html`: a singleton overlay in the persistent shell, mirroring the shortcuts/switcher modal lifecycle (`__lcDialogTrap` focus trap, opener focus restore). A delegated `click` on `a[data-lc-lightbox]` snapshots all such anchors on the page, opens at the clicked index, and shows the full-size `href`. Plain left-click only - modified clicks (open-in-tab) keep native behavior. Covers images rendered after load (live arrivals, thread panel, edits) for free via delegation. - `main.css`: `.lc-lightbox-img` constrains the image to the viewport (`90vw` / `85vh`, `object-fit: contain`); sizing lives here rather than as Tailwind arbitrary values so it is reliable without a CSS rebuild. - Strings localized in en + es. No new route, DB, or env var. Not operator-visible. No LC-77 fixture impact (those carry no attachments). ## Tests `routes_drafts::image_attachment_ships_lightbox_markup` seeds an image attachment (`insert_upload` + `link_upload_to_message`) and asserts the anchor is lightbox-enabled and the page ships the modal. `just test` and `just test-saas` pass. ## QA note The viewer is JS-driven, so worth a quick manual pass before merge: click an image (opens enlarged with caption); with 2+ images, Arrow keys + prev/next page through and wrap; with one image, prev/next are hidden; Esc / backdrop / close dismiss and focus returns; Ctrl/Cmd-click still opens the raw file in a new tab; confirm the no-JS fallback (anchor still has href/target).
feat(ui): image lightbox - click an inline image to view it enlarged (LC-262)
All checks were successful
check-secrets / Nosey parker (push) Successful in 6s
check-secrets / Kingfisher (push) Successful in 7s
check-secrets / TruffleHog (push) Successful in 9s
check-secrets / Kingfisher (pull_request) Successful in 5s
check-secrets / Nosey parker (pull_request) Successful in 5s
Create release / Create release from merged PR (pull_request) Has been skipped
check-secrets / TruffleHog (pull_request) Successful in 6s
Check / clippy + fmt + tests (pull_request) Successful in 2m51s
47a81c12ec
Inline image attachments now open in an in-app lightbox instead of navigating to the raw file in a new tab. Clicking an image shows it enlarged over a dark backdrop with the filename as a caption; ArrowLeft/ArrowRight page through every image on the page (wrapping), and Esc / a backdrop click / the close button dismiss it.

The image anchor in attachment.html gains a data-lc-lightbox marker but keeps its href + target, so with JS disabled the click still opens the raw image in a new tab (progressive enhancement, nothing hidden). A delegated click handler covers images rendered after load (live arrivals, thread panel, edits) and leaves modified clicks (open-in-tab) alone. The modal is a singleton in the persistent shell mirroring the shortcuts/switcher lifecycle (__lcDialogTrap focus trap, opener focus restore); image sizing lives in main.css so it is reliable without a Tailwind rebuild.

Strings localized in en + es. Test seeds an image attachment and asserts the anchor is lightbox-enabled and the page ships the modal.

#LC-262
#LC-263

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
longjacksonle deleted branch feat/lc-262-image-lightbox 2026-06-14 04:49:16 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/lets-chat!309
No description provided.