Introduction
JPEG XL was developed to meet the demands of web delivery and professional photography. In addition to supporting wide color gamut, it supports images with high dynamic range and high bit depth. To help a wide range of use cases, JPEG XL offers a range of features, including animation, alpha channels, layers, thumbnails, and lossless and progressive coding. In addition to providing improved image quality and compression ratios, JPEG XL also has a shorter specification. Encoding and decoding are computationally efficient when using software implementations without requiring additional hardware acceleration.
In this tutorial, you will see how easy it is to use JPEG XL images through HTML. HTML provides a media-type hint which enables the browser to find the correct version of the image for you. This is not an option available in CSS. It is still possible to support JPEG XL, AVIF, and WebP with a simple Javascript function. Our detection script utilizes classes to manipulate the HTML elements on your page.
We must remember that the first rule does not win in CSS, but the last rule prevails. We have created a script that checks JPEG XL, AVIF, and WebP support by loading a JPEG XL-encoded 1x1 pixel image. If the browser successfully loads the JXL image, the HTML element receives a "jxl" class. If no JXL support is detected, the browser checks for AVIF support and adds an "avif" class. If the browser does not load the AVIF image, the script will further check the browser's support for WebP using the method described above. If your browser successfully loads the image, the HTML element gets a "webp" class. If it is doubtful that your browser will not pass this test, the HTML element will not get any class at all. Obviously, the AVIF and WebP detection are entirely optional, and you can remove them according to your preferences.
Detect Browser Support
The following code can be added as an inline script in your header above any CSS content to avoid visible image changes when adding the jxl/avif/webp class to your DOM. There are only 1100 bytes in this script, and it takes approximately five milliseconds to load, which is sufficient for the purpose it serves.
1function addClass(format) {
2 document.documentElement.classList.add(format);
3}
4var jxl = new Image();
5(jxl.src =
6 "data:image/jxl;base64,/woIELASCAgQAFwASxLFgkWAHL0xqnCBCV0qDp901Te/5QM="),
7(jxl.onload = function() {
8 addClass("jxl");
9}),
10(jxl.onerror = function() {
11 var avif = new Image();
12 (avif.src =
13 "data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A="),
14 (avif.onload = function() {
15 addClass("avif");
16 }),
17 (avif.onerror = function() {
18 var webp = new Image();
19 (webp.src =
20 "data:image/webp;base64,UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA=="),
21 (webp.onload = function() {
22 addClass("webp");
23 });
24 });
25});
Minified version:
1function addClass(A){document.documentElement.classList.add(A)}var jxl=new Image;jxl.src="data:image/jxl;base64,/woIELASCAgQAFwASxLFgkWAHL0xqnCBCV0qDp901Te/5QM=",jxl.onload=function(){addClass("jxl")},jxl.onerror=function(){var A=new Image;A.src="data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A=",A.onload=function(){addClass("avif")},A.onerror=function(){var A=new Image;A.src="data:image/webp;base64,UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA==",A.onload=function(){addClass("webp")}}};
This will result in the following HTML code to use:
1<html></html> <!-- no class at all, you have to use jpg (oh god)-->
2<html class="webp"></html> <!-- you can use webp -->
3<html class="avif"></html> <!-- you can use avif -->
4<html class="jxl"></html> <!-- you can use jxl (yeah!) -->
CSS Code Snippet
Once implemented, we can use the following CSS due to the high-level classes and cascading.
1.img {
2 background-image: url("image.jpg");
3}
4.webp .img {
5 background-image: url("image.webp");
6}
7.avif .img {
8 background-image: url("image.avif");
9}
10.jxl .img {
11 background-image: url("image.jxl");
12}
SCSS Background mixin
Here is an SCSS background mixin that can be used to support JXL, AVIF and WebP including Retina support.
1@mixin bg-url(
2 $url,
3 $url2x: false,
4 $webp1x: false,
5 $webp2x: false,
6 $avif1x: false,
7 $avif2x: false,
8 $jxl1x: false,
9 $jxl2x: false
10) {
11 background-image: url($url);
12 @if $webp1x {
13 .webp & {
14 background-image: url($webp1x);
15 }
16 }
17 @if $avif1x {
18 .avif & {
19 background-image: url($avif1x);
20 }
21 }
22 @if $jxl1x {
23 .jxl & {
24 background-image: url($jxl1x);
25 }
26 }
27@if $url2x {
28 @media screen and (-webkit-min-device-pixel-ratio: 2),
29 screen and (min-resolution: 192dpi),
30 screen and (min-resolution: 2dppx) {
31 background-image: url($url2x);
32@if $webp2x {
33 .webp & {
34 background-image: url($webp2x);
35}
36}
37@if $avif2x {
38 .avif & {
39 background-image: url($avif2x);
40}
41}
42@if $jxl2x {
43 .jxl & {
44 background-image: url($jxl2x);
45}}}}}
Future JPEG XL Detection
According to the World Wide Web Consortium (W3C), we will define the image type using the CSS Images Module Level 4 in the future. We will be able to specify different image formats using the image-set property. The browser will render the first image format it supports. We demonstrate below how type() can be used to deliver multiple images. Type declarations are a feature that is unique to CSS4. 90% of browsers currently support image-set but do not yet support type declarations. In the absence of browser support for this feature, we must rely on JavaScript to detect the presence of JXL, AVIF, and WebP support.
1background-image:
2 image-set(
3 "zebra.jxl" type("image/jxl"),
4 "zebra.avif" type("image/avif"),
5 "zebra.webp" type("image/webp"),
6 "zebra.png" type("image/png")
7 );
Post CSS Plugin
Joan Leon authored a tiny JS script (150B) and PostCSS plugin to use JPEG XL as a CSS background. This PostCSS Plugin lets you easily create CSS backgrounds that use JPEG XL images and fall back to the original image in unsupported browsers. NPM and Github packages are available for common JS and ES6. You can find it here: JXL in CSS PostCSS Plugin by Joan.
A code snippet like this
1.img {
2 background-image: url(img.jpg);
3}
will generate a JXL class on the HTML and a CSS output like this:
1body.jxl .logo {
2 background-image: url(img.jxl);
3}
4body.no-jxl .logo {
5 background-image: url(img.jpg);
6}