Tag: Images

What Google’s New Page Experience Update Means for Images on Your Website

It’s easy to forget that, as a search engine, Google doesn’t just compare keywords to generate search results. Google knows that if people don’t enjoy their experience on a web page, they won’t stay on the page long enough to consume the content — no matter how relevant it is.

As a result, Google has been experimenting with ways to analyze the user experience of web pages using quantifiable metrics. Factoring these into its search engine rankings, it’s hoped to provide users not only with great, relevant content but with awesome user experiences as well.

Google’s soon-to-be-launched page experience update is a major step in this direction. Website owners with image-heavy websites need to be particularly vigilant to adapt to these changes or risk falling by the wayside. In this article, we’ll talk about everything you need to know regarding this update, and how you can take full advantage of it.

Note: Google introduced their plans for Page Experience in May 2020 and announced in November 2020 that it will begin rolling out in May 2021. However, Google has since delayed their plans for a gradual rollout starting mid-Jun 2021. This was done in order to give website admins time to deal with the shifting conditions brought about by the COVID-19 pandemic first.

Some Background

Before we get into the latest iteration of changes to how Google factors user experience metrics into search engine rankings, let’s get some context. In April 2020, Google made its most pivotal move in this direction yet by introducing a new initiative: core web vitals.

Core web vitals (CWV) were introduced to help web developers deal with the challenges of trying to optimize for search engine rankings using testable metrics – something that’s difficult to do with a highly subjective thing like user experience.

To do this, Google identified three key metrics (what it calls “user-centric performance metrics”). These are:

  1. LCP (Largest Contentful Paint): The largest element above the fold when a web page initially loads. Typically, this is a large featured image or header. How fast the largest content element loads plays a huge role in how fast the user perceives the overall loading speed of the page.
  2. FID (First Input Delay): The time it takes between when a user first interacts with the page and when the main thread is free for the browser to process the event. This can be clicking/tapping a button, link, or interacting with any other dynamic element. Delays when interacting with a page can obviously be frustrating to users which is why keeping FID low is crucial.
  3. Cumulative Layout Shift (CLS): This calculates the visual stability of a page when it first loads. The algorithm takes the size of the elements and the distance they move relevant to the viewport into account. Pages that load with high instability can cause miscues by users, also leading to frustrating situations.

These metrics have evolved from more rudimentary ones that have been in use for some time, such as SI (Speed Index), FCP (First Contentful Paint), TTI (Time-to-interactive), etc.

The reason this is important is because images can play a significant role in how your website’s CWVs score. For example, the LCP is more often than not an above-the-fold image or, at the very least, will have to compete with an image to be loaded first. Images that aren’t correctly used can also negatively impact CLS. Slow-loading images can also impact the FID by adding further delays to the overall rendering of the page.

What’s more, this came on the back of Google’s renewed focus on mobile-first indexing. So, not only are these metrics important for your website, but you have to ensure that your pages score well on mobile devices as well.

It’s clear that, in general, Google is increasingly prioritizing user experience when it comes to search engine rankings. Which brings us to the latest update – Google now plans to incorporate page experience as a ranking factor, starting with an early rollout in mid-June 2021.

So, what is page experience? In short, it’s a ranking signal that combines data from a number of metrics to try and determine how good or bad the user experience of a web page is. It consists of the following factors:

  • Core Web Vitals: Using the same, unchanged, core web vitals. Google has established guidelines and recommended rankings that you can find here. You need an overall “good” CWV rating to qualify for a “good” page experience score.
  • Mobile Usability: A URL must have no mobile usability errors to qualify for a “good” page experience score.
  • Security Issues: Any flagged security issues will disqualify websites.
  • HTTPS: Pages must be served via HTTPS to qualify.
  • Ad Experience: Measures to what degree ads negatively affect the user experience on your web page, for example, by being intrusive, distracting, etc.

As part of this change, Google announced its intention to include a visual indicator, or badge, that highlights web pages that have passed its page experience criteria. This will be similar to previous badges the search engine has used to promote AMP (Accelerated Mobile Pages) or mobile-friendly pages.

This official recognition will give high-performing web pages a massive advantage in the highly competitive arena that is Google’s SERPs. This visual cue will undoubtedly boost CTRs and organic traffic, especially for sites that already rank well. This feature may drop as soon as May if it passes its current trial phase.

Another bit of good news for non-AMP users is that all pages will now become eligible for Top Stories in both the browser and Google News app. Although Google states that pages can qualify for Top Stories “irrespective of its Core Web Vitals score or page experience status,” it’s hard to imagine this not playing a role for eligibility now or down the line.

Key Takeaway: What Does This Mean For Images on Your Website?

Google noted a 70% surge in consumer usage of their Lighthouse and PageSpeed Insight tools, showing that website owners are catching up on the importance of optimizing their pages. This means that standards will only become higher and higher when competing with other websites for search engine rankings.

Google has reaffirmed that, despite these changes, content is still king. However, content is more than just the text on your pages, and truly engaging and user-friendly content also consists of thoughtfully used media, the majority of which will likely be images.

With the proposed page experience badges and Top Stories eligibility up for grabs, the stakes have never been higher to rank highly with the Google Search algorithm. You need every advantage that you can get. And, as I’m about to show, optimizing your image assets can have a tangible effect on scoring well according to these metrics.

What Can You Do To Keep Up?

Before I propose my solution to help you optimize image assets for core web vitals, let’s look at why images are often detrimental to performance:

  • Images bloat the overall size of your website pages, especially if the images are unoptimized (i.e. uncompressed, not properly sized, etc.)
  • Images need to be responsive to different devices. You need much smaller image sizes to maintain the same visual quality on smaller screens.
  • Different contexts (browsers, OSs, etc.) have different formats for optimally rendering images. However, most images are still used in .JPG/.PNG format.
  • Website owners don’t always know about the best practices associated with using images on website pages, such as always explicitly specifying width/height attributes.

Using conventional methods, it can take a lot of blood, sweat, and tears to tackle these issues. Most solutions, such as manually editing images and hard-coding responsive syntax have inherent issues with scalability, the ability to easily update/adjust to changes, and bloat your development pipeline.

To optimize your image assets, particularly with a focus on improving CWVs, you need to:

  • Reduce image payloads
  • Implement effective caching
  • Speed up delivery
  • Transform images into optimal next-gen formats
  • Ensure images are responsive
  • Implement run-time logic to apply the optimal setting in different contexts

Luckily, there is a class of tools designed specifically to solve these challenges and provide these solutions — image CDNs. Particularly, I want to focus on ImageEngine which has consistently outperformed other CDNs on page performance tests I’ve conducted.

ImageEngine is an intelligent, device-aware image CDN that you can use to serve your website images (including GIFs). ImageEngine uses WURFL device detection to analyze the context your website pages are accessed from (device, screen size, DPI, OS, browser, etc.) and optimize your image assets accordingly. Based on these criteria, it can optimize images by intelligently resizing, reformatting, and compressing them.

It’s a completely automatic, set-it-and-forget-it solution that requires little to no intervention once it’s set up. The CDN has over 20 global PoPs with the logic built into the edge servers for faster across different regions. ImageEngine claims to achieve cache-hit ratios of as high as 98%+ as well as reduce image payloads by 75%+.

Step-by-Step Test + How to Use ImageEngine to Improve Page Experience

To illustrate the difference using an image CDN like ImageEngine can make, I’ll show you a practical test.

First, let’s take a look at how a page with a massive amount of image content scores using PageSpeed Insights. It’s a simple page, but consists of a large number of high-quality images with some interactive elements, such as buttons and links as well as text.

FID is unique because it relies on data from real-world interactions users have with your website. As a result, FID can only be collected “in the field.” If you have a website with enough traffic, you can get the FID by generating a Page Experience Report in the Google Console.

However, for lab results, from tools like Lighthouse or PageSpeed Insights, we can surmise the impact of blocking resources by looking at TTI and TBT.

Oh, yes, and I’ll also be focussing on the results of a mobile audit for a number of reasons:

  1. Google themselves are prioritizing mobile signals and mobile-first indexing
  2. Optimizing web pages and images assets are often most challenging for mobile devices/general responsiveness
  3. It provides the opportunity to show the maximum improvement a image CDN can provide

With that in mind, here are the results for our page:

So, as you can see, we have some issues. Helpfully, PageSpeed Insights flags the two CWVs present, LCP and CLS. As you can see, because of the huge image payload (roughly 35 MB), we have a ridiculous LCP of nearly 1 minute.

Because of the straightforward layout and the fact that I did explicitly give images width and height attributes, our page happened to be stable with a 0 CLS. However, it’s important to realize that slow loading images can also impact the perceived stability of your pages. So, even if you can’t directly improve on CLS, the faster sizable elements such as images load, the better the overall experience for real-world users.

TTI and TBT were also sub-par. It will take at least two  seconds from the moment the first element appears on the page until when the page can start to become interactive.

As you can see from the opportunities for improvement and diagnostics, almost all issues were image-related:

Setting Up ImageEngine and Testing the Results

Ok, so now it’s time to add ImageEngine into the mix and see how it improves performance metrics on the same page.

Setting up ImageEngine on nearly any website is relatively straightforward. First, go to ImageEngine.io and signup for a free trial. Just follow the simple 3-step signup process where you will need to:

  1. provide the website you want to optimize, 
  2. the web location where your images are stored, and then 
  3. copy the delivery address ImageEngine assigns to you.

The latter will be in the format of {random string}.cdn.imgeng.in but can be updated from within the ImageEngine dashboard.

