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

Feature: Responsive media component #45

Draft
wants to merge 17 commits into
base: main
Choose a base branch
from

Conversation

peterschewe
Copy link
Collaborator

@peterschewe peterschewe commented Sep 21, 2022

This PR merges all media components (c-image, c-video, c-media) into a new component c-responsive-media.

Todos

  • Remove c-image and add as recipe instead
  • Remove c-video and add as recipe instead (new name: c-video-player)
  • core-utils/video: Load the video lazy as default and remove image preloading
  • core-utils/image: Set width and height at <source> instead of at <img>. This change is important if different ratios are defined via sources. The img itself must not have a default size.
  • Add new component c-responsive-media
    • Template
    • Migration
    • Test and finalise cropping with ratio and focus area
    • Caption handling (see TBD)
  • Allow include of local img/video assets in Storybook from separate static dir and add demo files
  • Add Storybook examples
  • Generate poster images with cssg
  • Add transparent data image as poster attribute

TBD

  • Image quality: Do we need type specific (JPEG, PNG, AVIF, WebP) quality settings? The Default for all is 80 I think. Here (https://www.industrialempathy.com/posts/avif-webp-quality-settings/) we find good presets for AVIF and WebP.
  • Core Utils: Are the adjustments to the core utils (see above) breaking changes?
  • Caption: Where do we like to use a caption? c-responsive-media vs. m-media
  • Video:
    • Do we need a poster attribute at the video element? I'm currently using a transparent data image.
    • We don't set a playsinline attribute in the video util yet. I'm not sure if it would cause problems on iOS.
    • We already use ffmpeg in cssg. The step to automatically generate smaller videos and in other formats (e.g. webm) would be obvious. However, it would slow down the build time. But maybe it could run as a separate task – e.g. when creating new assets, trigger a webhook directly, which provides the new files via Netlify function and sends them back to Contentful.

New component: Responsive media

The component simplifies the handling of images and short loop videos, which can be used in the hero or media module, for example.

The name (c-responsive-media instead of c-media) is also a clearer distinction from the module m-media.

Features

  • Can display different media for each breakpoint, e.g. an image on mobile and a video on desktop.
  • Ratios and focus areas can be defined in Contentful. Images are cropped accordingly via Contentful's Image API. Video files remain in the original and are then masked in the frontend.
  • (wip) Video posters are generated automatically via cssg, incl. different sizes and types.

Contentful

image

Markup (simplified)

<!-- Example 1: Mobile image; desktop video; different ratios; image without lazy loading (for use above the fold) -->
<figure>
  <picture>
    <source srcset="poster-desktop.jpg" media="(min-width: 768px)" width="1920" height="1080">
    <source srcset="image-mobile.jpg" media="(max-width: 767px)" width="768" height="768">
    <img src="image-mobile.jpg" alt="Lorem ipsum" decoding="auto" fetchpriority="high">
  </picture>
  <video class="-md:u-hidden" poster="transparent data/base64 image">
    <source data-src="video-desktop.mp4" type="video/mp4">
  </video>
</figure> 

<!-- Example 2: Mobile video; desktop video; different sizes; image with lazy loading -->
<figure>
  <picture>
    <source srcset="poster-desktop.jpg" media="(min-width: 768px)" width="1920" height="1080">
    <source srcset="poster-mobile.jpg" media="(max-width: 767px)" width="768" height="432">
    <img src="poster-mobile.jpg" alt="Lorem ipsum" decoding="auto" loading="lazy">
  </picture>
  <video class="-md:u-hidden" poster="transparent data/base64 image">
    <source data-src="video-desktop.mp4" type="video/mp4">
  </video>
  <video class="md:u-hidden" poster="transparent data/base64 image">
    <source data-src="video-mobile.mp4" type="video/mp4">
  </video>
</figure> 

<!-- Example 3: Mobile image; desktop image; different ratios; image with lazy loading -->
<figure>
  <picture>
    <source srcset="image-desktop.jpg" media="(min-width: 768px)" width="1920" height="1080">
    <source srcset="image-mobile.jpg" media="(max-width: 767px)" width="768" height="768">
    <img src="image-mobile.jpg" alt="Lorem ipsum" decoding="auto" loading="lazy">
  </picture>
</figure> 

@peterschewe peterschewe changed the title Feature/refactor media components Feature: Responsive media component Sep 21, 2022
Copy link
Member

@bezoerb bezoerb left a comment

Choose a reason for hiding this comment

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

Looks good already :)
Nice work @peterschewe

