BannerImage
apps/www/src/sections/BannerImage.astro · 66 lines · group Dividers
In the fleet
Used on 1 site built with this engine.
Samples 1



MDX of this sample
<BannerImage imageName={["photo-wide-01.jpg", "photo-wide-02.jpg", "photo-wide-03.jpg"]} />Props
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| imageName | string | string[] | yes | one image, or several to cross-fade between | |
| alt | string | '' | described only when it carries meaning; a decorative band stays silent | |
| interval | number | 6 | seconds each image stays up; ignored for a single image | |
| aspect | string | 'aspect-[3840/880]' | the band's proportions, as a Tailwind aspect class |
Source apps/www/src/sections/BannerImage.astro @ gitt.one
---
import { Image } from 'astro:assets';
import { resolveImage } from '../lib/images';
interface Props {
/** one image, or several to cross-fade between */
imageName: string | string[];
/** described only when it carries meaning; a decorative band stays silent */
alt?: string;
/** seconds each image stays up; ignored for a single image */
interval?: number;
/** the band's proportions, as a Tailwind aspect class */
aspect?: string;
}
const { imageName, alt = '', interval = 6, aspect = 'aspect-[3840/880]' } = Astro.props;
const names = Array.isArray(imageName) ? imageName : [imageName];
const images = await Promise.all(names.map((name) => resolveImage(name)));
---
<div
class:list={[
// the band follows the photo's own proportions, so nothing is cut off the
// top or bottom; the bounds only bite on very narrow or very wide screens
'relative max-h-[36rem] min-h-64 w-full overflow-hidden',
aspect,
]}
data-banner={images.length > 1 ? String(interval) : undefined}
>
{
images.map((image, i) => (
<Image
src={image}
alt={i === 0 ? alt : ''}
widths={[800, 1440, 1920, 2560]}
loading={i === 0 ? 'eager' : 'lazy'}
fetchpriority={i === 0 ? 'high' : 'auto'}
class:list={[
'absolute inset-0 h-full w-full object-cover object-center transition-opacity duration-1000',
i === 0 ? 'opacity-100' : 'opacity-0',
]}
/>
))
}
</div>
<script>
// cross-fade the banner photos, unless the visitor asked for less motion.
// Waits for load: nothing should be moving while the page is still arriving.
window.addEventListener('load', () => {
document.querySelectorAll<HTMLElement>('[data-banner]').forEach((banner) => {
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
const slides = Array.from(banner.querySelectorAll('img'));
let current = 0;
setInterval(
() => {
slides[current].classList.replace('opacity-100', 'opacity-0');
current = (current + 1) % slides.length;
slides[current].classList.replace('opacity-0', 'opacity-100');
},
Number(banner.dataset.banner) * 1000,
);
});
});
</script>Source links point to engine-astro@38c294d9. Samples are demo content: the components are the real ones, the copy and the pictures are made up.