Skip to content

Comments

feat(figure): add image zoom functionality with multiple modes#1139

Merged
linawolf merged 9 commits intoTYPO3-Documentation:mainfrom
CybotTM:feature/figure-zoom-directive
Jan 18, 2026
Merged

feat(figure): add image zoom functionality with multiple modes#1139
linawolf merged 9 commits intoTYPO3-Documentation:mainfrom
CybotTM:feature/figure-zoom-directive

Conversation

@CybotTM
Copy link
Contributor

@CybotTM CybotTM commented Dec 22, 2025

Summary

Add :zoom: option to figure and image directives supporting four zoom modes:

  • lightbox: Click to open full-size in modal dialog overlay
  • gallery: Click to open with navigation between grouped images
  • inline: Mouse wheel zoom directly on image with pan support
  • lens: Magnifying lens follows cursor showing zoomed view

Usage

.. figure:: /images/screenshot.png
   :zoom: lightbox

   Click to enlarge this screenshot

.. figure:: /images/photo1.png
   :zoom: gallery
   :gallery: photos

   Part of the photos gallery

Features

  • :zoom: option accepts lightbox | gallery | inline | lens
  • :gallery: option groups images for gallery navigation
  • :zoom-indicator: option to show/hide the zoom icon (default: show)
  • Zoom indicator with appropriate cursor (zoom-in or crosshair)
  • Tooltips on indicators ("Click to enlarge", "Scroll to zoom")
  • Click/scroll events pass through indicator to underlying image
  • Full keyboard accessibility for all zoom modes
  • Respects prefers-reduced-motion media query
  • Works with with-border and with-shadow styling classes

Technical Changes

  • New FigureDirective.php extending SubDirective
  • New image.html.twig template for standalone images with zoom
  • JavaScript in image-zoom.js bundled into theme.min.js
  • CSS in _component_image_zoom.scss imported into theme.scss
  • Updated figure.html.twig with zoom data attributes

Test plan

  • Verify lightbox mode opens dialog on click
  • Verify gallery mode with navigation between grouped images
  • Verify inline mode with scroll-to-zoom and drag-to-pan
  • Verify lens mode with magnifying glass following cursor
  • Verify keyboard navigation works for all modes
  • Verify zoom indicator positioning with border/shadow classes
  • Verify :zoom-indicator: false hides the indicator

@CybotTM
Copy link
Contributor Author

CybotTM commented Dec 27, 2025

Fixes Applied

Based on @linawolf's testing feedback, the following issues have been resolved:

🔧 Lightbox (was: "does nothing")

  • Root cause: JS expected a pre-existing <dialog> element referenced by data-zoom-dialog attribute, but none was created
  • Fix: Now creates the dialog dynamically when initializing lightbox triggers

🔧 Gallery (was: "opens empty")

  • Root cause: JS looked for trigger.src but trigger is <figure> element (not <img>), so src was undefined
  • Fix: Now finds the <img> child inside the figure and extracts src/caption from there

🔧 Lens (was: "looks wrong")

  • Root cause: CSS expected .lens-zoom-container class but container was just <figure data-zoom="lens">
  • Fix: JS now adds required class and positioning styles to the container

🔧 Inline

  • Similar fix: adds required container class and overflow styles

✅ Additional Changes

  • Added image.html.twig template to support :zoom: on standalone image directive (not just figure)
  • Added render test examples for both figures and images with all zoom modes
  • Rebuilt compiled assets (theme.css, theme.min.js)

Note: Changes from #1140 are now integrated into this PR.

@CybotTM CybotTM changed the title Add image zoom/lightbox feature with :zoom: RST directive feat: Add image zoom/lightbox feature with :zoom: directive option Dec 27, 2025
@linawolf
Copy link
Member

Happy to see you are working on this, please ping me when you require my review

Add :zoom: option to figure and image directives supporting four modes:
- lightbox: Click to open full-size in modal dialog overlay
- gallery: Click to open with navigation between grouped images
- inline: Mouse wheel zoom directly on image with pan support
- lens: Magnifying lens follows cursor showing zoomed view

Features:
- :zoom: option accepts lightbox|gallery|inline|lens
- :gallery: option groups images for gallery navigation
- :zoom-indicator: option to show/hide the zoom icon (default: show)
- Zoom indicator with appropriate cursor (zoom-in or crosshair)
- Tooltips on indicators ("Click to enlarge", "Scroll to zoom")
- Click/scroll events pass through indicator to image
- Keyboard accessibility for all zoom modes
- Respects prefers-reduced-motion
- Works with border/shadow styling classes

Technical:
- New FigureDirective.php extending SubDirective
- New image.html.twig template for standalone images
- JavaScript in image-zoom.js bundled into theme.min.js
- CSS in _component_image_zoom.scss imported into theme.scss
@CybotTM CybotTM force-pushed the feature/figure-zoom-directive branch from 6d82fc2 to 9767659 Compare December 27, 2025 18:31
@CybotTM CybotTM changed the title feat: Add image zoom/lightbox feature with :zoom: directive option feat(figure): add image zoom functionality with multiple modes Dec 27, 2025
@CybotTM
Copy link
Contributor Author

CybotTM commented Dec 27, 2025

Hi @linawolf, IMO it should be ready now for a review.

- Remove dead CSS .zoom-enabled class (never used by JS)
- Add :zoom-factor: option support for figures (was image-only)
- Add zoom value validation (only lightbox|gallery|inline|lens)
- Add image load error handling in gallery mode
- Rebuild assets with fixes
- Add pinch-to-zoom and touch pan for gallery and inline modes
- Add helper functions for pinch gesture detection
- Make zoom limits configurable via data attributes:
  - data-max-zoom: maximum zoom level
  - data-min-zoom: minimum zoom level
  - data-zoom-step: zoom increment per scroll
- Update FigureDirective docblock with accurate documentation
@josefglatz
Copy link
Member

I just tested it as a non-a11y-expert. Really cool to see them accessible.

is it also possibel with actual RsT parser to have a automatic rendered thumbnail/image in the normal view and an original image in the lightbox?

@linawolf
Copy link
Member

linawolf commented Dec 28, 2025

I added an integration test. In my eyes we can remove draft status

@CybotTM CybotTM marked this pull request as ready for review December 28, 2025 18:35
@CybotTM
Copy link
Contributor Author

CybotTM commented Dec 28, 2025

@josefglatz Good question! Currently the zoom modes use the same image source for both the inline view and the lightbox/gallery.

To support separate thumbnail/original images, we'd need to add a new directive option like :zoom-src: that specifies the full-size image to show in the lightbox:

.. figure:: /images/screenshot-thumbnail.png
   :zoom: lightbox
   :zoom-src: /images/screenshot-original.png
   :alt: Screenshot

   Click to see full resolution

This would be a nice enhancement for a follow-up PR. Would you like to create a feature request issue for it?

@CybotTM
Copy link
Contributor Author

CybotTM commented Dec 28, 2025

To clarify - there are two possible approaches:

  1. Manual: :zoom-src: option where the user provides both thumbnail and original (as described above)

  2. Automatic: The renderer generates thumbnails from the original image during build time

The automatic approach would be more user-friendly but requires image processing capabilities in the render pipeline (resizing, caching, output path management). This is likely out of scope for render-guides as it's primarily a documentation renderer, not an image processing tool.

For automatic thumbnails, this might be better handled at the infrastructure level (e.g., via a CDN/image service that serves resized versions on-the-fly) rather than in render-guides itself.

@linawolf what's your take on this?

Extend the zoom integration test to cover:
- All four zoom modes (lightbox, gallery, inline, lens)
- Gallery mode with grouping option
- Lens mode with zoom-factor option
- Image directive with zoom (not just figure)
- zoom-indicator: false option

This provides comprehensive test coverage for the image zoom feature.
@CybotTM
Copy link
Contributor Author

CybotTM commented Dec 28, 2025

oh, yes, tests, sorry, totally forgot about them 🤦
added some more now.

Changes:
- Change FigureDirective to extend BaseDirective instead of SubDirective
  to have full control over option processing
- Add CompilerPass to remove base library's FigureDirective during
  container compilation, ensuring our custom implementation is used
- Validate zoom values and set invalid values to null (not remove)
  so they override raw options during post-processing
- Add explicit $startingRule service binding in DI configuration
- Add integration test for invalid zoom value validation
@linawolf
Copy link
Member

I would suggest that we first get this PR merged and then deal with different images for preview and the zoom box itself in follow ups.

I am not very familiar with how the rendering pipeline actually works so I would be hesistant to change it unless someone really knows what they are doing. New features are nice, but it also means that we have more things we need to maintain and promise for the indefinate future. Also ppl actually using such features are usually quite a small group. So I would prefer a lowtech variant like :zoom-src: /images/screenshot-original.png In such a case we should also link the original picture so it is accessible without javascript

Copy link
Member

@linawolf linawolf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for these changes

@linawolf linawolf merged commit c4a29ab into TYPO3-Documentation:main Jan 18, 2026
8 checks passed
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.

3 participants