Problem/Motivation
Working on #3346765: Add PerformanceTestBase for allowing browser performance assertions within FunctionalJavaScriptTests I noticed that Largest Contentful Paint is well over two seconds.
Checking Lighthouse shows an image in the hero content being lazy loaded, additionally, inspecting that element, the image is hidden by CSS anyway via overflow: none; and some sizing, but not actually display none. So the hero content view mode could potentially not have that image configured to display at all, saving an HTTP request. A hacky fix for this would be to set the image/field to display:none; so it doesn't render at all and then doesn't affect LCP.
However, the reason this is done is for this template logic:
themes/umami/templates/components/banner-block/block--bundle--banner-block.html.twig:{% set background_image = file_url(content.field_media_image[0]['#media'].field_media_image.entity.uri.value) %}
themes/umami/templates/components/banner-block/block--bundle--banner-block.html.twig:<div{{ attributes.addClass(classes) }} style="background-image: url({{ background_image }})">
Proposed resolution
- Display the image from the
<img>tag instead of rendering a background image. The background image was coming in at full width (not being processed by an image style) at 3000px wide. - Utilize responsive image styles.
- Utilize WebP image format.
- Remove lazy-loading on the image.
Testing steps
There should be no visual difference. To test
- Ensure image displays properly at multiple screen widths (small, medium, large)
- Ensure image displays properly both when authenticated and anonymous
| Comment | File | Size | Author |
|---|---|---|---|
| #42 | Screenshot from 2023-07-15 17-25-35.png | 157.5 KB | catch |
| #40 | updated.png | 137.61 KB | mherchel |
| #40 | baseline.png | 142.07 KB | mherchel |
| #38 | Screenshot from 2023-07-15 11-52-31.png | 42.68 KB | catch |
| #38 | Screenshot from 2023-07-15 11-52-31.png | 42.68 KB | catch |
Comments
Comment #2
mherchelI love the focus on performance.
TBH, we should try not to use
background-imageanymore because we don't support IE.A better way to do it would be to output the image as normal (via an
<img>tag), absolute position it against the container, and then useobject-fit: cover(see https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit).This would have a lot of benefits:
While we're at it, we could also update the image styles to use
webpfor the image format. We also need to make sure that when loading the image (like normal) the image is not lazy-loaded.Comment #3
mherchelHere's a patch showing the method.
Notes:
Comment #4
mherchelComment #5
mherchelOops... the absolutely positioned image wasn't properly anchored when not logged in. New patch fixes this as well as removes some unneeded CSS rules.
Comment #6
catchComment #7
catchCould we add a new media view mode instead of using the thumbnail formatter? I think we should add #2947796: Responsive image format for media to core and it could use that eventually, but there's disagreement on whether the thumbnail formatter should be used for anything other than admin-facing thumbnails. That'd give us both responsive images and loading=eager.
Comment #8
catchWe should do that in #3275557: Add webp image conversion to core's install profile's image style rather than here, but yes :)
Comment #9
mherchelSure thing. Note that this doesn't work out of the box because the media template inserts a wrapping element that gets
position: relative;when authenticated. I'll have to remove that wrapping element by overriding the template.I know this is an aside, but this blows my mind a bit. The thumbnail formatter is the primary formatter that I use, because it's dramatically easier to set up, less config, less markup, yada, yada.
I'll likely update the patch in a day or two!
Comment #11
andypostImages can be converted to WebP it allows to cut few tens kilobytes, see #3275557-10: Add webp image conversion to core's install profile's image style
Comment #12
catchI've been using this as a test case for a an APM dashboard for core, and just got to a point where it's able to show the issue, see #3352459: Add OpenTelemetry Application Performance Monitoring to core performance tests.
Comment #13
mherchelThanks for bumping this to the top @catch (its a fun thing on a rainy Florida afternoon 😀)
New patch attached. This does several things:
Comment #14
mherchelForgot to remove the UUID keys from the config. New patch attached.
I didn't create any interdiffs in #13 (sorry) because lots of changes and necessitates a full review.
Comment #15
mherchelComment #16
mherchelFixing an issue where image wasn't positioned against container when anonymous, and also cleaning up some newly unneeded CSS rules.
Comment #17
mherchelUpdating issue summary
Comment #18
markconroy commentedI really like the general direction of this, but I think we've messed up the CSS a bit (testing on 10.1, as I think it's okay for Umami to bring in new changes in point releases).
Small screen - looks fine
Medium Screen - image becomes a big square taking over the full screen, not the "Read article" link in bottom left, as it's covering the cards that come after it
Large Screen - see how the image is covering the cards that follow after this component.
I'll try fix this up now, but then I think I'll propose that we remove the new 7:3 responsive image style and create one called "Hero", so we can have a portrait image for small screens, some other ratio for tablets, and 7:3 for large screens which I think will be more in tune with the designs.
Comment #19
markconroy commentedHere's my suggestion
As I said in the previous message, I'd prefer us to _not_ use 7:3 aspect ratio for all the image styles in this component and would rather if we had different dimensions/ratios for different view ports.
Comment #20
markconroy commentedNew patch after running the CSS linter. Interdiff is against #15.
Comment #21
mherchelAfter applying the patch in #20 and running though installation, I get this error:
Comment #22
mherchel@markconroy - looking through your comment, I think you tested the wrong patch (I think you tested #15). The patch in #16 should resolve those.
I tested #16 again, and verified it worked (on my local 🙃). Can you check again?
Comment #23
heddnwhere are the 2x and 3x multipliers?
Comment #24
markconroy commented@mherchel Darn, looks like you are right, not sure how I missed #16
In any case, I think I prefer the HTML and the CSS of my #20. How about we take your config and my template and CSS?
Comment #25
mherchelNot sure they're needed. When adding those, they tend to overcomplicate things IMO. I think the regular 1x image looks fine on my iPad FWIW.
Sounds good. I'll work on this later today or tonight and throw in another patch.
Comment #26
heddnThis is supposed to also be a training opportunity too. Are we suggesting that adding 2x,3x image styles aren't needed in general and site owners should focus their efforts on other things instead to improve perceived performance?
Comment #27
markconroy commented@heddn for the current issue, let's not expand the scope of it too much. It might be a good idea, however, to open a new issue to have 2x and 3x multipliers added for Umami images.
Comment #28
markconroy commentedNew patch incorporating #16 and my updates from #20 (with some amendments).
Comment #29
catchfwiw 2x/3x images as a follow-up seems like a good plan, that's more of a design question than about loading performance (except that it allows the 1x resolution to be lower I guess).
Comment #30
mherchel#28 is 99.99999% there!
New patch fixes a couple things:
position: relative;to the container.Up for another review 😀
Comment #31
mherchelMinor indentation fix.
Comment #32
heddnMostly questions here. This is looking really great.
Almost never will the fallback be used with the state of modern browsers. The cases where it will are for older mobile devices and desktops using older browers, i.e. etc.
I wonder if we should be rendering a 1400px wide image in those cases. In my mind, these older devices go hand in glove with smaller displays. Is the small or medium a better fallback?
I'm never really sure what to do in this case, so your guys input would be valuable here.
Some comments on why this special template is needed could help someone as it isn't readily apparent why we need it.
Probably a bit of a nit, but should these weights be 0,1,2,3?
Do we need a max width? It feels like if someone had a super large display, they would fall back to the tiny breakpoint. Which doesn't seem right.
Comment #33
mherchelAgree. Right now the field is required though.
That makes sense to me.
Comment is there. Right now it's
I followed the current pattern, but I'll take another look.
You are 1000% correct. Good catch!
Comment #34
mherchelNew patch attached!
Comment #35
heddnI did a manual test of with/without the patch. Results will vary, but I found with the patch we dropped to 1.9seconds for LCP and without it was 3.5seconds.
I'm good with RTBC and we can improve other parts of the page in follow-ups.
Comment #36
mherchel🙌 🙌 🙌 That's like 50% decrease!
Comment #37
heddnThere was some points from lighthouse about sizing and format of the images. We can do more to improve the performance. But I suggest we land as-is rather then let perfect be the enemy of good enough.
Comment #38
catchTrying to use this as a test case for the performance monitoring dashboard and it's seeing a change, but opposite to the one lighthouse and common sense would indicate. This is probably to do with the specifics of the test (browser and Drupal caches etc.) rather than a problem with the patch, so shouldn't hold up commit and I can always -p1 -R the patch for more testing once it's in.
Will update here if I manage to figure out why it's doing it, but otherwise just enjoy the pretty picture and ignore what it actually says.
Comment #39
mherchelNote that the initial load of the image takes longer because the image style needs to generate and convert (and then it has to do it for all of the viewport sizes because of responsive images). Make sure you warm the caches and at all screen sizes.
In the meantime, I'm writing an article about this, and will be doing some testing.
Comment #40
mherchelHere are my results from throwing up two temp dev sites up on Pantheon, and testing them (at mobile widths, 4G) with WPT
Baseline: LCP 2.443S
With Patch: LCP 1.379S
Comment #41
mherchelOne thing I'm noticing here is that the background image (without the patch) is loading the full image without image styles. It's highly optimized and has a smaller filesize than the same-sized WebP version:
Either way, we're making progress.
Comment #42
catchI altered the test so that there's a front page request before a cache clear, then a request to different page, then the front page again - that gives us 'lukewarm' caches with the image derivatives on disk, plugin and route discovery etc, but without asset aggregates, the page cache, entity render caches etc.
Still a visible change and in the right direction this time :) 1200ms down to about 1000ms
Test diff is here: https://git.drupalcode.org/project/drupal/-/merge_requests/4226/diffs?co...
You can see from the graph that the largest contentful paint (green dots) merges into the first contentful paint (blue dots) with the patch applied, the yellow dots are time to first byte. I double checked the LCP size is the same between both versions, which means that in the patched version the first contentful paint is also the largest contentful paint now which is great. Doesn't mean there's nothing to optimize overall, but does mean there's nothing to optimize between FCP and LCP any more, just the one big paint instead of two.
Comment #43
catchThe webp vs. jpeg issue is unfortunate, but seems quite specific to having a highly-optimized jpeg so I think we should live with it. Unless I guess we wanted to configure the original image for that viewport size? But then even with Umami can we guarantee every image is similarly optimized?
2x and 3x we should definitely do in a follow-up and not here.
For me this is ready, but I am not really qualified to review the CSS. There is less going on it it than there is in HEAD though which is a good sign.
Comment #44
andy-blumI am qualified to review the CSS 😁
Everything here looks fine. The nits below are completely optional.
"top" and "left" could be replaced with a single "inset:0;". This reduces the CSS by a negligible amount and adheres to the best practices of logical properties
This is a super pedantic nit, but these rules don't actually create a new stacking context, they just position the content properly (additional info)
Comment #45
mherchelI'm good with committing as is. The
insetpoint is valid, but tbh there are many places where we're doing top and left, and I think it's okay.Note sure you're right here. According to https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_positioned_layout/U...,
Either way, we can discuss this is Slack.
Comment #46
markconroy commentedLet's leave the top and left CSS as is. If we use inset, then it's in effect saying "top: 0; right: 0; bottom: 0; left: 0" and in that case we should also remove the width and height, since inset will have taken care of it.
Let's not hold this issue up any more, looks like enough maintainers and committers are happy with it as is. We can create a follow up to rewrite our CSS in more modern, logical property format.
Comment #48
catchOK that's the sort of CSS review I was hoping for :) Let's open a follow-up for the more concise properties, but agreed that's fine in a follow-up, this is already improving a lot of things in one go.
Tagging for 10.2.0 highlights, it's not a 'feature' but it's an improvement to a feature (the Umami demo).
Comment #49
tstoecklerFixing tag.
Comment #51
slashrsm commented