If we have a full screen height hero image we need to take into account the menu header and also the admin bar when logged in. Using the mobile chin code we can use a custom property to set the height.
A full screen desktop image would need dimensions 2560 x 1440 (27-inch iMac), but can be slightly less height due to browser chrome and the website header. Let's say 2560 x 1400 px.
Based on screensizemap.com and yesviz.com/viewport the portrait image gets shown on screens sizes up to 800 pixels wide. The height to width ratio for mobile devices is commonly between 1.78 and 2.16 (iPhones), while tablets can be as low as 1.3 (e.g. iPad Mini 768 x 1024 px). The 828px width breakpoint (used by Next.js) coincides with the iPhone 11 which has a device resolution of 828 x 1792. Therefore the size is recommended to be a minumum of 828 x 1600 px (taking into account there will normally be some header chrome and we round to an even number). This means the image will be big enough to be double density on the most common mobile devices, while the size of the image does not need to be ridiculously big (especially in terms of height).
When using <picture>
it is best to wrap this in a <div>
rather than using it is a top level grid cell as this can cause problems with height.
<div class="hero">
<?php
$hero = get_field('hero_image');
$width = $hero['sizes'][ 'full_2560-width' ];
$height = $hero['sizes'][ 'full_2560-height' ];
$hero_mobile = get_field('hero_image_mobile');
$mobile_width = $hero_mobile['sizes'][ 'full_828-width' ];
$mobile_height = $hero_mobile['sizes'][ 'full_828-height' ];
?>
<div class="col-start-1 row-start-1">
<picture>
<!-- Portrait -->
<source
media="(max-width: 828px)"
srcset="
<?php echo $hero_mobile['sizes']['full_640']; ?> 640w,
<?php echo $hero_mobile['sizes']['full_750']; ?> 750w,
<?php echo $hero_mobile['sizes']['full_828']; ?> 828w
"
sizes="100vw"
width="<?php echo $mobile_width; ?>"
height="<?php echo $mobile_height; ?>"
/>
<!-- Landscape -->
<img
src="<?php echo $hero['sizes']['full_2560']; ?>"
srcset="
<?php echo $hero['sizes']['full_640']; ?> 640w,
<?php echo $hero['sizes']['full_750']; ?> 750w,
<?php echo $hero['sizes']['full_828']; ?> 828w,
<?php echo $hero['sizes']['full_1080']; ?> 1080w,
<?php echo $hero['sizes']['full_1200']; ?> 1200w,
<?php echo $hero['sizes']['full_1440']; ?> 1440w,
<?php echo $hero['sizes']['full_1920']; ?> 1920w,
<?php echo $hero['sizes']['full_2048']; ?> 2048w,
<?php echo $hero['sizes']['full_2560']; ?> 2560w
"
sizes="100vw"
alt="<?php echo $hero['alt']; ?>"
class="object-cover w-full h-full"
width="<?php echo $width; ?>"
height="<?php echo $height; ?>"
fetchpriority="high"
/>
</picture>
</div>
/* Align bottom left */
<div class="flex flex-col justify-end col-start-1 row-start-1 p-7">
<h1 class="text-white heading1">
<?php the_field('page_heading'); ?>
</h1>
</div>
</div>
The height of the hero is calculated, taking into account the admin bar when appicable.
.hero {
@apply grid grid-cols-1 grid-rows-1;
--hero-height: calc( 100svh - var(--header-height) );
height: calc( var(--hero-height) - var(--wp-admin--admin-bar--height, 0px) );
min-height: 420px;
}
Note: min-height
is needed for unusual screens that are wide but not heigh. Ideally the following would work but the background image appears to extend the height beyond 100vh.
min-height: calc(var(--hero-height) - var(--wp-admin--admin-bar--height, 0px));
height: auto;
fetchpriority
Use fetchpriority=”high”
to load the LCP hero image sooner.
https://addyosmani.com/blog/fetch-priority/
This is currently available on Chrome and Edge. Not on Safari or Firefox. It is suggested that fetchpriority
be used rather than <link rel=preload>
. https://web.dev/fetch-priority/
Hero Animation
Add id="hero"
to the main hero image.
Set the class on the image: transition-opacity ease-out opacity-0
.
Add the script to handle image loading and add hero image fadein
<script>
const heroImg = document.getElementById("hero");
function handleLoading(heroImg, onLoadingComplete) {
const handleLoad = () => {
heroImg.classList.add("fadein");
if (onLoadingComplete) {
onLoadingComplete();
}
};
if (heroImg.complete) {
handleLoad();
} else {
heroImg.onload = handleLoad;
}
}
// Add other animations to run once the image is loaded
function loadHeading() {
const heading = document.getElementById("heading-block");
heading.classList.add("fadeinup");
const logo = document.getElementById("logo");
logo.classList.add("slideInLeft");
const mainMenu = document.getElementById("main-menu");
mainMenu.classList.add("slideInRight");
}
handleLoading(heroImg, loadHeading);
</script>
Add fadein
style to css.
/*--------------------------------------------------------------
# Animation
--------------------------------------------------------------*/
@keyframes fadein {
100% {opacity: 100;}
}
.fadein {
animation-name: fadein;
animation-duration: 1s;
animation-fill-mode: forwards;
}
For other animations we have a starting state (e.g. opacity 0 and translateY), then we apply an animation class and the animation keyframes. Or the animation start state can be applied to the html element, in which case only the end state is required in the keyframes animation.
#heading-block {
opacity: 0;
}
.fadeinup {
animation: growAndAppear 0.5s ease-out forwards;
animation-delay: 0.5s;
}
@keyframes growAndAppear {
0% {
opacity: 0;
transform: scale(0.5) translateY(200px);
}
100% {
opacity: 1;
transform: scale(1) translateY(0);
}
}