A flexible, responsive image component for Kirby 2, featuring lazy-loading, fancy placeholders and much more.
A flexible, responsive image component for Kirby CMS, featuring lazy loading, fancy placeholders and more.
NOTE: This plugin was realeased under a commercial license at first. As of October 2nd, 2019, the code has been re-issued under the MIT license. As I have stopped sales and development, please note that I do not offer support any longer. If you want to use this plugin with a legacy Kirby 2-website, just use this license key to get rid of any limitations of the trial version: IMGST1-00000000000000000000000000000000
.
Table of Contents
<picture>
element and srcset
-attribute).*) Lazy loading uses the very performant lazysizes script and requires JavaScript to be activated on the client. However, the plugin provides a <noscript>
-fallback which is enabled by default.
ImageSet has been tested in the following browsers, but should work with any browser that supports modern web standards:
IE | Edge | Firefox | Safari (OS X) | Safari (iOS) | Chrome | Opera | Opera mini |
---|---|---|---|---|---|---|---|
11+ | ✓ | ✓* | 9+ | 9+ | ✓ | ✓ | ✓** |
*) Includes Firefox ESR
**) Does not work with dynamic content (e.g. widgets loaded via XHR/AJAX) due to the limited JavaScript support in Opera mini.
ImageSet works best with high-resolution source images, so they can be scaled according to your needs. This way, your content will also be more future-proof as in a couple of years, new devices might demand for images with even higher resolution. This also ensures an easier transition if new image formats like WebP become more popular. Make sure, that your server can handle scaling of thus large image files, though. If you use the GD library (Kirby’s default setting), also make sure that your server provides enough RAM to PHP, so large-sized images can be processed.
If you’re using the Kirby CLI, you need to cd
to the root directory of your Kirby installation and run the following command:
kirby plugin:install fabianmichael/kirby-imageset
This will download and copy ImageSet into site/plugins/imageset
.
To install this plugin as a git submodule, execute the following command from the root of your Kirby project:
git submodule add https://github.com/fabianmichael/kirby-imageset.git site/plugins/imageset
imageset
and copy it into the site/plugins/
directory in your Kirby project.In order to make ImageSet work properly, you have to include the corresponding CSS and JavaScript files to your templates. Add the following lines to your header snippet:
<!-- replaces "no-js" class on html element with "js" -->
<script>(function(d,c,r){d[c]=r.test(d[c])?d[c].replace(r,'$1js$2'):[d[c],'js'].join(' ')})(document.documentElement,'className',/(?:(^|\s)no-js(\s|$))/)</script>
<?= css('assets/plugins/imageset/css/imageset.min.css') ?>
If you use lazy loading, also add the following line anywhere after the previous code in your template, it is recommended to include the file before the closing </head>
tag to ensure that it loads as fast as possible. If this is not possible, you can also include the script right before the closing body tag, but this leads to later execution and can easily result in the flashing of un-rendered placeholders.
<?= js('assets/plugins/imageset/js/dist/imageset.min.js') ?>
Not all browsers are providing native support for the <picture>
element. If your site does not already include a polyfill for this, you might also want to add the following lines within the <head>
of your site to load the respimage polyfill conditionally and as fast as possible.
<script>
(function(w, d){
function loadJS(u){var r = d.getElementsByTagName("script")[0], s = d.createElement("script");s.src = u;r.parentNode.insertBefore( s, r );}
if(!window.HTMLPictureElement){
d.createElement('picture');
loadJS("<?= url('assets/plugins/imageset/js/dist/respimage.min.js') ?>");
}
})(window, document);
</script>
Alright, your site should now be prepared for using ImageSet! :-D
ℹ️ For a dev environment or low-traffic sites, it is completely fine to link plugin assets directly. However, if performance is critical for your project, it is highly recommended to place a copy of the JS and CSS files linked above in your assets directory and link to them or to bundle up these assets with your regular CSS and JavaScript using the build tool of your choice. |
The settings listed below are meant to be defined in your config.php
and change the global behavior of the plugin. To learn about configuring the default behavior of image sets in your templates (lazy loading, placeholders, fallback behavior etc.), have a look at 4.3.1 Available Options.
Option | Default value | Possible values | Description |
---|---|---|---|
imageset.license |
'' |
— | Enter your license code here, once your site goes live. See the License section of this document for more information. |
imageset.styles.consolidate |
false |
true , false |
If you use image sets with multiple aspect-ratios, the plugin needs an inline <style> element for each of those. This is common practice in web design and works in every browser, but throws a validation error as this is not officially part of the HTML5 spec. Enabling this setting will consolidate all inline style decorations and move them to head <head> of your document.⚠️ You should only enable this feature, if passing the validator is really important for your project and if you don’t use AJAX to load markup containing image sets dynamically. |
imageset.tags.image |
true |
true , false |
If enabled, ImageSet will replace Kirby’s built-in image Kirbytag with one that will generate image sets instead of plain image tags. |
imageset.tags.image.sizes.default |
'' |
any defined preset name | Set the default size of image sets generated via the image Kirbytag. If this option is set, ImageSet will always generate image sets whenever the image Kirbytag is used, and no size parameter was given. |
imageset.tags.image.class |
'size-{size}' |
— | Added to the class attribute of every image set generated by the image Kirbytag.{size} will be replaced by the name of the corresponding preset. |
ImageSet offers a convenient API for including image sets into your Template-Code. Just use the global imageset()
function in your templates:
The imageset()
function
imageset(Media $image, mixed $sizes = 'default', array $options = null)
Parameters:
$image
Media
or File
objects.$sizes
'default'
and uses it, if defined.$options
Working with File
objects:
Whenever your source image is inside Kirby's content folder, you should use Kirby’s API to retrieve the image as a File
object, so you can use the imageset()
method. In this case, you don’t have to specify the $image
parameter:
if($image = $page->image('sample.jpg')) {
echo $image->imageset('200,400,600');
}
Styling Image Sets
The markup generated by the plugin is wrapped in a <span>
tag which has the class imageset
and additional classes depending on your options. By default, image sets always span the full width of their parent container. You can override this behavior by either wrapping them with another container element that has a constrained width or by using something like .text .imageset { width: 50% ; }
in your CSS.
Image sets also have have display: inline-block
on their container element by default to make them behave similar to regular <img>
tag with all its upsides and downsides. You can safely override this by adding .imageset.-ratio { display: block; }
somewhere to your stylesheet after imageset.scss
was included, if you would like image sets to behave like block elements.
As thumbnail sizes are usually defined by their width, ImageSet provides a convenient syntax for this.
List of widths
echo $image->imageset('200,400,600');
echo $image->imageset([200, 400, 600]);
This will generate 3 thumbnails with a width of 200, 400 and 600 pixels. You can provide the sizes either as array or as string of comma-separated values. For better readability, you may also add a space after each comma.
Range between widths
echo $image->imageset('200-600');
When you specifiy a range, ImageSet will calculate the intermediate sizes automatically. By default, the plugin will generate 2 intermediate sizes, so the example above will create a total of 4 thumbnails at widths of 200, 333, 467 and 600 pixels.
echo $image->imageset('200-600,3');
If you need more than 2 intermediate sizes, you can optionally tell the function, how many intermediate sizes it should calculate. The example above will create a total of 5 thumbnails at widths of 200, 300, 400, 500 and 600 pixel
List of cropped sizes
echo $image->imageset('400x200,800x400');
You can also describe explicit width and height parameters for images to create cropped thumbnails.
ℹ️ All images described in one size must have the same aspect-ratio. If you want to serve differently cropped images based on viewport-size, continue reading for more detailed information on more complex usage scenarios. |
Range between cropped sizes
echo $image->imageset('400x200-800x400'); // (1)
echo $image->imageset('400x200-800x400,5'); // (2)
You can also provide a range between the smallest and the largest cropped thumbnail you need (1) and tell the plugin how many intermediate sizes it should generate (2).
The examples above should be enough for most scenarios. If your layout does not require different crop ratios for differently-sized viewports, you can safely continue at 4.3 Setting Options. 😌
Some scenarios require your images to be cropped in different aspect-ratios for different viewports.
Take the full-width hero image of a blog example for example; If a screen is much wider than tall, a hero image in 16:9 format might be a good choice, but on other screens — like a smartphone held upright, showing a square image might be the better option. The examples below demonstrates that:
<?= imageset($page->image('marina.jpg'), [
[ '320x180-1920x1080,5', 'media' => '(min-aspect-ratio: 3/2)' ],
[ '320x320-1000x1000,5' ],
]); ?>
The example will create an image set with 2 different aspect-ratios, based on the viewport’s aspect-ratio. The first size in 16:9 format is only shown when the viewport has an aspect-ratio of at least 3:2. On taller viewports, a square version of the image is shown instead.
sizes
AttributeThe HTML spec requires you to specify the sizes
attribute for every <source>
or <img>
element with a srcset
, that uses width descriptors. Otherwise, the HTML code does not pass the validator.
By default, ImageSet will add sizes="100vw"
to your images to make your markup valid. As long as you use lazy loading (enabled by default), the plugin will automatically calculate the size based on the current width of each image set. If you disable lazy loading or a lot of your visitors use plugins like NoScript, you should pass a valid sizes descriptor to avoid the download of larger image files than needed.
<?= $image->imageset([ '160-1280,6', 'sizes' => '(min-width: 640px) 100vw, 50vw' ]) ?>
Further Reading:
The options described in this section define the behavior of a single image set and can be passed as additional parameter when calling the imageset()
function in your templates:
<?= $image->imageset([
'160-1280,6', 'sizes' => '(min-width: 640px) 100vw, 50vw'
], [
'class' => 'awesome-image',
'placeholder' => 'blurred',
]) ?>
You can also define default values for all options by setting them in your config.php
file with a prefix of imageset.[option name]
:
c::set('imageset.placeholder', 'color');
c::set('imageset.noscript', false);
Option | Default value | Possible values | Description |
---|---|---|---|
class |
'' |
– | Additional classes added to the wrapper tag of the image set. |
alt |
'' |
any string | Sets the alternative text for this image set. |
ratio |
true |
true , false |
Adds a ratio-placeholder/intrinsic size to the image set. This reserves space for the images while the page is loading and avoids reflows (aka “page-jumps”). This option is ignored, if placeholder has any other value than false or lazyload is enabled and always behaves like it had a value of true in these cases. |
placeholder |
'mosaic' |
false , 'triangles' *, 'mosaic' , 'blurred' , 'lqip' , 'color' |
Sets the visual style of the placeholder shown while the main image is being loaded. Set to false if you don’t want to use a placeholder at all. Have a look at the Demo page to preview all available placeholder styles. *) May not render correctly in IE 10/11. |
lazyload |
true |
true , false |
Enables lazy loading of image sets. Remember to include the corresponding JavaScript file into your markup as described in 2.3 Template Setup. |
noscript |
true |
true , false |
Includes a fallback for clients without JavaScript support or disabled JavaScript. |
output |
'auto' |
'auto' , 'plain' |
'auto' = Image set will produce full-featured HTML according to your options.'plain' = HTML output will be plain HTML without any classes, placeholders etc. img and source tags are also rendered in XHTML-compatible syntax. |
output.xhtml |
false |
true , false |
When enabled, ImageSet will generate XHTML/XML-compatible markup for all image sets. This means that a trailing slash is added to void elements and a different handling of special chars. |
cache |
true |
true , false |
When enabled, the HTML output of image sets is always cached, resulting for major speed improvements. |
Your project probably has a lot of re-occuring image sizes. You can configure a set of default presets in your config.php
to use them in your templates:
# site/config/config.php
c::set('imageset.presets', [
'default' => [
[ '300-1500,7' ],
],
'hero' => [
[ '320x180-1920x1080,5', 'media' => '(min-aspect-ratio: 3/2)' ],
[ '320x320-1000x1000,5' ],
],
'small' => [
[ '200,300,600,800' ]
],
]);
<?php if($hero = $page->hero()->toFile()): ?>
<div class="hero">
<?= $hero->imageset('hero') ?>
</div>
<?php endif ?>
In more complex scenarios, you might want to use different size presets depending on the current template, snippet or content area. Maybe your project uses Kirbytext in differently sized columns and you don’t want to use size names like “sidebar-small” in your Kirbytext markup. In these cases, you should have a look ate the dynamic presets API:
imageset::presets([ 'default' => … , 'small' => … ])
$sizes
parameter to the imageset()
function.imageset::presets('clear')
config.php
via the imageset.presets
. You can still load any presets that have been saved before by calling imageset::presets('save', …)
.imageset::presets('load', $name)
$name
before.imageset::presets('reset')
config.php
file.imageset::presets('save', $name)
$name
has to be anything that would be a valid PHP array key, except for the word 'inital', which is reserved.Example
# /site/snippets/imageset-presets.php
<?php
// Presets for page content
imageset::presets([
'default' => '200-1000,5',
'small' => '150-500',
'square' => '200x200-1000x1000,4',
]);
imageset::presets('save', 'content');
imageset::presets('reset');
// Presets for sidebar
imageset::presets([
'default' => '200-500',
'small' => '100,150,200',
]);
imageset::presets('save', 'sidebar');
imageset::presets('reset');
# /site/snippets/header.php
<?php snippet('imageset-presets') ?>
<!DOCTYPE html>
<html>
[…]
In imageset-presets.php
, two different sets of presets are defined, one for the main content ('content'
) and one for the narrower sidebar content ('sidebar'
). This file was included in header.php
to ensure that it loaded with every template. The following templates offers two different areas, where Kirbytext appears. Before calling the Kirbytext command, the corresponding set of size presets is loaded, so image tags inside your Kirbytext will be resized. After all Kirbytext has been generated, presets are reset to their initial state by calling imageset::presets('reset')
.
# /site/templates/default.php
<?php snippet('header') ?>
<main class="container">
<article class="main-content">
<?php imageset::presets('load', 'content') ?>
<?= $page->text()->kirbytext() ?>
</article>
<aside class="sidebar">
<?php imageset::presets('load', 'sidebar') ?>
<?php if($image = $page->sidebar_image()->toFile()): ?>
<?= $image->imageset() ?>
<?php endif ?>
<?= $page->sidenotes()->kirbytext() ?>
</aside>
<?php imageset::presets('reset') ?>
</main>
[…]
⚠️ If you’re using a snippet file for storing presets, it must not be named imageset.php as this name is reserved for a custom image set snippet as described in 7 Generating Custom Markup. |
ImageSet replaces Kirby’s built-in (image: …)
Kirbytag by default to also allow usage of the plugin in your content. You can disable this custom image tag by setting imageset.tags.image
to false
in your config.php
. It will also be ignored, if you have a file named image.php
placed in the /site/tags
directory.
The tag just works like Kirby’s image tag, but has an additional attribute called size
. Using this parameter lets you use any of your size presets within Kirbytext:
(image: marina.jpg size: small)
If you want to transform every image within your content to an image set automatically, you can also specify a default size in your config.php
:
c::set('imageset.tags.image.sizes.default', 'small');
If you do so, every image tag without the size attribute will be resized according to the preset you defined as default.
ℹ️ Only images in JPG, PNG or GIF format will be converted into ImageSets, other image types like SVG will just act like the original image tag and generate the same markup, as Kirby’s built-in image tag would do. |
If your site generates RSS-feeds, including full-blown image sets into the markup might not work with most RSS readers due to their lack of JavaScript and CSS support. You can force ImageSet to generate plain, RSS/XHTML-compatible markup by setting the output
option.
# site/templates/feed.rss.php
[…]
<?= $image->imageset('200-800', [ 'output' => 'plain' ]) ?>
[…]
Because this is not suitable for Kirbytext, you can also change the global setting dynamically:
The imageset::outputStyle()
function
imageset::outputStyle(string $style = 'auto')
$style
: Takes either a value of 'plain'
or 'auto'
(default value).
Example
# site/templates/feed.rss.php
<?php
imageset::outputStyle('plain')
// All image sets created from this point will generate plain markup.
?>
[…]
<?= $image->imageset('200-800') ?>
[…]
<?php
imageset::outputStyle('auto');
// All image sets created from this point will generate regular markup again.
If the code generated by ImageSet does not satisfy the requirements of your project, you may override the snippet file, which is used to generate the markup for image sets.
Just place a copy of site/plugins/imageset/snippets/imageset.php
in your snippets directory and make sure this file is also named imageset.php
. Make your adjustments in the cloned file and ImageSet will load your custom snippet automatically.
⚠️ Keep in mind, that you might have to update your custom snippet file whenever ImageSet gets an update that makes changes to the vanilla snippet file. If you’re missing a feature that could be useful for other users as well, consider to open an issue describing your idea instead. |
tag. But at SVG is a very complex document format, there is no simple way in PHP to generate placeholder thumbnails (imagemagick does not work reliably with every SVG file), so placeholder style will be ignored and the SVG file will be shown in its original aspect-ratio. Everything set in the $sizes
parameter will be ignored.
code> tag. You can use the class
option to add custom classes, if you need different styles for different image sets.
Safari 10
When an image set appears within an element that uses CSS multi-colum layout (column-count > 1
), the fade-in animation for lazy-loaded image sets does not work. However, the loaded image is displayed correctly. Currently, there is no way to fix this as far as I know.
Printing
Although it’s possible to trigger loading of images when a user hits the print button, that does not necessarily mean that images are generated before the browser generates its print preview or the user starts to print. A workaround for this is to preload all images after the page has been loaded. This ensures that image sets are loaded after other resources like JS and CSS files, but loads every image on the page and thus basically disables lazy-loading:
<script>
window.lazySizesConfig = window.lazySizesConfig || {};
window.lazySizesConfig.preloadAfterLoad = true;
</script>
ImageSet was developed and maintained by Fabian Michael, a graphic designer & web developer from Germany.
The plugin includes the following third-party components: