Making Images Responsive

This guide is based on our instructions for integrating Scrivito. You can complete it in no time after creating your Scrivito account. Using Bootstrap is not required but recommended.

Responsive websites adapt the layout of their content to different screen sizes to retain its legibility and usability on today's various device types. Having responsive images is essential to reaching this goal because appropriately sized images not only contribute to the look and feel of web pages but also speed up their delivery and thus improve the visitors' user experience. Scrivito allows you to easily make the images on your website responsive.

We are going to use the srcset HTML attribute to achieve this. Let's get started.

Introducing srcset

The srcset attribute can be used as an optional extension to img tags and lets us specify differently sized versions of an image. Browsers recognizing this attribute can then choose the image that fits best.

Scrivito has the ability to scale images on demand and also cache the versions produced (see Transforming Images for details). It also provides an easy-to-use mechanism for specifying the versions and sizes to make available to the browser.

The srcset attribute requires a comma-separated list of URLs pointing to images and their respective widths. For example, if we wanted to offer three different versions of an image, we would specify the srcset attribute like this:

Copy
srcset="large.jpg 1024w,
        medium.jpg 640w,
        small.jpg 320w"

Now, all that's missing are the scaled images.

Introducing image scaling

As mentioned above, Scrivito is capable of producing differently sized versions of images. It doesn't simply scale images on-the-fly but caches them, so they are delivered faster and cause less server load. Take a look at the Rubydoc, if you want to see how to use the API in detail. We are only going to transform images with respect to their width, preserving their aspect ratio. An example:

Copy
transformed_image = image.transform(width: 640)

Creating a srcset helper method

To be able to easily apply image transformation to all images later on, we are first going to define a helper method that does this job. This helper transforms individual images and returns their respective URLs and widths required for srcset. Create the file app/helpers/srcset_helper.rb and add the following code to it:

Copy
module SrcsetHelper

  def srcset_for(obj)
    image = obj.try(:binary)
    return unless image

    [160, 320, 640, 1280, 2560].map do |width|
      transformed_image = image.transform(width: width)
      url = scrivito_url(transformed_image)

      "#{url} #{width}w"
    end.join(", ")
  end
end


We can call this helper method in any view in which an image is rendered, e.g. in the view of the image widget that comes with Scrivito.

Integrate srcset into the image widget view

Navigate to the view file of the image widget located at app/views/image_widget/show.html.erb and add the srcset list by using the srcset_for method we just created.

Copy
<%= scrivito_image_tag widget, :image, { class: 'image-widget', srcset: srcset_for(widget.image)} %>

Are we finished now? Almost. Since the helper method creates images with different widths, the browser won't know the width of the original image. Therefore, we need to specify a width in the application stylesheet in app/assets/stylesheets/application.css.sass. Open this file and add the following line to it:

Copy
img[srcset] { width: 100%; }

Image responsiveness should work with all new browsers. But what about older browser versions? Let's go ahead and see what we can do.

Adding backwards compatibility

For browsers that don't support srcset yet (you might want to check the supported browsers), simply use the transform option of the scrivito_image_tag to define a fallback image width:

Copy
<%= scrivito_image_tag widget, :image, {  class: 'image-widget',  srcset: srcset_for(widget.image)}, transform: {width: 1280} %>

Improving image selection

The srcset attribute allows the browser to choose the image that fits best. But how does it know? Basically, the width of the browser window is used to determine the amount of space available for an image. In the future, additional criteria like the connection speed or traffic limits might come into play.

The method introduced above creates several scaled-down versions of your original image. This is sufficient for one-column layouts or header images occupying the full width of the window. With dynamic multi-column layouts, however, the browser needs to know the reduced width of the area available for an image to select the best fitting one from the srcset list. For this, the sizes HTML attribute is available.

Introducing the sizes HTML attribute

The sizes attribute can be applied to img tags. It lets you specify fractional widths depending on one or several media queries:

Copy
sizes="(min-width: 992px) 50%, 100%"

The example above states that for all browser window widths equal to or larger than 992px (which is Bootstrap's breakpoint for desktops), the image should use 50% of the screen width. Otherwise, it uses 100% of the available width.

For example, in the case of a full HD display with a width of 1920 pixels, which exceeds 992px, the result will be 1920/2=960px. Hence, the browser will select from your srcset list the image whose width comes closest to 960px.

Note that you can combine several media queries as a comma-separated list. The 100% fallback in the example above could be replaced with another media query to distinguish between smaller and tiny image areas, in case you need to handle three-column layouts, for example.

For every image, an individual pair of srcset and sizes attributes can be specified. With Scrivito, the sizes attribute can be calculated using the container method of the widget.

Integrating sizes into the image widget view

Navigate to the image widget view file located at app/views/image_widget/show.html.erb and add a sizes specification that computes the relative image width using the not yet existing column_size method:

Copy
<%= scrivito_image_tag widget, :image,
  {  class: 'image-widget',  
     srcset: srcset_for(widget.image),
     sizes: "(max-width: 992px) #{widget.column_size(widget)}%, 100%"},
  transform: {width: 1280} %>

To determine the width available for the image, its location in the DOM must be known. To get at this location, we can use the widget's container method and provide it with the column_size method we used in advance above.

Copy
# app/models/image_widget.rb
def column_size(image)
  self.container.column_size(image)
end

If the parent widget is a column widget, we're done and return the percentage available for the image, depending on the column width. This is done by determining the percentage of the smallest possible column and multiply this value by the column's width.

Copy
# app/models/twoColumnWidget.rb
def column_size(image)
  column = image.container_attribute_name
  self.send("#{column}_width") * (100/12.0)
end

# app/models/threeColumnWidget.rb
def column_size(image)
  column = image.container_attribute_name
  self.send("#{column}_width") * (100/12.0)
end

# app/models/widget.rb
# fallback for all other container widgets
def column_size(image)
  self.container.column_size(image)
end

If the image widget is directly contained in a widgetlist attribute of a CMS object, the full width (100%) is returned. If the layout of your pages reduces the width available for the content (like a sidebar does), you might want to take account of this, too.

Copy
# app/models/obj.rb
def column_size
  100
end

Alternatively, you can include the Picturefill JavaScript library in your application, which enables support of the srcset and sizes attributes for all browsers.

That's it!

Congratulations, you are finished! Your website now delivers images contained in image widgets responsively as a default.