astro icon indicating copy to clipboard operation
astro copied to clipboard

🐛 BUG: `<Picture />` component passes props as attributes to the underlying `picture` element

Open carlcs opened this issue 1 year ago • 11 comments

What version of astro are you using?

1.0.0-beta.65

Are you using an SSR adapter? If so, which one?

none

What package manager are you using?

yarn

What operating system are you using?

Mac

Describe the Bug

When passing props such as class or alt to the <Picture /> component, they are added as attributes to the underlying picture element. It would probably be better if they ended up being added to the child img element.

Issue with the current behaviour that I ran into (see repro):

  • Lighthouse lists a missing alt attribute. It needs to be added to the img element.
  • CSS property object-fit does nothing on the picture element, it works when added to img.

Other image components make it possible to add attributes to the img element via a separate prop.

<!-- Astro Imagetools -->
<Picture attributes={{ class: "my-class" }} />

<!-- Nuxt Image -->
<nuxt-picture :imgAttrs="{ class: 'my-class' }" />

But I don’t think that this is really necessary, and we should just pass all props to the img instead. In my experience I have yet come across a use case where I needed to add attributes to the picture element. If a container is required for styling purposes you can always wrap it in a div and add your styles there. If anything I think we should rather do it the other way around and support passing attributes to the picture element via a dedicated pictureAttrs prop.

All examples on MDN or in HTML specs that I came across use picture attribute-less, as a plain container to wrap the img and its additional sources.

They have some notes to prevent usage mistakes:

The picture element itself does not display anything; it merely provides a context for its contained img element that enables it to choose from multiple URLs — https://html.spec.whatwg.org/multipage/embedded-content.html#the-picture-element

Use these properties on the child img element, not the picture element — https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture#usage_notes

Link to Minimal Reproducible Example

https://codesandbox.io/s/nifty-wiles-cn6p8w?file=/src/pages/index.astro

Participation

  • [ ] I am willing to submit a pull request for this issue.

carlcs avatar Jul 13 '22 11:07 carlcs

This suggestion makes a lot of sense, I'll leave this one in @tony-sull's capable hands!

natemoo-re avatar Jul 14 '22 21:07 natemoo-re

💯 those props really should be passed down to the img rather than the picture, thanks for catching that oversight!

tony-sull avatar Jul 18 '22 18:07 tony-sull

Hello, is there a suggested workaround for the "object-fit" case?

rndexe avatar Jul 19 '22 18:07 rndexe

First of all, thanks @tony-sull and @natemoo-re for considering this change. The PR looks great, I’m really looking forward to the merge!

@rndexe I’m pretty sure once this is implemented you can do something like this:

<div class="banner">
  <Picture
    class="banner-img"
    src={imageUrl}
    widths={[530, 1060]}
    aspectRatio={1060 / 1081}
    sizes="100vw"
  />
</div>

<style>
  .banner {
    position: relative;
    height: 200px;
  }
  .banner-img {
    position: absolute;
    top: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
</style>

carlcs avatar Jul 19 '22 19:07 carlcs

I think the PR only covers the alt attribute for accessibility concerns and reverted the change for other attributes to existing behaviour.

rndexe avatar Jul 19 '22 19:07 rndexe

Oh, I didn’t notice that this was reverted. That is sad.

carlcs avatar Jul 19 '22 19:07 carlcs

Does the picture element even support styling? I was trying create a scaling effect on hover and it isn't working in the current scenario

rndexe avatar Jul 19 '22 19:07 rndexe

At least for image specific styles (e.g. object-fit) you’ve got to apply them on the img element. So if you are relying on those you’ve got to use the <Image /> component currently, I guess.

@tony-sull can we please re-open the issue?

carlcs avatar Jul 19 '22 19:07 carlcs

Sorry, this issue should not have been marked as completed! This is still something we want to support, we just had to bump down the priority a bit since we have more pressing things to tackle.

If anyone wants to submit a PR to cover this use case in a more generic way, contributions are welcome!

natemoo-re avatar Jul 20 '22 02:07 natemoo-re

@rndexe I was thinking too much in the Tailwind way of applying classes directly to the elements. Which in case of the child img element isn’t possible right now when using the <Picture /> component. But you can for sure target the nested img element with CSS selectors like so:

<div class="banner">
  <Picture
    class="banner-img"
    src={imageUrl}
    widths={[530, 1060]}
    aspectRatio={1060 / 1081}
    sizes="100vw"
  />
</div>

<style>
  .banner {
    position: relative;
    height: 200px;
  }
  .banner-img > img {
    position: absolute;
    top: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
</style>

carlcs avatar Jul 20 '22 07:07 carlcs

For some reason even this doesn't work, maybe I should start a support thread on discord instead of polluting this issue's comment thread.

Edit: Found this workaround in the bugbash today

main > :global(picture > img) {
    width: 50%;
    height: auto;
    margin: auto;
    aspect-ratio: 4 / 3;
    object-fit: cover;
}

it needs global somehow

rndexe avatar Jul 20 '22 15:07 rndexe