Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Generating CSS sprites with T4 tempales

0.00/5 (No votes)
5 Oct 2013 2  
with some ASP.NET MVC 4 helpers

Introduction

In this article, I would like to present an easy approach to handle the many little images that websites use nowadays. This approach will use T4 templates for making CSS sprites and other helper files. The main purpose in this article is how we can increase developer productivity with T4. 

All files are available at GitHub:  

https://github.com/regiuskornel/T4CSS-Sprite  

Background

A "well" designed site is full of icons that make many problems. Every icon makes individual requests to the website. 30-100 extra requests can take a lot of time before the whole HTML page is fully rendered. In worst cases, this time can make up to 1-10 second delays and consuming network and web server resources. We need to decrease these extra requests to a single one.

For CSS and JS files we have a handy ’bundling’ (in ASP.NET 4) service, implemented in the System.Web.Optimization dll. This service bundles these text files together, and sends them in one request to the browser. The CSS sprite is the same, just with images. We put all the small images on a big one and we send only this one big image to the browser. Moreover, if we collect the images in a smart way, we can reuse it on whole web application. This way, the browser don’t even need to download that picture again, as it can find the image in the local cache. It only sends a check request of a few tenth seconds, that asks if the image has been modified since the last download.

The problem

is, who is and when going to assemble this mosaic, this sprite? Let’s say, we have 150 little image… Desktop tools and online sprite generators have been around for a while now. With these, we can usually do two things: put all the images on a surface, or maybe position them, then save the generated sprite. Better tools create the CSS file also, based on the background-position coordinates and the names of the icons. E. G.:  

.t4SpriteVar { background-image: url("SpriteVar.png")}
.t4accept {background-position:0px -418px;width: 48px;height: 48px;}
.t4accept.disabled {background-position:-48px -418px;width: 48px;height: 48px;} 
What is the problem with tools? For me, the problem is that they are external tools, web applications, exe files, that need correct handling and parameterization. However, the biggest problem is still that they are not compatible with a version-handling environment (TFS, SVN), that makes them uncomfortable to use them in teamwork. Just think about it, how much time do we have to spend on putting a new icon on a button for one of our users? Just swap one of them for a bigger one. Manually check-out, modify+generate, chech-in process. In contrast, how easy it is for a developer without sprites. He copies the image into a folder, drops it on the editor, .aspx or .cshtml in Visual Studio (or something else), and that’s it. I see why the developer likes individual icons instead of sprites.

Solving problems 

This made me think. How could we eliminate all those problems, while keeping the advantages of CSS sprites? If possible, we don’t even want to leave the window of Visual Studio. Let’s see the requirements:

  • We have a folder with lots of small images. They might wary in extension (png, jpg, gif) and/or size.  
  • We want to make a sprite from those; therefore, we need an image-generator. 
  • We need a CSS file that contains the sizes and coordinates of the images in a background-position format.  
  • It would be also nice to have an overview for the images and their names on a page, to make searching possible. 
  • A static c# class would be also nice to be able to reference the names of the pictures in a typed way. This way, we will be notified at compile time if a picture is missing. In addition, we will also get Intellisense support on the .cshtml and .aspx pages for the picture names. 

An image file and three text files. That’s what we will use the T4 templates for.   

T4 stands for Text Template Transformation Toolkit. It is most commonly used to generate
text files, although its name doesn’t define that as its only purpose. As the name says, we have a text template, with a syntax similar to the .aspx files of Web Forms, that we can use to generate content within Visual Studio. The template – as always – contains both static parts and dynamic-content generator parts. In addition, we can also define ordinary C# or VB.NET code blocks in it.
The great advantage of T4 is that VS itself runs the templates. Therefore, we can both assign the generated files to projects and notify the versioning system, from the T4 source code.
 

 
In the following sections, I will not go deep into the depths of T4, rather just describe the function and usage of the T4 file, that the demo project contains. I am sure, that the demo I will present is not a perfect solution, but rather a POC project. It is open for addition and expansion, which is not difficult to do in GitHub. As VS does not provide code-formatting (coloring) or Intellisense for T4 files, it is recommended to download an external VS plugin from Visual Studio Gallery. During the development, I was using the DevArt T4-Editor.  

