How to write a dotless plugin

If you are using dotless you probably know it does more than just port less.js, it also extends it with custom functions like those listed below.

  • rgb(red, green, blue)
  • floor(number)
  • ispercentage(anything)

These functions allow you to do powerful server-side computation and use the result within your stylesheet. What might not be so well known is that dotless also allows you to extend functionality and write your own functions using their plugin model. I will show how easily this is done.

Square Root

The function below will implement the functionality of Math.Sqrt in C#. Our task is to implement plumbing needed to makes this available in our less files as well as give useful debugging information if used wrongly.

using dotless.Core.Parser.Functions;
using dotless.Core.Parser.Infrastructure;
using dotless.Core.Parser.Infrastructure.Nodes;
using dotless.Core.Parser.Tree;
using dotless.Core.Plugins;
using dotless.Core.Utils;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;

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

public class SquareRootFunction : Function
{
	protected override Node Evaluate(Env env)
	{
		Guard.ExpectMinArguments(1, Arguments.Count(), this, Location);
		Guard.ExpectNode<Number>(Arguments[0], this, Arguments[0].Location);

		var input = Arguments[0] as Number;

		return new Number(Math.Sqrt(input.Value), input.Unit);
	}
}

LessPlugins : IFunctionPlugin
We need an implementation of IFunctionPlugin used to register functions and give them names which will be used when writing less stylesheets. The name of any given function is then mapped to a specific implementation of the Function class.

SquareRootFunction : Function
A dotless Function implementation only needs the Evaluate function. This returns an object of type Node which has many specific implemetation like:

  • Color ( ex. #C5C5C5 )
  • Number ( ex. 720 )
  • Url ( ex. url(http://abc.com) )

The next couple of tasks are as follows.

1. Use the Guard object to express that we expect minimum one argument and that this must be of type Number.

2. If the Guard object validates the input we then access the argument and cast this to type Number.

3. Finally, we calculate the square root of said input and return the computed value in form of Node type Number.

Using sqrt

Making your plugins available is the simple matter of adding a few lines of configuration to your web.config file. This will register the new plugin at runtime.

<?xml version="1.0"?>
<configuration>
  ...
    <dotless minifyCss="true" cache="true">
        <plugin name="LessPlugins" assembly="Company.Plugins.Web" />
    </dotless>
  ...
</configuration>

You can now use this function as if it was a standard part of dotless. In the example below we have created a dotless mixin called fancymargin. It sets the margin property ensuring left/right is always the square root of top/bottom.

.fancymargin(@val) {
    margin: @val sqrt(@val);
}

body {
    .fancymargin(16px);
}

resulting in

body {
    margin: 16px 4px;
}

Whats next

Writing a dotless plugin function is fairly straight forward. The first couple of steps can, however, be a bit tricky since the documentation on the matter is practically non-existing. Once we get to this point, however, the source code is available on Github and what I have shown should be a nice stepping stone to more advanced implementations.

If you want to give it a go you should check out the dotless source code on Github.

Comments

  1. Jan Aagaard says:

    The following also has to be in the Web.config file:

    <configSections>
    <section name=”dotless” type=”dotless.Core.configuration.DotlessConfigurationSectionHandler,dotless.Core” />
    </configSections>

    • Jacob T. Nielsen says:

      @Jan Aagaard
      Yes this is true. However i’m only trying to illustrate what is needed for writing a dotless plugin. I assume dotless is already configured and running making the dotless section present. Where this article falls short the dotless wiki should be refered to.

  2. Where should the classes ([DisplayName(“LessPlugins”)] public class LessPlugins : IFunctionPlugin)) be created?

    • Jacob T. Nielsen says:

      Not sure I know exactly what you mean. The classes can be created in the project using it or in a referenced project. The example I give above assumes they are places in an assembly named “Company.Plugins.Web”.

  3. Hi,
    I have written a function plugin in a class file which is inside my asp.net mvc project.
    In the below configuration

    what is assembly here? Do I need to give my web project aseembly name.

    • Jacob T. Nielsen says:

      You should provide the assembly name of the project in which the Class resides. If this is your “web project” then that should be provided.

  4. so this should looks like this?

    I have this is .less file
    @line: getCustomStyle();
    .button {
    }
    .textother {
    color: @line;
    }
    #button1 {
    .button
    }
    #text2 {
    .textother
    }

    I call for Textother in HTML but nothing happens.

    [DisplayName(“UtilPlugins”)]
    public class UtilPlugins : IFunctionPlugin
    {
    public Dictionary GetFunctions()
    {
    return new Dictionary
    {
    { “getCustomStyle”, typeof(GetCustomColorFunction) }
    };
    }
    }
    public class GetCustomColorFunction : Function
    {
    protected override Node Evaluate(Env env)
    {
    Guard.ExpectNumArguments(1, Arguments.Count(), this, Location);
    Guard.ExpectNode(Arguments[0], this, Arguments[0].Location);
    var colorAttrName = Arguments[0] as Keyword;
    return new Color(129, 129, 129);
    }
    }

    this is the Plugin.

  5. What should be the best one ? Implement my own plugin to be used for populate my less variables (calling from my less file) or simply implement an IHttpHandler that appends my variables values in my less file?

    By the way, very nice article. Both:
    http://whoknew.dk/how-to-write-a-dotless-plugin-711.htm
    and http://whoknew.dk/dotless-dynamic-server-side-values-856.htm

    Best regards

Speak Your Mind

*