Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set image width/height attributes in HTML #6783

Open
wants to merge 25 commits into
base: v2
Choose a base branch
from

Conversation

thewilkybarkid
Copy link
Contributor

@thewilkybarkid thewilkybarkid commented Aug 27, 2021

↪️ Pull Request

This sets the width and height attributes on HTML <img> elements to help layout in browsers (refs #3738 (comment)).

I had been using posthtml-img-autosize (with the processEmptySize option), but that's incompatible with things like the image transformer (since it runs before transformations).

💻 Examples

See the tests.

🚨 Test instructions

Tests included.

✔️ PR Todo

  • Added/updated unit tests for this change
  • Filled out test instructions (In case there aren't any unit tests)
  • Included links to related issues/PRs

@height
Copy link

height bot commented Aug 27, 2021

Link Height tasks by mentioning a task ID in the pull request title or description, commit messages, or comments.

💡Tip: You can also use "Close T-X" to automatically close a task when the pull request is merged.

@thewilkybarkid
Copy link
Contributor Author

thewilkybarkid commented Aug 27, 2021

The potential layout problem is if someone has something like img { width: 100% } without height: auto (since the image will stretch). This is combination 3 on WICG/intrinsicsize-attribute#16 (comment).

Or are using the width/height to control the size (refs #6783 (comment) and #3738 (comment)).

Feels like the benefits far outweigh this edge case though.

Comment on lines +8 to +9
<img id="image5" src="200x200.png" width="100">
<img id="image6" src="200x200.png" height="100">
Copy link
Contributor Author

Choose a reason for hiding this comment

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

As it stands, these are both fixed to be the real size of the image.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Would be changed by #7390

@thewilkybarkid
Copy link
Contributor Author

thewilkybarkid commented Sep 4, 2021

I'm planning on experimenting with injecting a little CSS to avoid misshapen images (which is hopefully tree-shaken away in most cases). (Too much for now.)

@thewilkybarkid thewilkybarkid marked this pull request as ready for review September 5, 2021 20:08
}

node.attrs.width = image.meta.width;
node.attrs.height = image.meta.height;
Copy link
Member

Choose a reason for hiding this comment

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

I think we should only do this if a width or height attribute is not already set on the element?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we should only do this if a width or height attribute is not already set on the element?

Probably. I left this in for the moment (#6783 (comment)) for feedback (primarily about the CSS issue), but in the longer term it would be better for these to be "fixed" earlier in the pipeline (refs #3738 (comment)).

@mischnic
Copy link
Member

We never want to make Parcel "break" your code after bundling, so if it worked correctly before bundling, it should still behave the same afterwards. That is unfortunately not always the case here:

https://codesandbox.io/s/serene-ives-legx7?file=/index.html

So I'm torn between automatically adding them to avoid layout shift and not randomly breaking the projects of unknowing users (especially because this is hard to debug if you don't know what you're looking for. In this case, you need to set the other "free" dimension to auto instead of initial to retain the aspect ratio).

@thewilkybarkid
Copy link
Contributor Author

So I'm torn between automatically adding them to avoid layout shift and not randomly breaking the projects of unknowing users (especially because this is hard to debug if you don't know what you're looking for. In this case, you need to set the other "free" dimension to auto instead of initial to retain the aspect ratio).

I do wonder about this too; the packager doesn't feel the right place to do it but I'm not sure where is (the HTML transformer's too early?).

I did wonder about injecting some CSS (#6783 (comment)), but that also could be thorny (and I'm not sure how and where it should happen: inline and before any CSS in all HTML files that contain images?).

@thewilkybarkid
Copy link
Contributor Author

If it helps, this could be split in two: the first part that adds the width/height as metadata on image assets, then a follow-up that uses it to set the width/height in HTML. Since the latter has potential CSS problems, I could release this as a separate plugin. (The former could also be a separate plugin, but doesn't seem unreasonable to make that information generally available for image assets?)

the packager doesn't feel the right place to do it but I'm not sure where is (the HTML transformer's too early?).

Looking at the plugin system again, would it be an optimizer? (It's not 'optimization' as such, but needs to be a late-running transformation.)

@thewilkybarkid
Copy link
Contributor Author

Looking at the plugin system again, would it be an optimizer? (It's not 'optimization' as such, but needs to be a late-running transformation.)

I had a go at writing an optimizer and managed to get it to work, it just seems a bit tricky to find the asset from the src (which is now the final path, e.g. /some-image.ab12345.jpg).

First attempt looks like:

tree.match({tag: 'img', attrs: {src: true}}, node => {
  let image;

  for (const dependency of dependencies) {
    const asset = bundleGraph.getResolvedAsset(dependency, bundle)

    if (!asset) {
      continue;
    }

    const bundles = bundleGraph.getBundlesWithAsset(asset);

    if (node.attrs['src'] !== bundles[0].name) {
      continue;
    }

    image = asset;
    break;
  }

  // use imageSize on image and update attributes on node

  return node;
});

which works, but is rather ugly. Is there a simple way that I've missed?

Does either of these approaches make sense @devongovett @mischnic? I've happy to update this PR or close it and release something standalone.

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