One of the features of T4 files – a wise thing if you ask me – is the possibility to import other T4 files. This way, we can define code-collections. The directive of include: 

 <#@ include file="T4SpriteMaker.ttinclude"#> 

For a T4 code-collection, it is a bad idea to let it generate a file. If we want VS not to run a T4 file, we must delete the value of ‘Custom Tool’ in the properties window of the file. 

 

 

The most important file will be this T4SpriteMaker.ttinclude, the rest of them will only contain custom parameterizations for picture generating.  

I also must mention that the basic code/algorithm that creates the sprite and the CSS is not my work. You can find the original one on Code Project: Image Sprites and CSS Classes Creator. I took the source code of V2.0, and modified it to match the logic of T4, as the original one is a console application. If you think, you can give an at least ‘Excellent’ vote for the Swiss AlexCode for his work. The best thing in his code is that it is simple, fast and short. That is why I chose it, as in a T4 file it is a little hard to write and debug long and complicated code. 

Another property of T4 files is that they cannot be parameterized from outside. We have the File Properties window, provided by VS, but we cannot get too far with that. Therefore, we must set the parameters within a file, and that is why I separated the codebase from the parameterizing T4 files. Let’s look into one of the parameterizing .tt files to see what each setting does. The settings, following AlexCode’s pattern, must be put as properties into the SCEngineOptions class:  

.SourceDirectory = T4Folder+"\\Images16"; 

This property defines the folder, where the source images are. The 'T4Folder' is the folder of the .tt file, which allows us to set relative paths for the folder of the source images.

.CssClassPrefix = "t4";  

It is recommended to add a prefix to the names of the CSS classes that define the positions of the images within the sprite, in order to prevent name collides with normal, manually created css classes (e.g. .t4label instead of .label). 

.BinPackingLevel = 1; 

The code number of the image positioning algorithm. The goal is to make the smallest possible sprite filesize. There were three algorithms in AlexCode’s code, and I took them as they were:

1 - Basic rectangle (fastest)

2 - Cygon packager (average size)

3 - ArevaloRectanglePacker (maybe minimal size). 

One might think, that the “best” is the best, but it is not. That is why you have this setting, so can choose. The best algorithm for a situation depends on the size of the pictures, the variation of the sizes and the number of pictures.  The bad thing is that the only way to find the smallest sprite for each picture-pack is to run the algorithms for ourself. Except if we have pictures of the same size. In this case, the 1. basic algorithm is the best for sure. 

.FilesName = Path.GetFileNameWithoutExtension(T4FileName);

The name of the generated .png sprite, CSS, HTML, .cs file, C# static class and CSS base class. As you can see, in this case comes from .tt file name. However, you can overwrite with any static name. 

.ImageVariants.Add(new ImageVariant() {
            GreyScale = true,            //is gray scaled image?
            Width = 0,                   //Resized image width, 0 means image won't resize
            Height = 0,
            CssClassName= "disabled",    //additional CSS class for this variant
            DisplayName = "Disabled"     //Column title in demo/test html 
});  

To go even further, I made an extension that allows us to make image variants from the source images with image-transformations. This solves the common problem, when a toolbox icon has multiple visual states: clickable (colorful), disabled (grey) and pressed (highlighted). Sometimes, we want to use an image in multiple sizes, like 48x48, 32x32 or 16x16. Because of the definition I shown above, every icon will have a greyscale version. You can also assign custom CSS classes for each variants. The great advantage of this is that the visual state, enabled or disabled, can be easily defined by the presence or absence of a CSS class. 

 

Enabled (left icon):  <span class="image24 t4icon t4SpriteVar t4clock"></span> 

Disabled (right icon): <span class="image24 t4icon t4SpriteVar t4clock disabled"></span> 

You can cram multiple CSS classes in the CssClassName, if you want more than one transformation. This is how I made the normal 128x128 / disabled 128x128 / normal 24x24 / disabled 24x24 icons on the bottom-right four 'clock': 