@@ -51,7 +51,7 @@
{{- $width := .width -}}
{{- $height := .height -}}
{{/* Preload hint is added in sources.html */}}
<img src="{{- $src -}}" width="{{- $width -}}" height="{{- $height -}}" {{ $image_attributes }} alt="{{- $alt -}}"{{ with $params.preload }} fetchpriority="high"{{ end }}>
Copy link
Member

Choose a reason for hiding this comment

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

why is this removed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I thought we needed it because with different ratios the sizes come from the <source> elements. Maybe it is enough there. But I just noticed another problem, there is a layout shift when the sources have a media attribute. I am looking for a solution.

@@ -26,9 +26,10 @@
{{- $autoplay := $params.autoplay | default true -}}
{{- $playsinline := $params.playsinline | default true -}}
{{- $controls := $params.controls | default false -}}
{{- $poster := $params.poster | default false -}}
{{/* Use transparent base64, the real poster should be loaded as <picture> element */}}
{{- $poster := "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" -}}
Copy link
Member

Choose a reason for hiding this comment

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

why not skip the poster attribute completely?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good question. I asked myself the same question (see TBD). The poster is not a mandatory attribute, but there was some reason... maybe the background is black when the video is not yet loaded? I don't know. I will try to find out and take it out for now.


// Observe the visiblity of the video element
const observer = new IntersectionObserver(observerCallback, { threshold: [0, 0.01] });
observer.observe(video);
};

export const initVideo = (root = document) => {
Copy link
Member

Choose a reason for hiding this comment

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

We should call init automatically without the need to be imported & called from main.js

Copy link
Member

@dlemm dlemm left a comment

Choose a reason for hiding this comment

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

Good job @peterschewe
I've found only small things, and more like suggestions.

.type('Symbol')
.localized(false)
.required(false)
.validations([
{
in: ['hd', 'rectangle', 'square', 'portrait'],
in: ['1x1', '16x9', '9x16', '4x3', '3x4'],
Copy link
Member

Choose a reason for hiding this comment

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

Definitely the better naming and more obviously 👍

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think so too. 👍 Unfortunately, there is still a bug in Sonar that I don't exactly understand yet. I'll have a look. https://sonarcloud.io/project/issues?resolved=false&types=BUG&pullRequest=45&id=jungvonmatt_create-contentful-hugo-app&open=AYNg2o7ptY5uRUoaXYTE

Copy link
Member

Choose a reason for hiding this comment

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

Sounds like sonar thinks that this is a mathematical expression and you just could write 1, I guess..

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks @dlemm, that solved it.

{{- $loop := $params.loop -}}
{{- $poster_asset := $params.poster -}}
{{- $controls := $params.controls -}}
{{- $muted := $params.muted -}}
Copy link
Member

Choose a reason for hiding this comment

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

Do we might need setting a default here? For example true for muted and autoplay or at least for muted since in Safari the autoplay set in Contentful would not work if the editor forgets the muted? Or would this to much control for the markup?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

You are right. I think there are more things we would have to rebuild. It's just a 1 to 1 export from the old c-video (just renamed). However, it also has slightly different requirements now. I might have to create a separate issue ticket for that first.

img,
svg {
vertical-align: top;
// max-width: 100%;
Copy link
Member

Choose a reason for hiding this comment

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

Can this comment be deleted?

@dlemm
Copy link
Member

dlemm commented Sep 22, 2022

For the TBD

Caption: Where do we like to use a caption? c-responsive-media vs. m-media

In my opinion it should be placed inside the c-responsive-media since the caption barely changes. So if you use the component somewhere else, would there be another caption?

And another suggestion is that we might need a desktop and a mobile caption since it is possible to maintain two different things now.

@peterschewe
Copy link
Collaborator Author

For the TBD

Caption: Where do we like to use a caption? c-responsive-media vs. m-media

In my opinion it should be placed inside the c-responsive-media since the caption barely changes. So if you use the component somewhere else, would there be another caption?

Yes, that is a good point. I can only see a disadvantage if the caption is not directly aligned with the media. For example, the media could be fullscreen and the caption could remain in the content grid. This is also possible in the component. But maybe it's more difficult if it's already in a grid (of the module). The case can probably be ignored...

And another suggestion is that we might need a desktop and a mobile caption since it is possible to maintain two different things now.

I would consider the responsive media as a unit. It is meant to be displayed differently, but the purpose of the content is the same. We also use only one alt text for both media.

@sonarqubecloud
Copy link

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 2 Code Smells

No Coverage information No Coverage information
10.8% 10.8% Duplication

@sonarqubecloud
Copy link

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 2 Code Smells

No Coverage information No Coverage information
10.8% 10.8% Duplication

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