To serve images via this domain, simply go back to your website markup and update the <img> src attributes. For example:

From:

<img src=”mywebsite.com/images/header-image.jpg”/>

To:

<img src=”myimages.cdn.imgeng.in/images/header-image.jpg”/>

That’s all you need to do. ImageEngine will now automatically pull your images and dynamically optimize them for best results when visitors view your website pages. You can check the official integration guides in the documentation on how to use ImageEngine with Magento, Shopify, Drupal, and more. There is also an official WordPress plugin.

Here’s the results for my ImageEngine test page once it’s set up:

As you can see, the results are nearly flawless. All metrics were improved, scoring in the green – even Speed Index and LCP which were significantly affected by the large images.

As a result, there were no more opportunities for improvement. And, as you can see, ImageEngine reduced the total page payload to 968 kB, cutting down image content by roughly 90%:

Conclusion

To some extent, it’s more of the same from Google who has consistently been moving in a mobile direction and employing a growing list of metrics to hone in on the best possible “page experience” for its search engine users. Along with reaffirming their move in this direction, Google stated that they will continue to test and revise their list of signals.

Other metrics that can be surfaced in their tools, such as TTFB, TTI, FCP, TBT, or possibly entirely new metrics may play even larger roles in future updates.

Finding solutions that help you score highly for these metrics now and in the future is key to staying ahead in this highly competitive environment. While image optimization is just one facet, it can have major implications, especially for image-heavy sites.

An image CDN like ImageEngine can solve almost all issues related to image content, with minimal time and effort as well as future proof your website against future updates.


The post What Google’s New Page Experience Update Means for Images on Your Website appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , , ,

Barebones CSS for Fluid Images

Zach takes a look at some fundamental HTML+CSS usage for fluid, responsive images. Most of it, I’d say, is what you’d expect, but things get weird when srcset gets involved.

I poked my way through, and in addition to the weird thing Zach noted, wanted to add one more thing. Let’s start like this:

<img src="./img.jpg" alt="" />

With no other CSS involved, this renders at the “intrinsic size” of the image. Say the original image is 400px wide, it renders 400px wide.

We should be putting width and height attributes on images, because it allows the browser to make space for them even before they are downloaded (even when they are fluid, which is super cool). So:

<img src="./img.jpg" alt="" width="400" height="300" />

Also nothing terribly weird there. Even if we slap max-width: 100% in the CSS, that’ll do what we want: preserving space, behave fluidly, and not growing bigger than it should.

But let’s hold off on the max-width: 100% thing for a second. If we just use srcset and set up multiple sources.

<img src="./img.jpg" alt=""      srcset="./img-200.jpg 200w, ./img-400.jpg 400w" />

BAM, we blow out the width of the thing.

That won’t render at 200px or 400px—it’ll actually render at 100vw, believe it or not. I think that’s because that’s the default sizes value. I normally think of the sizes attribute as not information about anything to do with actual layout, but just information for the browser to choose a source. But that’s not true here. It really does effect layout (in all browsers I tested). Here’s proof:

See the little one below it where all I change is the sizes.

Anyway that’s not what Zach honed in on, but it’s similar. Let’s put back the responsible thing and add in width and height attributes.

<img src="./img.jpg" alt="" width="200" height="137"      srcset="./img-200.jpg 200w, ./img-400.jpg 200w" />

No more blowout (with or without sizes) but now we have a new weird problem. This is basically like saying max-width: 200px. Even though we have sources that are wider than 200px, we’ve capped the width at 200px. Zach puts it like:

Using max-width: 100% constrains the image to the container, but be careful when you use this with srcset—it may cap smaller than you want when using [width]! Pair with width: auto to fix this.

Zach’s final snippet is this, which I think reigns in all the weirdness:

img {   max-width: 100%; } img[width] {   width: auto; /* Defer to max-width */ } img[width][height] {   height: auto; /* Preserve aspect ratio */ }  /* Let SVG scale without boundaries */ img[src$  =".svg"] {   width: 100%;   height: auto;   max-width: none; }

Direct Link to ArticlePermalink


The post Barebones CSS for Fluid Images appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, ,
[Top]

Automatic Social Share Images

It’s a pretty low-effort thing to get a big fancy link preview on social media. Toss a handful of specific <meta> tags on a URL and you get a big image-title-description thing. Here’s Twitter’s version of an article on this site:

It’s particularly low-effort on this site, as our Yoast SEO plugin puts the correct tags in place automatically. The image it uses by default is the “featured image” feature of WordPress, which we use anyway.

I’m a fan of that kind of improvement for that so little work. Jetpack helps the process, too, by automating things.

But let’s say you don’t use these particular tools. Maybe creating an image per blog post isn’t even something you’re interested in doing, but you still want something nice to show for the social media preview.

We’ve covered this before. You can design the “image” with HTML and CSS, using content and metadata you already have from the blog post. You can turn it into an image with Puppeteer (or the like) and then use that for the image in the meta tags.

Ryan Filler has detailed out that process the best I’ve seen so far.

  1. Create a route on your site that takes dynamic data from the URL to create the layout
  2. Make a cloud function that hits that route, turns it into an image, and uploads it to Cloudinary (for optimizing and serving)
  3. Any time the image is requested, check to see if you’ve already created it. If so, serve it from Cloudinary; if not, make it, then serve it.

This stuff gets my brain cooking. What if we didn’t need to create a raster image at all?

Maybe rather than needing to create a raster image we could use SVG? SVG would be easy to template, and we know <img src="file.svg" alt="" /> is extremely capable. But… Twitter says:

Images must be less than 5MB in size. JPG, PNG, WEBP and GIF formats are supported. Only the first frame of an animated GIF will be used. SVG is not supported.

Fifty sad faces, Twitter. But let’s continue this thought experiment.

We need raster. The <canvas> element can spit out a PNG. What if the cloud function that you talked to was an actual browser? Richard Young called that a “browser function” last year. Maybe the browser-in-the-cloud could do that SVG templating we’re dreaming of, but then draw it to a canvas and spit out that PNG.

Meh, I’m not sure that solves anything since you’d still have the Puppeteer dependency and, if anything, this just complicates how you make the image. Still, something appeals to me about being able to use native browser abilities at the server level.

Direct Link to ArticlePermalink


The post Automatic Social Share Images appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , ,
[Top]

Converting and Optimizing Images From the Command Line

Images take up to 50% of the total size of an average web page. And if images are not optimized, users end up downloading extra bytes. And if they’re downloading extra bytes, the site not only takes that much more time to load, but users are using more data, both of which can be resolved, at least in part, by optimizing the images before they are downloaded.

Researchers around the world are busy developing new image formats that possess high visual quality despite being smaller in size compared to other formats like PNG or JPG. Although these new formats are still in development and generally have limited browser support, one of them, WebP, is gaining a lot of attention. And while they aren’t really in the same class as raster images, SVGs are another format many of us have been using in recent years because of their inherently light weight.

There are tons of ways we can make smaller and optimized images. In this tutorial, we will write bash scripts that create and optimize images in different image formats, targeting the most common formats, including JPG, PNG, WebP, and SVG. The idea is to optimize images before we serve them so that users get the most visually awesome experience without all the byte bloat.

Our targeted directory of images

Our directory of optimized images

This GitHub repo has all the images we’re using and you’re welcome to grab them and follow along.

Set up

Before we start, let’s get all of our dependencies in order. Again, we’re writing Bash scripts, so we’ll be spending time in the command line.

Here are the commands for all of the dependencies we need to start optimizing images:

sudo apt-get update sudo apt-get install imagemagick webp jpegoptim optipng npm install -g svgexport svgo

It’s a good idea to know what we’re working with before we start using them:

OK, we have our images in the original-images directory from the GitHub repo. You can follow along at commit 3584f9b.

Note: It is strongly recommended to backup your images before proceeding. We’re about to run programs that alter these images, and while we plan to leave the originals alone, one wrong command might change them in some irreversible way. So back anything up that you plan to use on a real project to prevent cursing yourself later.

Organize images

OK, we’re technically set up. But before we jump into optimizing all the things, we should organize our files a bit. Let’s organize them by splitting them up into different sub-directories based on their MIME type. In fact, we can create a new bash to do that for us!

The following code creates a script called organize-images.sh:

#!/bin/bash  input_dir="$ 1"  if [[ -z "$ input_dir" ]]; then   echo "Please specify an input directory."   exit 1 fi  for img in $ ( find $ input_dir -type f -iname "*" ); do   # get the type of the image   img_type=$ (basename `file --mime-type -b $ img`)    # create a directory for the image type   mkdir -p $ img_type    # move the image into its type directory   rsync -a $ img $ img_type done

This might look confusing if you’re new to writing scripts, but what it’s doing is actually pretty simple. We give the script an input directory where it looks for images. the script then goes into that input directory, looks for image files and identifies their MIME type. Finally, it creates subdirectories in the input folder for each MIME type and drops a copy of each image into their respective sub-directory.

Let’s run it!

bash organize-images.sh original-images

Sweet. The directory looks like this now. Now that our images are organized, we can move onto creating variants of each image. We’ll tackle one image type at a time.

Convert to PNG

We will convert three types of images into PNG in this tutorial: WebP, JPEG, and SVG. Let’s start by writing a script called webp2png.sh, which pretty much says what it does: convert WebP files to PNG files.

#!/bin/bash  # directory containing images input_dir="$ 1"  if [[ -z "$ input_dir" ]]; then   echo "Please specify an input directory."   exit 1 fi  # for each webp in the input directory for img in $ ( find $ input_dir -type f -iname "*.webp" ); do   dwebp $ img -o $ {img%.*}.png done

Here’s what happening:

  • input_dir="$ 1": Stores the command line input to the script
  • if [[ -z "$ input_dir" ]]; then: Runs the subsequent conditional if the input directory is not defined
  • for img in $ ( find $ input_dir -type f -iname "*.webp" );: Loops through each file in the directory that has a .webp extension.
  • dwebp $ img -o $ {img%.*}.png: Converts the WebP image into a PNG variant.

And away we go:

bash webp2png.sh webp

We now have our PNG images in the webp directory. Next up, let’s convert JPG/JPEG files to PNG with another script called jpg2png.sh:

#!/bin/bash  # directory containing images input_dir="$ 1"  if [[ -z "$ input_dir" ]]; then   echo "Please specify an input directory."   exit 1 fi  # for each jpg or jpeg in the input directory for img in $ ( find $ input_dir -type f -iname "*.jpg" -o -iname "*.jpeg" ); do   convert $ img $ {img%.*}.png done

This uses the convert command provided by the ImageMagick package we installed. Like the last script, we provide an input directory that contains JPEG/JPG images. The script looks in that directory and creates a PNG variant for each matching image. If you look closely, we have added -o -iname "*.jpeg" in the find. This refers to Logical OR, which is the script that finds all the images that have either a .jpg or .jpeg extension.

Here’s how we run it:

bash jpg2png.sh jpeg

Now that we have our PNG variants from JPG, we can do the exact same thing for SVG files as well:

#!/bin/bash  # directory containing images input_dir="$ 1"  # png image width width="$ 2"  if [[ -z "$ input_dir" ]]; then   echo "Please specify an input directory."   exit 1 elif [[ -z "$ width" ]]; then   echo "Please specify image width."   exit 1 fi  # for each svg in the input directory for img in $ ( find $ input_dir -type f -iname "*.svg" ); do   svgexport $ img $ {img%.*}.png $ width: done

This script has a new feature. Since SVG is a scalable format, we can specify the width directive to scale our SVGs up or down. We use the svgexport package we installed earlier to convert each SVG file into a PNG:

bash svg2png.sh svg+xml

Commit 76ff80a shows the result in the repo.

We’ve done a lot of great work here by creating a bunch of PNG files based on other image formats. We still need to do the same thing for the rest of the image formats before we get to the real task of optimizing them.

Convert to JPG

Following in the footsteps of PNG image creation, we will convert WebP, JPEG, and SVG into JPG. Let’s start by writing a script called png2jpg.sh that converts PNG to SVG:

#!/bin/bash  # directory containing images input_dir="$ 1"  # jpg image quality quality="$ 2"  if [[ -z "$ input_dir" ]]; then   echo "Please specify an input directory."   exit 1 elif [[ -z "$ quality" ]]; then   echo "Please specify image quality."   exit 1 fi  # for each png in the input directory for img in $ ( find $ input_dir -type f -iname "*.png" ); do   convert $ img -quality $ quality% $ {img%.*}.jpg done

You might be noticing a pattern in these scripts by now. But this one introduces a new power where we can set a -quality directive to convert PNG images to JPG images. Rest is the same.

And here’s how we run it:

bash png2jpg.sh png 90

Woah. We now have JPG images in our png directory. Let’s do the same with a webp2jpg.sh script:

#!/bin/bash  # directory containing images input_dir="$ 1"  # jpg image quality quality="$ 2"  if [[ -z "$ input_dir" ]]; then   echo "Please specify an input directory."   exit 1 elif [[ -z "$ quality" ]]; then   echo "Please specify image quality."   exit 1 fi  # for each webp in the input directory for img in $ ( find $ input_dir -type f -iname "*.webp" ); do   # convert to png first   dwebp $ img -o $ {img%.*}.png    # then convert png to jpg   convert $ {img%.*}.png -quality $ quality% $ {img%.*}.jpg done

Again, this is the same thing we wrote for converting WebP to PNG. However, there is a twist. We cannot convert WebP format directly into a JPG format. Hence, we need to get a little creative here and convert WebP to PNG using dwebp and then convert PNG to JPG using convert. That is why, in the for loop, we have two different steps.

Now, let’s run it:

bash webp2jpg.sh jpeg 90

Voilà! We have created JPG variants for our WebP images. Now let’s tackle SVG to JPG:

#!/bin/bash  # directory containing images input_dir="$ 1"  # jpg image width width="$ 2"  # jpg image quality quality="$ 3"  if [[ -z "$ input_dir" ]]; then   echo "Please specify an input directory."   exit 1 elif [[ -z "$ width" ]]; then   echo "Please specify image width."   exit 1 elif [[ -z "$ quality" ]]; then   echo "Please specify image quality."   exit 1 fi  # for each svg in the input directory for img in $ ( find $ input_dir -type f -iname "*.svg" ); do     svgexport $ img $ {img%.*}.jpg $ width: $ quality% done

You might bet thinking that you have seen this script before. You have! We used the same script for to create PNG images from SVG. The only addition to this script is that we can specify the quality directive of our JPG images.

bash svg2jpg.sh svg+xml 512 90

Everything we just did is contained in commit 884c6cf in the repo.

Convert to WebP

WebP is an image format designed for modern browsers. At the time of this writing, it enjoys roughly 90% global browser support, including with partial support in Safari. WebP’s biggest advantage is it’s a much smaller file size compared to other mage formats, without sacrificing any visual quality. That makes it a good format to serve to users.

But enough talk. Let’s write a png2webp.sh that — you guessed it — creates WebP images out of PNG files:

#!/bin/bash  # directory containing images input_dir="$ 1"  # webp image quality quality="$ 2"  if [[ -z "$ input_dir" ]]; then   echo "Please specify an input directory."   exit 1 elif [[ -z "$ quality" ]]; then   echo "Please specify image quality."   exit 1 fi  # for each png in the input directory for img in $ ( find $ input_dir -type f -iname "*.png" ); do   cwebp $ img -q $ quality -o $ {img%.*}.webp done

This is just the reverse of the script we used to create PNG images from WebP files. Instead of using dwebp, we use cwebp.

bash png2webp.sh png 90

We have our WebP images. Now let’s convert JPG images. The tricky thing is that there is no way to directly convert a JPG files into WebP. So, we will first convert JPG to PNG and then convert the intermediate PNG to WebP in our jpg2webp.sh script:

#!/bin/bash  # directory containing images input_dir="$ 1"  # webp image quality quality="$ 2"  if [[ -z "$ input_dir" ]]; then   echo "Please specify an input directory."   exit 1 elif [[ -z "$ quality" ]]; then   echo "Please specify image quality."   exit 1 fi  # for each webp in the input directory for img in $ ( find $ input_dir -type f -iname "*.jpg" -o -iname "*.jpeg" ); do   # convert to png first   convert $ img $ {img%.*}.png    # then convert png to webp   cwebp $ {img%.*}.png -q $ quality -o $ {img%.*}.webp done

Now we can use it like this to get our WebP variations of JPG files:

bash jpg2webp.sh jpeg 90

Commit 6625f26 shows the result.

Combining everything into a single directory

Now that we are done converting stuff, we’re one step closer to optimize our work. But first, we’re gong to bring all of our images back into a single directory so that it is easy to optimize them with fewer commands.

Here’s code that creates a new bash script called combine-images.sh:

#!/bin/bash  input_dirs="$ 1" output_dir="$ 2"  if [[ -z "$ input_dirs" ]]; then   echo "Please specify an input directories."   exit 1 elif [[ -z "$ output_dir" ]]; then   echo "Please specify an output directory."   exit 1 fi  # create a directory to store the generated images mkdir -p $ output_dir  # split input directories comma separated string into an array input_dirs=($ {input_dirs//,/ })  # for each directory in input directory for dir in "$ {input_dirs[@]}" do   # copy images from this directory to generated images directory   rsync -a $ dir/* $ output_dir/ done

The first argument is a comma-separated list of input directories that will transfer images to a target combined directory. The second argument is defines that combined directory.

bash combine-images.sh jpeg,svg+xml,webp,png generated-images

The final output can be seen in the repo.

Optimize SVG

Let us start by optimizing our SVG images. Add the following code to optimize-svg.sh:

#!/bin/bash  # directory containing images input_dir="$ 1"  if [[ -z "$ input_dir" ]]; then echo "Please specify an input directory." exit 1 fi  # for each svg in the input directory for img in $ ( find $ input_dir -type f -iname "*.svg" ); do   svgo $ img -o $ {img%.*}-optimized.svg done

We’re using the SVGO package here. It’s got a lot of options we can use but, to keep things simple, we’re just sticking with the default behavior of optimizing SVG files:

bash optimize-svg.sh generated-images
This gives us a 4KB saving on each image. Let’s say we were serving 100 SVG icons — we just saved 400KB!

The result can be seen in the repo at commit 75045c3.

Optimize PNG

Let’s keep rolling and optimize our PNG files using this code to create an optimize-png.sh command:

#!/bin/bash  # directory containing images input_dir="$ 1"  if [[ -z "$ input_dir" ]]; then   echo "Please specify an input directory."   exit 1 fi  # for each png in the input directory for img in $ ( find $ input_dir -type f -iname "*.png" ); do   optipng $ img -out $ {img%.*}-optimized.png done

Here, we are using the OptiPNG package to optimize our PNG images. The script looks for PNG images in the input directory and creates an optimized version of each one, appending -optimized to the file name. There is one interesting argument, -o, which we can use to specify the optimization level. The default value is 2 **and values range from 0 to 7. To optimize our PNGs, we run:

bash optimize-png.sh generated-images
PNG optimization depends upon the information stored in the image. Some images can be greatly optimized while some show little to no optimization.

As we can see, OptiPNG does a great job optimizing the images. We can play around with the -o argument to find a suitable value by trading off between image quality and size. Check out the results in commit 4a97f29.

Optimize JPG

We have reached the final part! We’re going to wrap things up by optimizing JPG images. Add the following code to optimize-jpg.sh:

#!/bin/bash  # directory containing images input_dir="$ 1"  # target image quality quality="$ 2"  if [[ -z "$ input_dir" ]]; then   echo "Please specify an input directory."   exit 1 elif [[ -z "$ quality" ]]; then   echo "Please specify image quality."   exit 1 fi  # for each jpg or jpeg in the input directory for img in $ ( find $ input_dir -type f -iname "*.jpg" -o -iname "*.jpeg" ); do   cp $ img $ {img%.*}-optimized.jpg   jpegoptim -m $ quality $ {img%.*}-optimized.jpg done

This script uses JPEGoptim. The problem with this package is that it doesn’t have any option to specify the output file. We can only optimize the image file in place. We can overcome this by first creating a copy of the image, naming it whatever we like, then optimizing the copy. The -m argument is used to specify image quality. It is good to experiment with it a bit to find the right balance between quality and file size.

bash optimize-jpg.sh generated-images 95

The results are shows in commit 35630da.

Wrapping up

See that? With a few scripts, we can perform heavy-duty image optimizations right from the command line, and use them on any project since they’re installed globally. We can set up CI/CD pipelines to create different variants of each image and serve them using valid HTML, APIs, or even set up our own image conversion websites.

I hope you enjoyed reading and learning something from this article as much as I enjoyed writing it for you. Happy coding!


The post Converting and Optimizing Images From the Command Line appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , ,
[Top]

Optimize Images According to Network and Device Constraints in React

Connectivity has evolved beyond recognition since the beginning of the internet. We are lightyears past dial up, these days, and can watch a video in high resolution on our smartphone while being connected to a mobile network. But not all mobile connections are created equal – older generation networks (3G, 2G, etc.) are still quite dominant, accounting for almost half of all connections worldwide in 2020.

Unfortunately, the phasing out process is very slow, and many people around the globe are experiencing really dragged out page loads, comparable to the very early days of home internet adoption.

Modern websites became resource-hungry, featuring lots of images and animations. For a visitor on an underpowered device and a fragile network connection, an average webpage might take a good minute to load completely. This is largely due to the fact that developers often make binary decisions when it comes to user’s hardware and network conditions: devices fall either in the desktop or smartphone category, while connectivity is a question of being on- or offline. In reality, user’s circumstances tend to be much more nuanced.

We Can Do Better?

What can be done to bridge the gap for users on modest devices and spotty connections? First, we need to do a quick evaluation of what exactly their conditions are by looking at the following two properties:

Based on that, we can decide, for instance, to adjust the quality of the images we intend to serve. There is a catch, however, with Jamstack websites and apps rendered on the server – `navigator`object, as any other browser API, isn’t available during the rendering stage. A common workaround for this issue is to add a bunch of responsive image markup, but it comes with a significant pain point – inefficient scaling. An image CDN like ImageEngine helps to avoid this and other pitfalls associated with responsive images as it handles all the heavy-lifting behind the scenes by applying automated, smart tweaks to requested resources on-the-fly.

When it comes to adapting to a user’s network constraints, one could detect connection type and instruct an image CDN to vary compression according to connection quality. Here’s how one might go about it in React:

import React, { useState, useEffect } from 'react'  const useConnectionType = (defaultConnectionType) => {    const isSupported = navigator?.connection?.effectiveType     ? true     : false    const [connectionType, setNetworkStatus] = useState(     isSupported       ? navigator.connection.effectiveType       : defaultConnectionType   )    useEffect(() => {     if (isSupported) {       const { connection } = navigator       const updateConnectionType = () => {         setNetworkStatus(connection.effectiveType)       }        connection.addEventListener('change', updateConnectionType)        return () => {         connection.removeEventListener('change', updateConnectionType)       }     }   }, [])    return [ connectionType, setNetworkStatus ] }  const imageCDNHost = 'images.foo.com  function ConnectionAwareComponent () {    const [ connectionType ] = useConnectionType()    let compressionLevel = 0    switch (connectionType) {     case 'slow-2g':       compressionLevel = 65       break     case '2g':       compressionLevel = 50       break     case '3g':       compressionLevel = 30       break     case '4g':       compressionLevel = 0       break   }    return (     <div>       {/* Apply variable compression via dedicated directive */}       <img src={`$ {imageCDNHost}/?imgeng?=cmpr_$ {compressionLevel}`} />     </div>   ) }

One can take this idea even further to accommodate those on really sluggish and wonky networks by rendering blurred images and offering an option to download a higher resolution version on demand. Or devise a performance score system and adjust what is sent based on that. 

On the other hand, the fact that the user is on a “speedy” 4G connection doesn’t necessarily mean they aren’t interested in saving data as they might be accessing a website in roaming. Enabling Client Hints on one’s website will let site owners detect the presence of a data saver flag and take necessary steps to adjust to the user’s preferences.

Reasons for Faster Images

Mediocre CPU, modest amounts of memory and a low-grade connection aren’t imaginary constraints. They pose real user experience challenges potentially affecting hundreds of millions of users worldwide. Some companies began to bake inclusive experiences into their products: streaming services like Netflix and Spotify adjust the streaming quality based on your network conditions, while many others are doing automatic image optimizations behind the scenes for users.

Developing regions, where fast networks aren’t yet accessible to everyone and everywhere, might not be one’s target market. Meanwhile, someone browsing from a rural area in a developed country will likely have a jarring experience if they are served a fully-fledged version of a website. We can be more considerate and intentional by adjusting what we send / display to our users with only a couple of small tweaks.

Using an image CDN like ImageEngine simplifies the image optimization process and automatically responds to the Client Hints for network constraints. The result is a better experience for a network-constrained visitor and an elegant workflow for developers.


The post Optimize Images According to Network and Device Constraints in React appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , , ,
[Top]

Optimize Images with a GitHub Action

I was playing with GitHub Actions the other day. Such a nice tool! Short story: you can have it run code for you, like run your build processes, tests, and deployments. But it’s just configuration files that can run whatever you need. There is a whole marketplace of Actions wanting to do work for you.

What I wanted to do was run code to do image optimization. That way I never have to think about it. Any image in the repo has been optimized.

There is an action for this already, Calibre’s image-actions, which we’ll leverage here. You’ll also need to ensure Actions is enabled for the repo. I know in my main organization we only flip on Actions on a per-repo basis, which is one of the options.

Then you make a file at ./github/workflows/optimize-images.yml. That’s where you can configure this action. All your actions can have separate files, if you want them to. I made this a separate file because (1) it only works with “pushes to pull requests,” so if you have other actions that run on different triggers, they won’t mix nicely, and (2) That’s what is in their docs and looks like the suggested usage.

name: Optimize images on: pull_request jobs:   build:     name: calibreapp/image-actions     runs-on: ubuntu-latest     steps:       - name: Checkout Repo         uses: actions/checkout@master        - name: Compress Images         uses: calibreapp/image-actions@master         with:           githubToken: $ {{ secrets.GITHUB_TOKEN }}

Now if you make a pull request, you’ll see it run:

That successful run then leaves a comment on the pull request saying what it was able to optimize:

It will literally re-commit those files back to the pull request as well, so if you’re going to stay on the pull request and keep working, you’ll need to push again before you can push to get the optimized images.

I can look at that automatic commit and see the difference:

The commit preview in Git Tower.

How I can merge the PR knowing all is well:

Pretty cool. Is optimizing your images locally particularly hard? No. Is never having to think about it again better? Yeah. You’re taking on a smidge of technical debt here, but reducing it elsewhere, which is a very fair trade, at least in my book.


The post Optimize Images with a GitHub Action appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , ,
[Top]

Lazy Loading Images in Svelte

One easy way to improve the speed of a website is to only download images only when they’re needed, which would be when they enter the viewport. This “lazy loading” technique has been around a while and there are lots of great tutorials on how to implement it.

But even with all the resources out there, implementing lazy loading can look different depending on the project you’re working in or the framework you’re using. In this article, I’ll use the Intersection Observer API alongside the onLoad event to lazy load images with the Svelte JavaScript framework.

Check out Tristram Tolliday’s introduction to Svelte if you’re new to the framework.

Let’s work with a real-life example

I put this approach together while testing the speed on a Svelte and Sapper application I work on, Shop Ireland. One of our goals is to make the thing as fast as we possible can. We hit a point where the homepage was taking a performance hit because the browser was downloading a bunch of images that weren’t even on the screen, so naturally, we turned to lazy loading them instead.

Svelte is already pretty darn fast because all of the code is compiled in advance. But once we tossed in lazy loading for images, things really started speeding up.

This is what we’re going to work on tofgether. Feel free to grab the final code for this demo from GitHub and read along for an explanation of how it works.

This is where we’ll end up by the end:

Let’s quickly start up Svelte

You might already have a Svelte app you’d like to use, but if not, let’s start a new Svelte project and work on it locally. From the command line:

npx degit sveltejs/template my-svelte-project cd my-svelte-project npm install npm run dev

You should now have a beginner app running on http://localhost:5000.

Adding the components folder