.ImageVariants.Add(new ImageVariant() {
            GreyScale = true,
            Width = 24,
            Height = 24,
            CssClassName= "image24 disabled",
            DisplayName = "24x24 dis."
            });  

This will make them gray, resize them to 24x24 and create two classes to distinguish from the original 128x128 and from the normal 24x24 image.   

You can put multiple variant definitions into the ImageVariants collection. For more examples, see the settings in "SpriteVar.tt", where multiple sizes and grey versions are defined to be created.

The rest of the parameterizing T4 files contain the parts that generate the CSS, the .cs and the overview HTML file.   

How to use 

We put the T4SpriteMaker.ttinlude file, wherever we want. Then we have to make sure that we set the linking in the parameterizing T4 files to reach the .ttinclude file in a relative path.

<#@ include file="..\path\T4SpriteMaker.ttinclude"#>

 

If it is in the same folder as the .tt files, then we don’t have to do anything. The include statements will be fine with the default value. 

 

After that, we create the folder for the images and put there anything we need. We give a meaningful name for the .tt files, because that will be the default name for the generated files (e.g. Sprite32.tt, because of the value of the .FilesName, that we use in this demo). 

 

After parameterizing and saving the .tt files, the other files will be generated.  

The .cs file will contain consts of the image names, wrapped up into a static class. The namespace will come from the 'Custom Tool Namespace' setting from the property window. 

namespace MvcApp.Content.Images 
{
	public static class Sprite32
	{
		public const string t4accept = "t4accept";
		public const string t4add = "t4add";
		public const string t4block = "t4block";
		public const string t4chart_pie = "t4chart_pie";
		public const string t4users = "t4users";
		//	}
}    

In the .css, you will find the position codes (compressed a litle): 

.t4Sprite32{background-image: url("Sprite32.png")}
.t4accept{background-position:0px -162px;width:32px;height:32px;}
.t4accept.disabled{background-position:-32px -162px;width:32px;height:32px;}
.t4add{background-position:-64px -162px;width:32px;height:32px;}
.t4add.disabled{background-position:-96px -162px;width:32px;height:32px;} 

The .html file will contain the overview page and an example of usage in the markup. The header of the table will contain the value of the .DisplayName parameter. For example, in the picture on the right, the .DisplayName = "Disabled" setting resulted the header “Disabled” for the column of the greyscale icons.   

 

(You will also find a few extra .tt files in the demo application that can be used for testing purposes. These are made for situations like bad image file, empty image folder or the lack of image folder (SpriteInvalidImages, SpriteEmptyDir, SpriteWrongPath)).  

The demo application also contains a possible usage in the ASP.NET MVC 4 framework with custom Html and Ajax link helpers. Some method calling variations: 

@Html.SPActionLink(SpriteVar.t4block, "Block button", "Index")
@Html.SPActionLink(SpriteVar.t4block, "Block button", "Index", ButtonMode.Disabled)
@Html.SPActionLink(SpriteVar.t4block, "Block button", "Index", "Home")
@Html.SPActionLink(SpriteVar.t4block, "Block button", "Index", "Home", ButtonMode.Disabled)
@Html.SPActionLink(SpriteVar.t4block, "Block button", "Index", "Home", null, null)
@Html.SPActionLink(SpriteVar.t4block, "Block button", "Index", "Home", null, null, 
                   ButtonMode.Disabled)

@Html.SPActionLink(SpriteVar.t4help, "Help button", "Index", "Home", null, null, 
                   ButtonMode.Default | ButtonMode.IconOnly)
@Html.SPActionLink(SpriteVar.t4help, "Help button", "Index", "Home", null, null, 
                   ButtonMode.Disabled | ButtonMode.IconOnly)  

 Points of Interest   

I hope I succeeded to raise interest about how great help the T4 templates can provide in the construction of a dynamic UI. This structure is flexible enough to prevent the obstacles and nuisances, that could arise from the frequent change of the image elements. 
The source images in this article and in the demo app are from dryicons.com 

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here