Handling Sprites in Sitecore with SVG

Sprites, as defined by wikipedia:

To reduce the number of requests the browser makes to the server, some web designers combine numerous small images or icons into a larger image called a sprite sheet or tile set.[27] CSS is used to select the parts of the composite image to display at different points in the page.

Rendering relatively static imagery on the web using sprites and CSS is a very well-known technique that has been in use for quite some time.

Take this sprite, for example:

chess_pieces

The traditional method used to render these pieces would look something like this:

<head>
<style type="text/css">
   .icon {
      display: inline-block;
      width: 100px;
      height: 100px;
   }
   .whiteKing {
      background-image: url('chess_pieces.png');
      background-position-x: 3px;
      background-position-y: 5px;
   }
   .blackQueen {
      background-image: url('chess_pieces.png');
      background-position-x: -111px;
      background-position-y: -110px;
   }
   .blackKnight {
      background-image: url('chess_pieces.png');
      background-position-x: -335px;
      background-position-y: -110px;
   }
   .whiteRook {
      background-image: url('chess_pieces.png');
      background-position-x: -452px;
      background-position-y: 0px;
   }
	</style>

</head>
<body>
<div>
      <img src="transparent.gif" class="icon whiteKing" />
      <img src="transparent.gif" class="icon blackQueen" /></div>
<div>
      <img src="transparent.gif" class="icon blackKnight" />
      <img src="transparent.gif" class="icon whiteRook" /></div>
</body>

This approach works quite well in a static-HTML world, or at least when you (or your dev team) have full control over the HTML and CSS of your site.  However, in a CMS world, this is more difficult to achieve, while still making it manageable for content authors without requiring them to have some knowledge of HTML and CSS.  In Sitecore, making this approach manageable wouldn’t be impossible, but certainly wouldn’t be optimal, especially if you wanted to make it a reusable component.

A much better approach, recommended to me by Rich Seal (@rich_seal) on slack, would be to implement these sprites as SVG images using fragment identifiers.  Not only are SVG images gracefully scalable, this approach will be much easier to implement in a reusable manner.

Let’s start by downloading the above sprite as an actual SVG file and upload it into the media library.  Configuring Sitecore to support SVG images is quite well documented here, here, here and here, but this is the configuration necessary to insert through a config patch:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
   <sitecore>
      <mediaLibrary>
         <mediaTypes>
            <mediaType name="SVG image" extensions="svg">
               <mimeType>image/svg+xml</mimeType>
               <forceDownload>false</forceDownload>
               <sharedTemplate>system/media/unversioned/image</sharedTemplate>
               <versionedTemplate>system/media/versioned/image</versionedTemplate>
               <mediaValidator type="Sitecore.Resources.Media.ImageValidator"/>
               <thumbnails>
                  <generator type="Sitecore.Resources.Media.ImageThumbnailGenerator, Sitecore.Kernel">
                     <extension>png</extension>
                  </generator>
                  <width>150</width>
                  <height>150</height>
                  <backgroundColor>#FFFFFF</backgroundColor>
               </thumbnails>
            </mediaType>
         </mediaTypes>
      </mediaLibrary>
   </sitecore>
</configuration>

Now, create a new data template called SVG Sprite, configured as:

svg-sprite-template

Next comes the Glass model and configuration.

public interface ISvgSpriteModel : IContentBase
{
    Image SpriteImage { get; }
    string Viewbox { get; }
    string FragmentSelector { get; }
}
public class ISvgSpriteModelMap : SitecoreGlassMap<ISvgSpriteModel>
{
    public override void Configure()
    {
        Map(config =>
        {
            ImportMap<IContentBase>();
            config.AutoMap();
            config.Field(f => f.SpriteImage).FieldName("Sprite Image");
            config.Field(f => f.Viewbox).FieldName("Viewbox");
            config.Field(f => f.FragmentSelector).FieldName("Fragment Selector");
        });
    }
}

Then, we define the ViewModel, the Controller action and the View to be used by our component.

public class SvgSpriteViewModel
{
    public string SpriteImageUrl { get; set; }
    public string Viewbox { get; set; }
    public string FragmentSelector { get; set; }
}
public class AtomicComponentsController : BaseController
{
    private readonly IAtomicContentService _contentService;
    private readonly IRenderingContext _renderingContext;

    public AtomicComponentsController(IAtomicContentService contentService, IRenderingContext renderingContext, ISitecoreContext glassContext) : base(glassContext)
    {
        _contentService = contentService;
        _renderingContext = renderingContext;
    }

    public ViewResult SvgSprite()
    {
        var viewModel = new SvgSpriteViewModel();

        if (_renderingContext.DataSource.IsNotNullOrEmpty())
        {
            var contentResponse = _contentService.GetSvgSprite(new GetContentRequest() { ContentGuid = _renderingContext.DataSource });
            if (contentResponse.Success && contentResponse.ContentItem != null && contentResponse.ContentItem.SpriteImage != null)
            {
                viewModel.SpriteImageUrl = contentResponse.ContentItem.SpriteImage.Src;
                viewModel.Viewbox = contentResponse.ContentItem.Viewbox;
                viewModel.FragmentSelector = contentResponse.ContentItem.FragmentSelector;
            }
        }

        return View(viewModel);
    }
}

 

@model ViewModels.SvgSpriteViewModel

<svg viewBox="@Model.Viewbox" class="svgicon">
   <use xlink:href="@Model.SpriteImageUrl#@Model.FragmentSelector"></use>
</svg>

Now that the Controller has been created, let’s create a few SVG Sprite content items.

svg-sprite-images

After creating the content items, configure a new Controller Rendering for the new SvgSprite controller action and point its Data Source Location to the SVG Sprite Images folder as well as its Data Template to our new SVG Sprite data template.

Now that all the pieces are in place, the component can be added to a placeholder on a page (4 instances have been added below).

components-on-a-page

The rendered components would look like:

rendered-components

But the beauty of SVG images comes when you need to scale the image up or down.

larger-rendered-components

Now, we have a reusable, sprite-based image that content authors can manage easily!

Happy Sitecore trails, my friends!

  One thought on “Handling Sprites in Sitecore with SVG

Leave a comment