Skip to content

Horizontal scroll on mobile devices when picture tag is enabled (Modern Image Formats) #1918

Open

Description

@Seriyyy95

Bug Description

Hello. I have noticed that when I enable “Use picture Element (experimental)“ in the settings, the width of the site container is doubled and horizontal scrolling appears on mobile devices in the articles where this tag is inserted and Gutenberg lightbox is enabled for images. I was able to reproduce this bug not only in the theme I'm using, but also in the standard Wordpress Twenty Twenty Five template in the newly deployed Wordpress.

Steps to reproduce

  1. Enable the Modern Image Formats plugin
  2. Enable "Use picture Element (experimental)" and "Also generate fallback images in the original upload format" in Media settings
  3. Create Gutenberg post and insert an image, with Link setting - Expand on Click
  4. Publish a post on the site and proceed to view it
  5. Enable mobile device mode, such as Pixel 7 in Chrome Developer Tools
  6. The container is twice as wide as the content, there is a scroll bar at the bottom of the screen and when you tap on an image it opens to the full width of the container.

Image

Image

This is how it looks like if the picture tag option is turned off:

Image

Here is the code that is inserted for image with the plugin enabled:

<figure data-wp-context="{&quot;imageId&quot;:&quot;67d010bcd9866&quot;}" data-wp-interactive="core/image" class="wp-block-image size-full wp-lightbox-container">
   <picture class="wp-picture-8" style="display: contents;">
      <source type="image/webp" srcset="http://testwp.local/wp-content/uploads/2025/03/c300dfa0cf518a9a5a9eb80e2c7e2e-jpg.webp 1000w, http://testwp.local/wp-content/uploads/2025/03/c300dfa0cf518a9a5a9eb80e2c7e2e-300x200-jpg.webp 300w, http://testwp.local/wp-content/uploads/2025/03/c300dfa0cf518a9a5a9eb80e2c7e2e-768x511-jpg.webp 768w" sizes="(max-width: 1000px) 100vw, 1000px">
      <img fetchpriority="high" decoding="async" width="1000" height="666" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on-async--click="actions.showLightbox" data-wp-on-async--load="callbacks.setButtonStyles" data-wp-on-async-window--resize="callbacks.setButtonStyles" src="http://testwp.local/wp-content/uploads/2025/03/c300dfa0cf518a9a5a9eb80e2c7e2e.jpg" alt="" class="wp-image-8" srcset="http://testwp.local/wp-content/uploads/2025/03/c300dfa0cf518a9a5a9eb80e2c7e2e.jpg 1000w, http://testwp.local/wp-content/uploads/2025/03/c300dfa0cf518a9a5a9eb80e2c7e2e-300x200.jpg 300w, http://testwp.local/wp-content/uploads/2025/03/c300dfa0cf518a9a5a9eb80e2c7e2e-768x511.jpg 768w" sizes="(max-width: 1000px) 100vw, 1000px">
   </picture>
   <button class="lightbox-trigger" type="button" aria-haspopup="dialog" aria-label="Enlarge image" data-wp-init="callbacks.initTriggerButton" data-wp-on-async--click="actions.showLightbox" data-wp-style--right="state.imageButtonRight" data-wp-style--top="state.imageButtonTop" style="right: -336px; top: -218px;">
      <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
         <path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z"></path>
      </svg>
   </button>
</figure>

And here without it:

<figure data-wp-context="{&quot;imageId&quot;:&quot;67d017ae9421e&quot;}" data-wp-interactive="core/image" class="wp-block-image size-full wp-lightbox-container">
   <img fetchpriority="high" decoding="async" width="1000" height="666" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on-async--click="actions.showLightbox" data-wp-on-async--load="callbacks.setButtonStyles" data-wp-on-async-window--resize="callbacks.setButtonStyles" src="http://testwp.local/wp-content/uploads/2025/03/c300dfa0cf518a9a5a9eb80e2c7e2e-jpg.webp" alt="" class="wp-image-8" srcset="http://testwp.local/wp-content/uploads/2025/03/c300dfa0cf518a9a5a9eb80e2c7e2e-jpg.webp 1000w, http://testwp.local/wp-content/uploads/2025/03/c300dfa0cf518a9a5a9eb80e2c7e2e-300x200-jpg.webp 300w, http://testwp.local/wp-content/uploads/2025/03/c300dfa0cf518a9a5a9eb80e2c7e2e-768x511-jpg.webp 768w" sizes="(max-width: 1000px) 100vw, 1000px">
   <button class="lightbox-trigger" type="button" aria-haspopup="dialog" aria-label="Enlarge image" data-wp-init="callbacks.initTriggerButton" data-wp-on-async--click="actions.showLightbox" data-wp-style--right="state.imageButtonRight" data-wp-style--top="state.imageButtonTop" style="right: 16px; top: 16px;">
      <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
         <path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z"></path>
      </svg>
   </button>
</figure>

Additional Context

  • OS: Android 13
  • PHP Version: 8.3
  • Browser: Chrome 133, 134
  • Performance Lab Version: 3.9.0
  • Modern Image Formats Version: 2.5.0
  • Device: OnePlus 8 Pro, Pixel 7 (Chrome Dev Tools)

Activity

  1. moved this to Not Started/Backlog 📆 in WP Performance Ongoingon Mar 11, 2025
  2. westonruter commented on Mar 11, 2025

    @westonruter
    Member

    Related: #1805

  3. sanketio commented on Aug 27, 2026

    @sanketio

    The <picture> wrapper is the cause. Core's setButtonStyles reads ref.parentElement.clientWidth/clientHeight (view.js#L621-L628), but with Modern Image Formats the img's parent is <picture style="display: contents">, which generates no box — so both read 0 and the button gets right: -offsetWidth + 16, placing it outside the figure.

    Reproduced on WP 7.1 / Twenty Twenty-Five at a 500px viewport: button right: -409px, scrollWidth 864 vs clientWidth 485. With the picture element off: right: 16px, no overflow.

    Two possible fixes:

    1. Gutenberg: use ref.closest( 'figure' ) instead of ref.parentElement. Fixes any plugin that wraps block images, but needs a core release.
    2. Modern Image Formats: skip the <picture> wrapper when the <img> carries data-wp-on--click="actions.showLightbox", rewriting src/srcset instead so the image still gets the modern format.

    Same root cause as #1805.

    Disclosure: analysis and reproduction were done with AI tooling (Claude Code), reviewed and verified by me.

  4. adamsilverstein commented on Aug 27, 2026

    @adamsilverstein
    Member

    wo possible fixes:

    1. Gutenberg: use ref.closest( 'figure' ) instead of ref.parentElement. Fixes any plugin that wraps block images, but needs a core release.

    This sounds like the most direct fix, making the Gutenberg styling more specific. I could also see it breaking some things.

    1. Modern Image Formats: skip the <picture> wrapper when the <img> carries data-wp-on--click="actions.showLightbox", rewriting src/srcset instead so the image still gets the modern format.

    Don't we need the picture element to get the browser fallback behavior?

  5. sanketio commented on Aug 31, 2026

    @sanketio

    You're right on the fallback — the non-picture path rewrites src/srcset in place with no <source> siblings, so lightbox images would lose the fallback. Withdrawing that option.

    On breakage: the button is position: absolute inside .wp-lightbox-container, which is position: relative, so the figure is already the containing block the math assumes — parentElement is only standing in for it. ref.closest( '.wp-lightbox-container' ) names it directly, is guaranteed present since block_core_image_render_lightbox() adds the class, and resolves to the same node as today for unwrapped images.

    Separate from #70800, which fixes the hover selector but not the position.

  6. adamsilverstein commented on Aug 31, 2026

    @adamsilverstein
    Member

    Gutenberg: use ref.closest( 'figure' ) instead of ref.parentElement. Fixes any plugin that wraps block images, but needs a core release.

    @sanketio can you open a follow up issue on the Gutenberg repository proposing this change? This would be the ideal fix and proposing it there will provide an opportunity to receive feedback about the idea.

  7. sanketio commented on Sep 1, 2026

    @sanketio
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions