Gatsby Plugin Image: withArtDirection
Hello! If youāre reading this you probably already know about Gatsby Plugin Image, but if not hereās a recap: Announcing: Gatsby Plugin Image with Laurie Barth
The new plugin is super dooper and thereās been some great demos from the community but I was curious about one of the helper functions right at the bottom of the docs called withArtDirection that no one was really talking about.
Whatās withArtDirection?
In short itās a way to show different images to the user via media queries but, without loading additional images into
the DOM using the <img />
tag and slowing down the initial page load speed.
Hereās a demo site and repo for you to have a look at, and in the rest of this post iāll try to explain a little more about each of the examples
Animation
In this first example I went mega overboard and instead of adding two or three media queries I added over a thousand!
The reason thereās so many media queries is because iām swapping an image out at each and every px
value between
576px and approx 1967px using
@media (min-width 576px), @media (min-width 577px), @media (min-width 578px) ... and so on
Each media query will show a new image which, in this example is a single frame from an animated walk cycle
Because media queries fire when the browser is resized itās possible to control the playback of the animation. If you make your browser smaller Mr Lemon will walk forward, if you make your browser larger again Mr Lemon will walk backwards (because the frames are now reversed)
To achieve this I start with 48 single .jpg
files,
you can see these images in the repo here
and theb by using gatsby-source-filesystem
iām able to then query them using GraphQL
const {
allFile: { nodes },
} = useStaticQuery(graphql`
query {
allFile(filter: { sourceInstanceName: { eq: "lemon" }, ext: { eq: ".jpg" } }, sort: { fields: name, order: DESC }) {
nodes {
name
sourceInstanceName
childImageSharp {
gatsbyImageData(layout: FULL_WIDTH)
}
}
}
}
`);
The nodes returned by this query look a little like the below:
Iāve removed some parts of the returned values for brevity
[
{
name: '01-lemon',
sourceInstanceName: 'lemon',
childImageSharp: {
gatsbyImageData: {
images: {
sources: [
{
srcSet: '/static//01-lemon.webp 750w',
},
],
},
},
},
},
{
name: '02-lemon',
sourceInstanceName: 'lemon',
childImageSharp: {
gatsbyImageData: {
images: {
sources: [
{
srcSet: '/static/02-lemon.webp 750w',
},
],
},
},
},
},
];
Then using these nodes
itās possible to pass them on to the withArtDirection
helper function
const images = withArtDirection(
getImage(nodes[0]),
[]
.concat(...new Array(29).fill(nodes))
.map((frame, index) => {
return {
media: `(min-width: ${576 + index}px)`,
image: getImage(frame),
};
})
.reverse()
);
- The first argument of
withArtDirection
is the default image, in my case itās the first node in the index. E.gnodes[0]
- The second argument is an array of images to use across however many breakpoints you define.
In my example the second argument is a concatenated array of all the nodes
but repeated 29 times. This was to
ensure I could incrementally increase the min-width ...px
value to a high enough number for the resize effect to work
on really large monitors. E.g up to 1967px
The return includes both the media query and the image data. I use a helper function here to. This one is called getImage
This all results in 1,391 media queries, and each will show a frame from the original 48 jpg
ās.
Iāve mentioned in the example site that iām not sure why youād want to use this but it is kinda cool that itās possible and that browsers can even handle this many media queries. Chrome does struggle a bit with this to be honest but Firefox on my mac plays these back quite smoothly.
You can see the
full component src
here
Aspect Ratio
This example is probably a little less of an edge case and it reminds me of what the social media advertising dweebs refer to as āvertical videoā. Generally, for social media users will be on their phones and generally phones will be held in the portrait orientation.
With this in mind itās sometimes more effective to show a video in itās vertical format. (It saves the user rotating their phone to see smaller details in an ad)
The same might also be helpful on the web and by using a much simpler @media
query itās possible to show a landscape
image to users on larger, wider screens and a portrait image to users on smaller, narrower screens.
To achieve this I start with x1 landscape image and x1 portrait image, you can see these images in the repo here
The GraphQL query is pretty much the same as the above but only returns x2 nodes.
Then using those nodes itās possible to pass them on to the withArtDirection
helper function
const images = withArtDirection(getImage(nodes[0]), [
{
media: `(max-width: 576px)`,
image: getImage(nodes[1]),
},
]);
- As above the first argument is the default image to display before the
@media
query kicks In - The second argument again is an array but this time itās an array of only one item.
This media query is kind of the wrong way round (ādesktop firstā) because iām using max-width
rather than the āmobile
firstā approach of using min-width
. I found that if I did this the other way round the aspect ratio was more difficult
to control.
This approach also requires a little bit of CSS. You can see below that GatsbyImage
has a className
of
art-directed
which I use to set the height
of the image for screen sizes below 576px
This is CSS-in-Js syntax FYI
'.art-directed': {
height: 'auto'
},
'@media (max-width: 576px)': {
'.art-directed': {
height: 600
}
}
<GatsbyImage className="art-directed" alt="animals" image={images} />
You can see the
full component src
here
Desktop First
Since weāre talking about ādesktop firstā, in this example I tried to be a little bit cheeky and present the user with a different image on smaller screen sizes. Itās broadly the same as the Aspect Ratio example except in this example both images are landscape.
If youāre hearing claims that āno one uses the site on mobileā then surely thereās no harm in displaying joke images for users on smaller screensā¦ because there arenāt any users on smaller screen right?
In all seriousness this method is actually pretty helpful. You could perhaps tailor make designs, or change crops to make images look better when viewed on smaller screens
To achieve this I start with x2 landscape images, you can see these images in the repo here
The GraphQL query is pretty much the same as the above and only returns x2 nodes.
Then using those nodes itās possible to pass them on to the withArtDirection
helper function
const images = withArtDirection(getImage(nodes[1]), [
{
media: `(min-width: 576px)`,
image: getImage(nodes[0]),
},
]);
- As above the first argument is the default image to display before the
@media
query kicks In - The second argument again is an array but this time itās an array of only one item.
Unlike the Aspect Ratio in this example iām using a āmobile firstā approach and have set the default image to
nodes[1]
. The media query takes care of displaying nodes[0]
which is the ādesktopā design you see in the demo, and
this image is shown on screen sizes above 576px
You can see the
full component src
here
Printer
To the best of my knowledge this example only works in Firefox but the idea is similar to the above but instead of using
a screen width @media
query this time I use a print
media query.
The thinking behind this is to surprise the user if they were to print your webpage. Instead of seeing what they saw on screen theyād see a different image. You could use it for additional fun marketing or as iāve tried to do remind the user that printing things isnāt that great for the environment.
To achieve this I start with x2 landscape images, you can see these images in the repo here
The GraphQL query is pretty much the same as the above and only returns x2 nodes.
Then using those nodes itās possible to pass them on to the withArtDirection
helper function
const images = withArtDirection(getImage(nodes[0]), [
{
media: `print`,
image: getImage(nodes[1]),
},
]);
- As above the first argument is the default image to display before the
@media
query kicks In - The second argument again is an array but this time itās an array of only one item.
Depending on how your GraphQL query returns these images will determine which node[n]
is the default and which one is
displayed when the media query kicks in
You can see the
full component src
here
That just about wraps things up. If you have any ideas about how else to use withArtDirection
please come find me on
Twitter and weāll have chat.
Cheerio!