The initial Svelte demo has an App.svelte file but no components just yet. Let’s set up the components we need for this demo. There is no components  folder, so let’s create one in the src folder. Inside that folder, create an Image folder — this will hold our components for this demo.

We’re going to have our components do two things. First, they will check when an image enters the viewport. Then, when an image does enter, the components will wait until the image file has loaded before showing it.

The first component will be an <IntersectionObserver> that wraps around the second component, an <ImageLoader>. What I like about this setup is that it allows each component to be focused on doing one thing instead of trying to pack a bunch of operations in a single component.

Let’s start with the <IntersectionObserver> component.

Observing the intersection

Our first component is going to be a working implementation of the Intersection Observer API. The Intersection Observer is a pretty complex thing but the gist of it is that it watches a child element and informs us when it enters the bounding box of its parent. Hence images: they can be children of some parent element and we can get a heads up when they scroll into view.

While it’s definitely a great idea to get acquainted with the ins and outs of the Intersection Observer API — and Travis Almand has an excellent write-up of it — we’re going to make use of a handy Svelte component that Rich Harris put together for svelte.dev.

We’ll set this up  first before digging into what exactly it does. Create a new IntersectionObserver.svelte file and drop it into the src/components/Image folder. This is where we’ll define the component with the following code:

<script>   import { onMount } from 'svelte'; 
   export let once = false;   export let top = 0;   export let bottom = 0;   export let left = 0;   export let right = 0; 
   let intersecting = false;   let container; 
   onMount(() => {     if (typeof IntersectionObserver !== 'undefined') {       const rootMargin = `$  {bottom}px $  {left}px $  {top}px $  {right}px`; 
       const observer = new IntersectionObserver(entries => {         intersecting = entries[0].isIntersecting;         if (intersecting && once) {           observer.unobserve(container);         }       }, {         rootMargin       }); 
       observer.observe(container);       return () => observer.unobserve(container);     } 
     function handler() {       const bcr = container.getBoundingClientRect(); 
       intersecting = (         (bcr.bottom + bottom) > 0 &&         (bcr.right + right) > 0 &&         (bcr.top - top) < window.innerHeight &&         (bcr.left - left) < window.innerWidth       ); 
       if (intersecting && once) {         window.removeEventListener('scroll', handler);       }     } 
     window.addEventListener('scroll', handler);     return () => window.removeEventListener('scroll', handler);   }); </script> 
 <style>   div {     width: 100%;     height: 100%;   } </style> 
 <div bind:this={container}>   <slot {intersecting}></slot> </div>

We can use this component as a wrapper around other components, and it will determine for us whether the wrapped component is intersecting with the viewport.

If you’re familiar with the structure of Svelte components, you’ll see it follows a pattern that starts with scripts, goes into styles, then ends with markup. It sets some options that we can pass in, including a once property, along with numeric values for the top, right, bottom and left distances from the edge of the screen that define the point where the intersection begins.

We’ll ignore the distances but instead make use of the once property. This will ensure the images only load once, as they enter the viewport.

The main logic of the component is within the onMount section. This sets up our observer, which is used to check our element to determine if it’s “intersecting” with the visible area of the screen. It also attaches a scroll event to check whether the element is visible as we scroll, and then it’ll remove this listener if we’ve determined that it is viable and that once is true.

Loading the images

Let’s use our <IntersectionObserver> component to conditionally load images by wrapping it around an <ImageLoader> component. Again, this is the component that receives a notification from the <IntersectionOberserver> so it knows it’s time to load an image.

That means we’ll need a new component file in components/Image. Let’s call it ImageLoader.svelte. Here’s the code we want in it:

<script>   export let src   export let alt 
   import IntersectionObserver from './IntersectionObserver.svelte'   import Image from './Image.svelte'    </script> 
 <IntersectionObserver once={true} let:intersecting={intersecting}>   {#if intersecting}     <Image {alt} {src} />   {/if} </IntersectionObserver>

This component takes some image-related props — src and alt — that we will use to create the actual markup for an image. Notice that we’re importing two components in the scripts section, including the <IntersectionObserver> we just created and another one called <Image> that we haven’t created yet, but will get to in a moment.

The <IntersectionObserver> is put to work by acting as a wrapping around the soon-to-be-created <Image> component. Check out those properties on it.  We are setting once to true, so the image only loads the first time we see it.

Then we make use of Svelte’s slot props. What are those? Let’s cover that next.

Slotting property values

Wrapping component, like our <IntersectionObserver> are handy for passing props to the children it contains. Svelte gives us something called slot props to make that happen.

In our <IntersectionObserver> component you may have noticed this line:

<slot {intersecting}></slot>

This is passing the intersecting prop into whatever component we give it. In this case, our <ImageLoader> component receives the prop when it uses the wrapper. We access the prop using let:intersecting={intersecting} like so:

<IntersectionObserver once={true} let:intersecting={intersecting}>

We can then use the intersecting value to determine when it’s time to load an <Image> component. In this case, we’re using an if condition to check for when it’s go time:

<IntersectionObserver once={true} let:intersecting={intersecting}>   {#if intersecting}     <Image {alt} {src} />   {/if} </IntersectionObserver> 

If the intersection is happening, the <Image> is loaded and receives the alt and src props. You can learn a bit more about slot props in this Svelte tutorial.

We now have the code in place to show an <Image> component when it is scrolled onto the screen. Let’s finally get to building the component.

Showing images on load

Yep, you guessed it: let’s add an Image.svelte file to the components/Image folder for our <Image> component. This is the component that receives our alt and src props and sets them on an <img> element.

Here’s the component code:

<script>   export let src   export let alt 
   import { onMount } from 'svelte' 
   let loaded = false   let thisImage 
   onMount(() => {     thisImage.onload = () => {       loaded = true     }   })  
 </script> 
 <style>   img {     height: 200px;     opacity: 0;     transition: opacity 1200ms ease-out;   }   img.loaded {     opacity: 1;   } </style> 
 <img {src} {alt} class:loaded bind:this={thisImage} />

Right off the bat, we’re receiving the alt and src props before defining two new variables: loaded to store whether the image has loaded or not, and thisImage to store a reference to the img DOM element itself.

We’re also using a helpful Svelte method called onMount. This gives us a way to call functions once a component has been rendered in the DOM. In this case, we’re set a callback for thisImage.onload. In plain English, that means it’s executed when the image has finished loading, and will set the loaded variable to a true value.

We’ll use CSS to reveal the image and fade it into view. Let’s give set an opacity: 0 on images so they are initially invisible, though technically on the page. Then, as they intersect the viewport and the <ImageLoader> grants permission to load the image, we’ll set the image to full opacity. We can make it a smooth transition by setting the transition property on image. The demo sets the transition time to 1200ms but you can speed it up or slow it down as needed.

That leads us to the very last line of the file, which is the markup for an <img> element.

<img {src} {alt} class:loaded bind:this={thisImage} />

This uses class:loaded to conditionally apply a .loaded class if the loaded variable is true. It also uses the bind:this method to associate this DOM element with the thisImage variable.

Let’s hook it all up!

Alright, it’s time to actually use our component. Crack open the App.svelte file and drop in the following code to import our component and use it:

<script>   import ImageLoader from './components/Image/ImageLoader.svelte'; </script> 
 <ImageLoader src="OUR_IMAGE_URL" alt="Our image"></ImageLoader>

Here’s the demo once again:

And remember that you’re welcome to download the complete code for this demo on GitHub. If you’d like to see this working on a production site, check out my Shop Ireland project. Lazy loading is used on the homepage, category pages and search pages to help speed things up. I hope you find it useful for your own Svelte projects!


The post Lazy Loading Images in Svelte appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , ,
[Top]

Fluid Images in a Variable Proportion Layout

Creating fluid images when they stand alone in a layout is easy enough nowadays. However, with more sophisticated interfaces we often have to place images inside responsive elements, like this card:

Screenshot. A horizontal card element with an image of two strawberries against a light blue background to the left of text that contains a heading and two sentences.

For now, let’s say this image is not semantic content, but only decoration. That’s a good use for background-image. And because in this context the image contains an object, we can’t allow any parts to be cropped out when it’s responsive, so we’d pick background-size: contain.

Here’s where it starts to get tricky: on mobile devices, this card shifts direction and becomes vertical, with the image on top. We can make that happen with any sort of CSS layout technique, and probably best handled with CSS grid or flexbox.

Screenshot. The same strawberry card but in a vertical format.

But as we test for smaller screens, because of the contain property, this is what we get:

The same card element in vertical but now the strawberry image is not flush with the top border of the card.
Hey, get back up there!

That’s not very nice. The image resizes to maintain its aspect ratio without cutting off any details, and if the image is important content and should not be cropped, we can’t change background-size to cover.

At this point, our next attempt might be familiar to you: placing the image inline, instead the background. 

On desktop, this works fine:

It’s not bad on mobile either:

But on smaller screens, because of all the fixed sizes, the image’s proportions get distorted.

Screenshot. The vertical card element with the strawberry image out of proportion, causing the strawberries to appear stretched vertically.
Hmm, those strawberries are not as appetizing when stretched.

We could spend hours fiddling with the image, the card, the flex properties, going back and forth. Or, we could…

Separate main content from the background

This is the base for obtaining much more flexibility and resilience when it comes to responsive images. It might not be possible 100% of the time but, in many cases, it can be achieved with a little effort on the design side of things, especially if this approach is planned beforehand.

For our next iteration, we’re placing our strawberries image on a transparent background and setting what was the blue color in the raster image with CSS instead. Go ahead and play with viewport sizes in this demo by adjusting the size of the sample space!

Looking deeper at the styles, notice that we’ve also added padding to the div that holds the image, so the strawberries don’t come too close to the edges. We have full control of how close or distant we want them to be, through this padding.

Note how we’re also using negative margins to compensate for the padding on our outer card wrapper, otherwise we’d get white space all around the image.

Use the object-fit property for inline images

As much as the previous demo works, we can still improve the approach. Up to now, we’ve assumed that the image was un-semantical content — but with this layout, it’s also likely that the image illustration could be more than decoration.

If that’s the case, we definitely don’t want the image to get cut off because that would essentially amount to data loss. It’s semantically better to put the image inline instead of a background to prevent that, and we can use the object-fit property to make it happen.

We’ve extracted the strawberries from the background and it’s now an inline <img> element, but we kept the background color in that same image div. 

Finally, combining the object-fit: contain with a 100% width makes it possible to resize the window and keep the aspect ratio of the strawberries. The caveat of this approach, however, is that we need to set a fixed height for the image on the desktop version — otherwise it’s going to follow the proportion of the set width (and reducing it will alter the layout). That might make things too constrained if we need to generate these cards with a variable amount of text that breaks into several lines.

Coming soon: aspect-ratio

The solution for the concern above might be just around the corner with the upcoming aspect-ratio property. This will enable setting a fixed ratio for an element, like this:

.el {   aspect-ratio: 16 / 9; }

This means we’ll be able to eliminate fixed height and replace it with our calculated aspect ratio. For example, the dimensions in the desktop breakpoint of our last example looked like this:

.image {   /* ... */   height: 184px;   width: 318px; }

With aspect-ratio, we could remove the height declaration and do the math to get the closest ratio that amounts to 184:

.image {   /* ... */   width: 318px; /*  Base width */   height: unset; /* Resets the height that was set outside the media query */   aspect-ratio: 159 / 92; /* Amounts close to a 184px height */ }

The upcoming property is better explored in this article, if you want to learn more about it.

In the end, there are multiple ways to achieve reliably responsive images in a variable proportion layout. However, the trick to make this job easier — and better — does not necessarily lie with CSS; it can be as simple as adapting your images, whether that’s by separating the foreground from background (like we did) or selecting specific images that will still work if a fair portion of the edges get cropped.

The post Fluid Images in a Variable Proportion Layout appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

A Guide to the Responsive Images Syntax in HTML

This guide is about the HTML syntax for responsive images (and a little bit of CSS for good measure). The responsive images syntax is about serving one image from multiple options based on rules and circumstances. There are two forms of responsive images, and they’re for two different things:

If your only goal is…

Increased Performance

Then what you need is…

<img   srcset=""   src=""   alt="" >

There is a lot of performance gain to be had by using responsive images. Image weight has a huge impact on pages’ overall performance, and responsive images are one of the best things that you can do to cut image weight. Imagine the browser being able to choose between a 300×300 image or a 600×600. If the browser only needs the 300×300, that’s potentially a 4× bytes-over-the-wire savings! Savings generally go up as the display resolution and viewport size go down; on the smallest screens, a couple of case studies have shown byte savings of 70–90%.

If you also need…

Design Control

Then what you need is…

<picture>   <source srcset="" media="">   <source srcset="" media="">   <img src="" alt=""> </picture>

Another perfectly legit goal with responsive images is not just to serve different sizes of the same image, but to serve different images. For example, cropping an image differently depending on the size of the screen and differences in the layout. This is referred to as “art direction.”

The <picture> element is also used for fallback image types and any other sort of media query switching (e.g. different images for dark mode). You get greater control of what browsers display.


There is a lot to talk about here, so let’s go through both syntaxes, all of the related attributes and values, and talk about a few related subjects along the way, like tooling and browsers.


Using srcset

The <img srcset="" src="" alt=""> syntax is for serving differently-sized versions of the same image. You could try to serve entirely different images using this syntax, but browsers assume that everything in a srcset is visually-identical and will choose whichever size they think is best, in impossible-for-you-to-predict ways. So I wouldn’t reccomend it.

Perhaps the easiest-possible responsive images syntax is adding a srcset attribute with x descriptors on the images to label them for use on displays with different pixel-densities.

<img    alt="A baby smiling with a yellow headband."   src="baby-lowres.jpg"   srcset="baby-highres.jpg 2x" >

Here, we’ve made the default (the src) the “low res” (1×) copy of the image. Defaulting to the smallest/fastest resources is usually the smart choice:. We also provide a 2× version. If the browser knows it is on a higher pixel-density display (the 2x part), it will use that image instead.

Demo
<img    alt="A baby smiling with a yellow headband."   src="baby-lowres.jpg"   srcset="     baby-high-1.jpg 1.5x,     baby-high-2.jpg 2x,     baby-high-3.jpg 3x,     baby-high-4.jpg 4x,     baby-high-5.jpg 100x   " >

You can do as many pixel-density variants as you like.

While this is cool and useful, x descriptors only account for a small percentage of responsive images usage. Why? They only let browsers adapt based on one thing: display pixel-density. A lot of times, though, our responsive images are on responsive layouts, and the image’s layout size is shrinking and stretching right along with the viewport. In those situations, the browser needs to make decisions based on two things: the pixel-density of the screen, and the layout size of the image. That’s where w descriptors and the sizes attribute come in, which we’ll look at in the next section.

Using srcset / w + sizes

This is the good stuff. This accounts for around 85% of responsive images usage on the web. We’re still serving the same image at multiple sizes, only we’re giving the browser more information so that it can adapt based on both pixel-density and layout size.

<img    alt="A baby smiling with a yellow headband."   srcset="     baby-s.jpg  300w,     baby-m.jpg  600w,     baby-l.jpg  1200w,     baby-xl.jpg 2000w   "   sizes="70vmin" >

We’re still providing multiple copies of the same image and letting the browser pick the most appropriate one. But instead of labeling them with a pixel density (x) we’re labelling them with their resource width, using w descriptors. So if baby-s.jpg is 300×450, we label it as 300w.

Using srcset with width (w) descriptors like this means that it will need to be paired with the sizes attribute so that the browser will know how large of a space the image will be displaying in. Without this information, browsers can’t make smart choices.

Demo

Creating accurate sizes

Creating sizes attributes can get tricky. The sizes attribute describes the width that the image will display within the layout of your specific site, meaning it is closely tied to your CSS. The width that images render at is layout-dependent rather than just viewport dependent!

Let’s take a look at a fairly simple layout with three breakpoints. Here’s a video demonstrating this:

Demo

The breakpoints are expressed with media queries in CSS:

body {   margin: 2rem;   font: 500 125% system-ui, sans-serif; } .page-wrap {   display: grid;   gap: 1rem;   grid-template-columns: 1fr 200px;   grid-template-areas:     "header header"     "main aside"     "footer footer"; }  @media (max-width: 700px) {   .page-wrap {     grid-template-columns: 100%;     grid-template-areas:       "header"       "main"       "aside"       "footer";   } } @media (max-width: 500px) {   body {     margin: 0;   } }

The image is sized differently at each breakpoint. Here’s a breakdown of all of the bits and pieces that affect the image’s layout width at the largest breakpoint (when the viewport is wider than 700px):

The image is as wide as 100vw minus all that explicitly sized margin, padding, column widths, and gap.
  • At the largest size: there is 9rem of explicit spacing, so the image is calc(100vw - 9rem - 200px) wide. If that column used a fr unit instead of 200px, we’d kinda be screwed here.
  • At the medium size: the sidebar is dropped below, so there is less spacing to consider. Still, we can do calc(100vw - 6rem) to account for the margins and padding.
  • At the smallest size: the body margin is removed, so just calc(100vw - 2rem) will do the trick.

Phew! To be honest, I found that a little challenging to think out, and made a bunch of mistakes as I was creating this. In the end, I had this:

<img    ...     sizes="     (max-width: 500px) calc(100vw - 2rem),      (max-width: 700px) calc(100vw - 6rem),     calc(100vw - 9rem - 200px)   " />

A sizes attribute that gives the browser the width of the image across all three breakpoints, factoring in the layout grid, and all of the surrounding gap, margin, and padding that end up impacting the image’s width.

Now wait! Drumroll! 🥁🥁🥁That’s still wrong. I don’t understand why exactly, because to me that looks like it 100% describes what is happening in the CSS layout. But it’s wrong because Martin Auswöger’s RespImageLint says so. Running that tool over the isolated demo reports no problems except the fact that the sizes attribute is wrong for some viewport sizes, and should be:

<img   ...   sizes="     (min-width: 2420px) 2000px,      (min-width: 720px) calc(94.76vw - 274px),      (min-width: 520px) calc(100vw - 96px),      calc(100vw - 32px)   " >

I don’t know how that’s calculated and it’s entirely unmaintainable by hand, but, it’s accurate. Martin’s tool programmatically resizes the page a bunch and writes out a sizes attribute that describes the actual, observed width of the image over a wide range of viewport sizes. It’s computers, doing math, so it’s right. So, if you want a super-accurate sizes attribute, I’d recommend just putting a wrong one on at first, running this tool, and copying out the correct one.

For an even deeper dive into all this, check out Eric Portis’ w descriptors and sizes: Under the hood.

Being more chill about sizes

Another option is use the Horseshoes & Hand Grenades Method™ of sizes (or, in other words, close counts).

For example, sizes="96vw" says, “This image is going to be pretty big on the page — almost the full width — but there will always be a little padding around the edges, so not quite. Or sizes="(min-width: 1000px) 33vw, 96vw" says, “This image is in a three-column layout on large screens and close to full-width otherwise.” Practicality-wise, this can be a sane solution.

You might find that some automated responsive image solutions, which have no way of knowing your layout, make a guess — something like sizes="(max-width: 1000px) 100vw, 1000px". This is just saying, “Hey we don’t really know much about this layout, but we’re gonna take a stab and say, worst case, the image is full-width, and let’s hope it never renders larger than 1000px”.

Abstracting sizes

I’m sure you can imagine how easy it is to not only get sizes wrong, but also have it become wrong over time as layouts change on your site. It may be smart for you to abstract it using a templating language or content filter so that you can change the value across all of your images more easily.

I’m essentially talking about setting a sizes value in a variable once, and using that variable in a bunch of different <img> elements across your site. Native HTML doesn’t offer that, but any back end language does; for instance, PHP constants, Rails config variables, the React context API used for a global state variable, or variables within a templating language like Liquid can all be used to abstract sizes.

<?php   // Somewhere global   $ my_sizes = ""; ?>  <img   srcset=""   src=""   alt=""   sizes="<?php echo $ my_sizes; ?>" />

“Browser’s choice”

Now that we have a sizes attribute in place, the browser knows what size (or close to it) the image will render at and can work its magic. That is, it can do some math that factors in the pixel density of the screen, and the size that the image will render at, then pick the most appropriately-sized image.

The math is fairly straightforward at first. Say you’re about to show an image that is 40vw wide on a viewport that is 1200px wide, on a 2x pixel-density screen. The perfect image would be 960 pixels wide, so the browser is going to look for the closest thing it’s got. The browser will always calculate a target size that it would prefer based on the viewport and pixel-density situations, and what it knows from sizes, and compare that target to what it’s got to pick from in srcset. How browsers do the picking, though, can get a little weird.

A browser might factor more things into this equation if it chooses to. For example, it could consider the user’s current network speeds, or whether or not the user has flipped on some sort of “data saver” preference. I’m not sure if any browsers actually do this sort of thing, but they are free to if they wish as that’s how the spec was written. What some browsers sometimes choose to do is pull from cache. If the math shows they should be using a 300px image, but they already have a 600px in local cache, they will just use that. Smart. Room for this sort of thing is a strength of the srcset/sizes syntax. It’s also why you always use different sizes of the same image, within srcset: you’ve got no way to know which image is going to be selected. It’s the browser’s choice.

This is weird. Doesn’t the browser already know this stuff?

You might be thinking, “Uhm why do I have to tell the browser how big the image will render, doesn’t it know that?” Well, it does, but only after it’s downloaded your HTML and CSS and laid everything out. The sizes attribute is about speed. It gives the browser enough information to make a smart choice as soon as it sees your <img>.

<img   data-sizes="auto"   data-srcset="     responsive-image1.jpg 300w,     responsive-image2.jpg 600w,     responsive-image3.jpg 900w"   class="lazyload"  />

Now you might be thinking, “But what about lazy-loaded images?” (as in, by the time a lazy-loaded image is requested, layout’s already been done and the browser already knows the image’s render size). Well, good thinking! Alexander Farkas’ lazysizes library writes out sizes attributes automatically on lazyload, and there’s an ongoing discussion about how to do auto-sizes for lazy-loaded images, natively.

sizes can be bigger than the viewport

Quick note on sizes. Say you have an effect on your site so that an image “zooms in” when it’s clicked. Maybe it expands to fill the whole viewport, or maybe it zooms even more, so that you can see more detail. In the past, we might have had to swap out the src on click in order to switch to a higher-res version. But now, assuming a higher-res source is already in the srcset, you can just change the sizes attribute to something huge, like 200vw or 300vw, and the browser should download the super-high-res source automatically for you. Here’s an article by Scott Jehl on this technique.

↩️ Back to top


Using <picture>

Hopefully, we’ve beaten it into the ground that <img srcset="" sizes="" alt=""> is for serving differently-sized versions of the same image. The <picture> syntax can do that too, but the difference here is that the browser must respect the rules that you set. That’s useful when you want to change more than just the resolution of the loaded image to fit the user’s situation. This intentional changing of the image is usually called “art direction.”

Art Direction

<picture>   <source      srcset="baby-zoomed-out.jpg"     media="(min-width: 1000px)"   />   <source      srcset="baby.jpg"     media="(min-width: 600px)"   />   <img      src="baby-zoomed-in.jpg"      alt="Baby Sleeping"   /> </picture>

This code block is an example of what it might look like to have three stages of an “art directed” image.

  • On large screens, show a zoomed-out photo.
  • On medium screens, show that same photo, zoomed in a bit.
  • On small screens, zoom in even more.

The browser must respect our media queries and will swap images at our exact breakpoints. That way, we can be absolutely sure that nobody on a small screen will see a tiny, zoomed-out image, which might not have the same impact as one of the zoomed-in versions.

Here’s a demo, written in Pug to abstract out some of the repetitive nature of <picture>.

Art direction can do a lot more than just cropping

Although cropping and zooming like this is the most common form of art direction by far, you can do a lot more with it. For instance, you can:

Sky’s the limit, really.

Combining source and srcset

Because <source> also uses the srcset syntax, they can be combined. This means that you can still reap the performance benefits of srcset even while swapping out visually-different images with <source>. It gets pretty verbose though!

<picture>   <source      srcset="       baby-zoomed-out-2x.jpg 2x,       baby-zoomed-out.jpg     "     media="(min-width: 1000px)"   />   <source      srcset="       baby-2x.jpg 2x,       baby.jpg     "     media="(min-width: 600px)"   />   <img      srcset="       baby-zoomed-out-2x.jpg 2x     "     src="baby-zoomed-out.jpg"     alt="Baby Sleeping"   /> </picture>

The more variations you create and the more resized versions you create per variation, the more verbose this code has to get.

Fallbacks for modern image formats

The <picture> element is uniquely suited to being able to handle “fallbacks.” That is, images in cutting-edge formats that not all browsers might be able to handle, with alternative formats for browsers that can’t load the preferred, fancy one. For example, let’s say you want to use an image in the WebP format. It’s a pretty great image format, often being the most performant choice, and it’s supported everywhere that the <picture> element is, except Safari. You can handle that situation yourself, like:

<picture>   <source srcset="party.webp">   <img src="party.jpg" alt="A huge party with cakes."> </picture>

This succeeds in serving a WebP image to browsers that support it, and falls back to a JPEG image, which is definitely supported by all browsers.

Here’s an example of a photograph (of me) at the exact same size where the WebP version is about 10% (!!!) of the size of the JPEG.

How do you create a WebP image? Well, it’s more of a pain in the butt than you’d like it to be, that’s for sure. There are online converters, command line tools, and some modern design software, like Sketch, helps you export in that format. My preference is to use an image hosting CDN service that automatically sends images in the perfect format for the requesting browser, which makes all this unnecessary (because you can just use img/srcset).

WebP isn’t the only player like this. Safari doesn’t support WebP, but does support a format called JPG 2000 which has some advantages over JPEG. Internet Explorer 11 happens to support an image format called JPEG-XR which has different advantages. So to hit all three, that could look like:

<picture>   <source srcset="/images/cereal-box.webp" type="image/webp" />   <source srcset="/images/cereal-box.jp2" type="image/jp2" />   <img src="/images/cereal-box.jxr" type="image/vnd.ms-photo" /> </picture>

This syntax (borrowed form a blog post by Josh Comeau) supports all three of the “next-gen” image formats in one go. IE 11 doesn’t support the <picture> syntax, but it doesn’t matter because it will get the <img> fallback which is in the JPEG-XR format it understands.

Estelle Weyl also covered this idea in a 2016 blog post on image optimization.

↩️ Back to top


Where do you get the differently-sized images?

You can make them yourself. Heck, even the free Preview app on my Mac can resize an image and “Save As.”

The Mac Preview app resizing an image, which is something that literally any image editing application (including Photoshop, Affinity Designer, Acorn, etc.) can also do. Plus, they often help by exporting the variations all at once.

But that’s work. It’s more likely that the creation of variations of these images is automated somehow (see the section below) or you use a service that allows you to create variations just by manipulating the URL to the image. That’s a super common feature of any image hosting/image CDN service. To name a few:

Not only do these services offer on-the-fly image resizing, they also often offer additional stuff, like cropping, filtering, adding text, and all kinds of useful features, not to mention serving assets efficiently from a CDN and automatically in next-gen formats. That makes them a really strong choice for just about any website, I’d say.

Here’s Glen Maddern in a really great screencast talking about how useful Image CDNs can be:

Design software is booming more aware that we often need multiple copies of images. The exporting interface from Figma is pretty nice, where any given selection can be exported. It allows multiple exports at once (in different sizes and formats) and remembers what you dod the last time you exported.

Exporting in Figma

Automated responsive images

The syntax of responsive images is complex to the point that doing it by hand is often out of the question. I’d highly recommend automating and abstracting as much of this away as possible. Fortunately, a lot of tooling that helps you build websites knows this and includes some sort of support for it. I think that’s great because that’s what software should be doing for us, particularly when it is something that is entirely programmatic and can be done better by code than by humans. Here are some examples…

  • Cloudinary has this responsive breakpoints tool including an API for generating the perfect breakpoints.
  • WordPress generates multiple versions of images and outputs in the responsive images syntax by default.
  • Gatsby has a grab-bag of plugins for transforming and implementing images on your site. You ultimately implement them with gatsby-image, which is a whole fancy thing for implementing responsive images and other image loading optimizations. Speaking of React, it has component abstractions like “An Almost Ideal React Image Component” that also does cool stuff.
  • Nicolas Hoizey’s Images Responsiver Node module (and it’s Eleventy plugin) makes a ton of smart markup choices for you, and pairs nicely with a CDN that can handle the on-the-fly resizing bits.
  • These are just a few examples! Literally anything you can do to make this process easier or automatic is worth doing.
Here’s me inspecting an image in a WordPress blog post and seeing a beefy srcset with a healthy amount of pre-generated size options and a sizes attribute tailored to this theme.
A landing page for gatsby-image explaining all of the additional image loading stuff it can do.

I’m sure there are many more CMSs and other software products that help automate away the complexities of creating the responsive images syntax. While I love that all this syntax exists, I find it all entirely too cumbersome to author by hand. Still, I think it’s worth knowing all this syntax so that we can build our own abstractions, or check in on the abstractions we’re using to make sure they are doing things correctly.

Related concepts

  • The object-fit property in CSS controls how an image will behave in its own box. For example, an image will normally “squish” if you change the dimensions to something different than its natural aspect ratio, but object-fit can be used to crop it or contain it instead.
  • The object-position property in CSS allows you to nudge an image around within its box.

What about responsive images in CSS with background images?

We’ve covered exactly this before. The trick is to use @media queries to change the background-image source. For example:

.img {   background-image: url(small.jpg); } @media    (min-width: 468px),   (-webkit-min-device-pixel-ratio: 2),    (min-resolution: 192dpi) {   .img {     background-image: url(large.jpg);   } }

With this CSS syntax, depending on the browser conditions, the browser will only download one of the two images, which achieves the same performance goal that the responsive images syntax in HTML does. If it helps, think of the above as the CSS equivalent of the <picture> syntax: the browser must follow your rules and display what matches.

If you’re looking to let the browser choose the best option, like srcset/sizes, but in CSS, the solution is ultimately going to be the image-set() function. There’s two problems with image-set(), today, though:

  • Support for it isn’t there yet. Safari’s implementation leads the pack, but image-set() has been prefixed in Chrome for eight years, and it’s not there at all in Firefox.
  • Even the spec itself seems behind the times. For example, it only supports x descriptors (no w, yet).

Best to just use media queries for now.

Do you need to polyfill?

I’m pretty meh on pollyfilling any of this right this moment. There is a great polyfill though, called Picturefill, which will buy you full IE 9-11 support if you need that. Remember, though, that none of this stuff breaks to the point of not displaying any image at all in non-supporting browsers, assuming you have an <img src="" alt=""> in there somewhere. If you make the (fairly safe) assumption that IE 11 is running on a low-pixel-density desktop display, you can make your image sources reflect that by default and build out from there.

Other important image considerations

  • Optimizing quality: The point of responsive images is loading the smallest, most impactful resource that you can. You can’t achieve that without effectively compressing your image. You’re aiming for a “sweet spot” for every image, between looking good and being light. I like to let image hosting services solve this problem for me, but Etsy has a really great writeup of what they’ve been able to accomplish with infrastructure that they built themselves.
  • Serving from CDNs: Speaking of image hosting services, speed comes in many forms. Fast servers that are geographically close to the user are an important speed factor as well.
  • Caching: What’s better than loading less data over the network? Loading no data at all! That’s what HTTP caching is for. Using the Cache-Control header, you can tell the browser to hang on to images so that if the same image is needed again, the browser doesn’t have to go over the network to get it, which is a massive performance boost for repeat viewings.
  • Lazy loading: This is another way to avoid loading images entirely. Lazy loading means waiting to download an image until it is in or near the viewport. So, for example, an image way far down the page won’t load if the user never scrolls there.

Other good resources

(That I haven’t linked up in the post already!)

Browser Support

This is for srcset/sizes, but it’s the same for <picture>.

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

Desktop

Chrome Firefox IE Edge Safari
38 38 No 16 9

Mobile / Tablet

Android Chrome Android Firefox Android iOS Safari
81 68 81 9.0-9.2

The post A Guide to the Responsive Images Syntax in HTML appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

Add Beautiful Images with the Unsplash API

Perhaps you know Unsplash? I’d wager it’s the most popular stock photography site out there for two big reasons:

  1. Every photo on there is pretty darn nice
  2. Every photo is entirely free even for commercial use. You don’t have to ask permission or even credit it (although that’s appreciated).

Here’s something you might not know though: Unsplash has an API, and it’s unlimited and free. Brass tacks: it’s exactly what you hope it’s going to be. A really clean, well documented, well-performing, JSON API that gives you URLs to photos with metadata.

What would you use the Unsplash API for?

There are lots of examples on Unsplash’s developer area, from Medium to Squarespace to Trello, but here is another one of my favorites!

I use Notion every day. It’s a great app for note-taking, planning, and all sorts of stuff. One of the features it has is giving every document you create within it a custom image header. These give the documents some great personality. Notion has a handful you can choose from or you can upload your own. Or, you can search Unsplash for them!

How does that work? Lemme show you first:

They use the Unsplash API to do it and here’s an article about that. There is a search endpoint as part of the API that makes this quite easy to do.

For example, you’d hit a URL like:

https://api.unsplash.com/search/photos?page=1&query=SEARCH_QUERY

And you’ll get JSON back like:

{   "total": 133,   "total_pages": 7,   "results": [     {       "id": "eOLpJytrbsQ",       "created_at": "2014-11-18T14:35:36-05:00",       "width": 4000,       "height": 3000,       "color": "#A7A2A1",       "likes": 286,       "liked_by_user": false,       "description": "A man drinking a coffee.",       "user": {         "id": "Ul0QVz12Goo",         "username": "ugmonk",         "name": "Jeff Sheldon",         "first_name": "Jeff",         "last_name": "Sheldon",         "instagram_username": "instantgrammer",         "twitter_username": "ugmonk",         "portfolio_url": "http://ugmonk.com/",         "profile_image": {           "small": "https://images.unsplash.com/profile-1441298803695-accd94000cac?ixlib=rb-0.3.5&q=80&fm=jpg&crop=faces&cs=tinysrgb&fit=crop&h=32&w=32&s=7cfe3b93750cb0c93e2f7caec08b5a41",           "medium": "https://images.unsplash.com/profile-1441298803695-accd94000cac?ixlib=rb-0.3.5&q=80&fm=jpg&crop=faces&cs=tinysrgb&fit=crop&h=64&w=64&s=5a9dc749c43ce5bd60870b129a40902f",           "large": "https://images.unsplash.com/profile-1441298803695-accd94000cac?ixlib=rb-0.3.5&q=80&fm=jpg&crop=faces&cs=tinysrgb&fit=crop&h=128&w=128&s=32085a077889586df88bfbe406692202"         },         "links": {           "self": "https://api.unsplash.com/users/ugmonk",           "html": "http://unsplash.com/@ugmonk",           "photos": "https://api.unsplash.com/users/ugmonk/photos",           "likes": "https://api.unsplash.com/users/ugmonk/likes"         }       },       "current_user_collections": [],       "urls": {         "raw": "https://images.unsplash.com/photo-1416339306562-f3d12fefd36f",         "full": "https://hd.unsplash.com/photo-1416339306562-f3d12fefd36f",         "regular": "https://images.unsplash.com/photo-1416339306562-f3d12fefd36f?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&s=92f3e02f63678acc8416d044e189f515",         "small": "https://images.unsplash.com/photo-1416339306562-f3d12fefd36f?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=400&fit=max&s=263af33585f9d32af39d165b000845eb",         "thumb": "https://images.unsplash.com/photo-1416339306562-f3d12fefd36f?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=200&fit=max&s=8aae34cf35df31a592f0bef16e6342ef"       },       "links": {         "self": "https://api.unsplash.com/photos/eOLpJytrbsQ",         "html": "http://unsplash.com/photos/eOLpJytrbsQ",         "download": "http://unsplash.com/photos/eOLpJytrbsQ/download"       }     },     // more photos ...   ] }

So to offer a search experience inside an app like Notion, you’d have a little search form and when users submit that search query, you’d hit the API with the value they entered, then loop over response.results using the response.results.urls.thumb to show the images returned. If the user picks one, you can use a higher-res URL to do something with and have access to all that photos metadata.

Hot tip! The URLs to photos are dynamic in that you can resize them, crop them, serve them in different formats, and even change the compression quality all from URL parameters. For example, changing size is like &w=200.

That is exactly what we do on CodePen

The purpose of CodePen Pen Editor is to provide an online code editor that makes it tremendously easy to code something up for the web, save it, and share it. Images are a big part of the web, so it’s very possible that you might want to use a gorgeous image in a Pen. We have Asset Hosting ourselves on CodePen as a PRO feature, but we also offer Unsplash images to everyone for free!

Check out how it works:

A basic example in React

  • Let’s make a search <form>, when submitted, it hits the Unsplash API and returns a bunch of photos.
  • We’ll use Superagent for the Ajax just to make a smidge easier.
  • We’ll track the current search query and returned data in state.

Here is that working!

How might you use that in your own app?

  • Does your app allow users to create anything? If so, could those things be enhanced by great photos? For example, cover images, background images, images for blog posts, etc. Check out existing partners for more ideas.
  • Could this be part of an avatar-choosing experience?
  • Maybe you could build a plugin that enhances some existing app by allowing quicker access to photos.

Feel free to leave comments with more ideas or how you have used the API. And if you haven’t, try it out.

The post Add Beautiful Images with the Unsplash API appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]