dotless: Base64 encoded SVG gradient backgrounds

Some time back I had to implement background color multi-stop gradients that could be used in IE9 and since linear-gradient is not supported I had to look at SVG-based CSS background-image gradients. There is, however, the small twist that I am using dotless and the colors are supplied by a database so the solution needs to generate the SVG XML dynamically.

Luckily I found this blog post Base64 encoded SVG gradient backgrounds in LESS written by Phil Brown. It details a nifty way to use svg background gradient with less mixins by executing javascript to base64 encode any given svg xml.

Problem

This is all fine if you are using less.js but dotless on the other hand does not support executing JavaScript since server-side the JavaScript interpreter is simply not present and if you read the dotless wiki they make this very clear.

In less.js you can use the backquote in order to execute JavaScript. This is not supported in dotless.

Because of this limitation, the solution put forward above is not viable. We need a solution where we can base64 encode the SVG XML without using JavaScript.

Solution

Instead of executing JavaScript, dotless allows you to execute .NET code and expose this functionality in the form of plugin functions. Previously I have written a guide on how to write a dotless plugin and by using this technique we are able to harness the same expressive power as with executing JavaScript. Actually, things become a lot easier in my opinion since we do not have to worry about what native methods might be available. Base64 encoding strings with .NET is a straight forward task.

[DisplayName("LessPlugins")]
public class LessPlugins : IFunctionPlugin
{
	public Dictionary<string, Type> GetFunctions()
	{
		return new Dictionary<string, Type>
			{
				{ "b64DataUri", typeof(b64DataUriFunction) }
			};
	}
}

public class b64DataUriFunction : Function
{
	protected override Node Evaluate(Env env)
	{
		// Validate input
		Guard.ExpectMinArguments(2, Arguments.Count(), this, Location);
		Guard.ExpectNode<Quoted>(Arguments[0], this, Arguments[0].Location);
		Guard.ExpectNode<Quoted>(Arguments[1], this, Arguments[1].Location);

		// Read input
		var data = Arguments[0].ToString();
		var type = Arguments[1].ToString();

		// base64 encode
		byte[] dataBytes = Encoding.ASCII.GetBytes(data);
		string dataEncoded = Convert.ToBase64String(dataBytes);

		return new TextNode(String.Format("url(data:{0};base64,{1})", 
											type, dataEncoded));
	}
}

Once the plugin is registered for usage by dotless the function above can be used in your less files like this:

.base64DataUriBackground (@encode, @type: ~"image/svg+xml") {
    background: b64Datauri(@encode, @type);
}

.body {
    @gradient-color-top: #00870A;
    @gradient-color-bottom: #008E53;

    @svg: ~'<?xml version="1.0" ?>... /* entire xml below */ ...</svg>';
    .base64DataUriBackground(@svg);
}

rendering

.mydiv {
    background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2l...);
}

We can use this function every time we want to use a base64 encoded string of type url(). All we need to do is define the SVG XML which expresses the gradient we want to use and simply call .base64DataUriBackground.

For a full description of how to register a dotless plugin please refer to how to write a dotless plugin.

note: full xml

<?xml version="1.0" ?>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none">
<linearGradient id="grad-ucgg-generated" gradientUnits="userSpaceOnUse" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="@{gradient-color-top}" stop-opacity="1"/>
<stop offset="100%" stop-color="@{gradient-color-bottom}" stop-opacity="1"/>
</linearGradient>
<rect x="0" y="0" width="1" height="1" fill="url(#grad-ucgg-generated)" />
</svg>

Speak Your Mind

*