Srcset and the Kirby image tag: what about file names and sizes

I’ve followed the example to extend the image tag with a srcset attribute.


And I’ve set up some defaults in the config file, so far so good.

But the src file name, with this set up, doesn’t correspond with any of the names in the srcset:
<img src="…/my-image.jpg" srcset="…/my-image-400x.jpg 400w, …/my-image-800x.jpg 800w">

As I understand the src file name should correspond with the largest file? So, should both be my-image-800x.jpg. If that’s correct, how do you deal with that to prevent extra downloads of images?

Also I believe the real ‘srcset magic’ happens when sizes are supplied? Any suggestions on how to deal with that with editors in mind and using the image tag?

Imho I think srcset is a pita. Adding a ‘resize()’ in the Kirby image tag is likely a better way to go …
Any, thoughts, best practices? Thanks

The URL in the src attribute is determined by the first value in the Html::img call:

// build a new image tag with the srcset
$image = Html::img($tag->src, [
	'width'  => $tag->width,
	'height' => $tag->height,
	'class'  => $tag->imgclass,
	'title'  => $tag->title,
	'alt'    => $tag->alt ?? ' ',
	'srcset' => $file->srcset($srcset),
]);

If you replace $tag->src with $file->thumb()->url() and add the desired values for the thumb() method (e.g. $file->thumb(['width' => 800])->url() or the name of a preset, that image will be set in the src attribute. If these are identical to one of the srcset sizes’ settings, the same file should be used.

1 Like

Interesting, thank you, I’ll give that a spin.

Do you think srcset is useful without sizes?
sizes="(max-width: 600px) 400px, 800px"

I’m not interested in offering images with x-descriptors.

When using srcset without the sizes attribute, the browser assumes it is being displayed at full width (covering the viewport from left to right). It then does its calculations to decide which file to load. So no need for a sizes attribute in this case. So not including a sizes attribute is the same as writing sizes="100vw" [ref.]

As soon as your image is not displayed at full width, the sizes attribute tells the browser how to do its math. So with sizes="(max-width: 600px) 400px, 800px" you are essentially telling the browser that your layout shows the image at max. 400px on small screens and even on bigger screens it does not get bigger than 800px (e.g. a max-width CSS rule).

I would in this case also include a 1600px image in the srcset (not in the sizes, but since the browser doubles these values if it knows it’s dealing with a Retina screen, it would look for a 1600w URL). Browsers on devices with high dpi resolution would use the 800px image on viewports up to 600px (it reads your rule and then calculates that it needs double the resolution) and accordingly load a 1600px image for wider screens if available.

This is indeed one of the more abstract HTML features, but it is great for optimizing performance and keeping data transfer small. MDN has a rather comprehensive article on it.

1 Like

Yes, I know the article very well by now :wink: thanks. I’m just getting confused when I see Kirby examples without the full ‘picture’ so to speak. The html validator warned me though.

I tried your code example and that works fine. I just hard coded the sizes in the image tag, is that ok?

How do you test srcset? In chrome > properties > img > currentSrc: seems to always show the large image.

Heh :smiley:

Might be a bit risky, in case the image has not always the same dimensions. A safer way would be to do as follows, which gives you the actual dimensions of that thumb image:

// create a variable for the default fallback image
$default = $file->thumb(['width' => 800]);

// build a new image tag with the srcset
$image = Html::img($default->url(), [
	'width'  => $default->width(),
	'height' => $default->height(),
	'class'  => $tag->imgclass,
	'title'  => $tag->title,
	'alt'    => $tag->alt ?? ' ',
	'srcset' => $file->srcset($srcset),
]);

…interestingly, ->width(), ->height() and ->dimensions() are not documented on the $file reference page, as pointed out before; not sure is that intentional (friendly ping: @texnixe)

Sorry, I don’t have a universal answer to that question. While working on a layout template, I commonly use different placeholder images that help me see at one glance that the right size gets loaded (makes testing in different devices faster, as I do not need any extra tools). In Firefox, right-click → copy URL at least gives the actually loaded URL.

Nope, that’s an inheritance problem, since the stuff is pulled from PHP reflections.

1 Like

srcset and sizes in Kirby image tag
Just in case anybody is dealing with srcset and sizes in a Kirby image tag: I’ll show you all the bits and pieces I’ve ended up with. With thanks to @sebastiangreger.

plugins/image/index:

<?php

$originalTag = Kirby\Text\KirbyTag::$types['image'];
Kirby::plugin('your/image', [
    'tags' => [
        'image' => [
            'attr' => array_merge(
                $originalTag['attr'],
                [
                    'srcset',
                    'sizes'
                ]),

            'html' => function($tag) use ($originalTag)  {

                $file    = $tag->file($tag->value());
                $srcset  = $tag->srcset;
                $srcset  = option('thumbs.srcsets.' . $tag->srcset );
                $sizes   = option('srcset-sizes.' . $tag->sizes );
                $result  = $originalTag['html']($tag);

                if (! $file === true || is_null($srcset) === true || is_null($sizes) === true) {
                    return $result;
                }

                $pattern = '/<img.*?>/i';

                // src image with sizes and quality (if any) in filename
                $src = $file->thumb('large')->url();

                // build a new image tag with the srcset and sizes
                $image = Html::img($src, [
                    'width'  => $tag->width,
                    'height' => $tag->height,
                    'class'  => $tag->imgclass,
                    'title'  => $tag->title,
                    'alt'    => $tag->alt ?? ' ',
                    'srcset' => $file->srcset($srcset),
                    'sizes'  => $sizes
                ]);

                // replace the old image tag
                $result = preg_replace($pattern, $image , $result);

                return $result;
            }
        ]
    ]
]);

site/config/config (to be specified: different srcsets and sizes for smaller images in columns for example, the srcset and sizes underneath are just a quick example, use your own)

'thumbs' => [

   'presets' => [
    'small'  => ['width' => 400,  'quality' => 75],
    'normal' => ['width' => 800,  'quality' => 75],
    'large'  => ['width' => 1200, 'quality' => 75],  // for use in src
    'xlarge' => ['width' => 1600, 'quality' => 75]
  ]

  'srcsets' => [
    'default' => [
      '400w'  => ['width' => 400, 'quality' => 75],
      '800w'  => ['width' => 800, 'quality' => 75],
      '1200w' => ['width' => 1200, 'quality' => 75],
      '1600w' => ['width' => 1600, 'quality' => 75]
    ]
     'columns' => [  ],
  ],
],

'srcset-sizes' => [
  'default' => '(max-width: 500px) 400px, (max-width: 800px) 800px',
  'columns' => ' ',
],

Kirby image tag (both srcset and sizes should be specified):

(image:my-image.jpg srcset:default sizes:default)

Result (corresponding filenames for src and srcset and sizes):

<img alt="Image alt text here" 
  
  sizes="(max-width: 500px) 400px, (max-width: 799px) 800px" 
  
  src="http://…/media/…/my-image-1200x-q75.jpg" 
  
  srcset="http://…/media/…/my-image-400x-q75.jpg 400w, 
  http://…/media/…/my-image-800x-q75.jpg 800w, 
  http://…/media/…/my-image-1200x-q75.jpg 1200w, 
  http://…/media/…/my-image-1600x-q75.jpg 1600w
">
4 Likes

Thank you so much for this clear explanation! Now I finally understand how the browser uses srcset and sizes to choose an image. Fantastic!