Category: LESS

Custom Functions With LESSPHP: Cheat sheet

My favorite feature of the fantastic lessphp library is the ability to easily add in your own custom functions. This makes working with LESS truly flexible as you can add a bunch of features that less doesn’t already have. Imagine having a css preprocessor that will do anything at your whim, well with lessphp you do. I’ve built some custom color scheme functions, a gradient image generator, a rem to px converter and more. The possibilities are endless.

The documentation on custom functions is fairly brief (make sure to read it!) so I started writing down a cheat sheet for reference purposes. For starters, it’s important to know how LESSPHP passes arguments to functions. You need to know what format the data will be in so you can process it inside your function.

Data Structure

The data structure your php function gets to work with depends on whether your custom less function received one argument or multiple arguments. If it’s a single argument, you’ll get an array like this:

/*
css example:
.test {
     padding: my_custom_function( 10px );
}
this array is sent on to your custom function
*/
array(3) {
  [0]=>
  string(6) "number"
  [1]=>
  string(2) "10"
  [2]=>
  string(2) "px"
}

This is pretty straightforward. The first value of the array contains the type of the value.

Arguments that are passed to your custom function are first processed and parsed by lessphp and then passed on as one of the following types:

  • keyword (a string that is a single word)
  • number (any numeric value, with or without a unit appended)
  • string
  • color

Each of these types also has its own data structure, which we will get to in a moment.

If you have multiple arguments, you’ll get a multidimensional array like the following:

/* .test{
    padding: my_custom_function( 20px, 62.5% );
}
the following is a vardump of what is passed on to the custom function
*/
array(3) {
  [0]=>
  string(4) "list"
  [1]=>
  string(1) ","
  [2]=>
  array(2) {
    [0]=>
    array(3) {
      [0]=>
      string(6) "number"
      [1]=>
      string(2) "10"
      [2]=>
      string(2) "px"
    }
    [1]=>
    array(3) {
      [0]=>
      string(6) "number"
      [1]=>
      string(4) "62.5"
      [2]=>
      string(1) "%"
    }
  }
}

Note that in case of multiple arguments the first value of the array is ‘list‘. You can use that value to check if your custom function should anticipate one or more arguments. If it’s a single argument the first value will be the value type, otherwise it will be equal to list. It’s important that you do a check like that if your function is designed to take a varying amount of arguments.

The second value in the array in case multiple arguments were passed is the delimiter used between the variables in the function call. When using custom functions it’s fine to separate arguments with spaces or with commas.

.test{
    color: my_color_function( @color1, @color2 );
    background-color: my_color_function( @color1 @color2 );
}

Data Structures Per Type

Color:

No matter what color notation (hex, color name, hsl(a) or rgb(a)) is used as an argument inside the less input, it will be parsed and sent on as an array in RGB format. If there is an alpha value, it will specify a fourth value containing the alpha value.

Keyword:

The keyword is the simplest value to deal with, as the array for this will contain ‘keyword’ as the first value in the array and the actual word as the second value in the array.

String:

Strings are returned with a nested array. The first value in the array contains the type ‘string’, the second value contains the delimiter (which could be double, single or no quotes for example) and the third value is an array containing the raw string.

Number:

For numeric values, the first value will as usual denote the type of the data (which will be number). The second value in the array will be the raw numeric value. The third value is the unit, which will be blank if the unit was passed without any unit.

Returning A Value

Your custom function has to send back a value and you have to follow the same data structure that the lessphp script likes to use.

To return a color, send back an array like this:

return array( 'color', $red, $green, $blue, $alpha );

You can also send a raw color value back, which basically means any kind of color notation you could enter in to css.

$rawcolor = 'hsla( 200, 10%, 60%, 0.4)';
return array( 'raw_color', $rawcolor );

To return a string, send it like this:

return array( 'string', $delimiter, array ( $string ) );

To return a keyword:

return array( 'keyword', $keyword );

To return a number:

return array( 'number', $number, $unit );

To call another less function:

$function = 'darken';
$arguments = array( '#FF330F', '10%' );
return array( 'function', $function, $arguments );

That covers the basics. As a final helper, here is what the array looks like when passing multiple arguments of various types. It should give you an easy reference to know how to deal with different types of arguments.

/*
in our less we are calling a custom function with tons of different variables to give you an idea of the array that gets passed to your custom function.
.test {
    my_custom_function( "Amatic SC", url(www.w3.org), 'http://html5.com', 800, regular, 100px, 40%, green, #FF44E3, rgba(30,40,254,0.1), hsl(100, 33%, 50%) );
}
And here's the vardump it produces:
*/
array(3) {
  [0]=>
  string(4) "list"
  [1]=>
  string(1) ","
  [2]=>
  array(11) {
    [0]=>
    array(3) {
      [0]=>
      string(6) "string"
      [1]=>
      string(1) """
      [2]=>
      array(1) {
        [0]=>
        string(9) "Amatic SC"
      }
    }
    [1]=>
    array(3) {
      [0]=>
      string(8) "function"
      [1]=>
      string(3) "url"
      [2]=>
      array(3) {
        [0]=>
        string(6) "string"
        [1]=>
        string(0) ""
        [2]=>
        array(1) {
          [0]=>
          string(10) "www.w3.org"
        }
      }
    }
    [2]=>
    array(3) {
      [0]=>
      string(6) "string"
      [1]=>
      string(1) "'"
      [2]=>
      array(1) {
        [0]=>
        string(16) "http://html5.com"
      }
    }
    [3]=>
    array(3) {
      [0]=>
      string(6) "number"
      [1]=>
      string(3) "800"
      [2]=>
      string(0) ""
    }
    [4]=>
    array(2) {
      [0]=>
      string(7) "keyword"
      [1]=>
      string(7) "regular"
    }
    [5]=>
    array(3) {
      [0]=>
      string(6) "number"
      [1]=>
      string(3) "100"
      [2]=>
      string(2) "px"
    }
    [6]=>
    array(3) {
      [0]=>
      string(6) "number"
      [1]=>
      string(2) "40"
      [2]=>
      string(1) "%"
    }
    [7]=>
    array(4) {
      [0]=>
      string(5) "color"
      [1]=>
      string(1) "0"
      [2]=>
      string(3) "128"
      [3]=>
      string(1) "0"
    }
    [8]=>
    array(4) {
      [0]=>
      string(5) "color"
      [1]=>
      float(255)
      [2]=>
      float(68)
      [3]=>
      float(227)
    }
    [9]=>
    array(5) {
      [0]=>
      string(5) "color"
      [1]=>
      float(30)
      [2]=>
      float(40)
      [3]=>
      float(254)
      [4]=>
      float(0.1)
    }
    [10]=>
    array(4) {
      [0]=>
      string(5) "color"
      [1]=>
      float(113.475)
      [2]=>
      float(169.575)
      [3]=>
      float(85.425)
    }
  }
}

How To Specify Top Level Selectors Within Mixins With LESSphp

While creating mixins for cool snippets of reusable css, I had trouble figuring out a way to set styles for top level elements while inside a mixin. To illustrate, let’s look at this example from CSS tricks which uses pseudo elements to create a bar that breaks out a container to touch both sides of the screen.

section {
   width: 50%;
   margin: 0 auto;
}
h2 {
   position: relative;
   background: black;
}
h2:before, h2:after {
   content: '';
   position: absolute;
   background: black;  /* Match the background */
   top: 0;
   bottom: 0;
   width: 9999px;   /* some huge width */
}
h2:before {
   right: 100%;
}
h2:after {
   left: 100%;
}
html, body {
    overflow-x: hidden;
}

It’s a pretty neat effect. The question is, how would you turn this into a mixin, so you can use it on an element of your choice?

Most likely you’ll start out with something like this:

.extended-bar() {
     position: relative;
     background: black;
   &:before, &:after {
     content: '';
     position: absolute;
     background: black;  /* Match the background */
     top: 0;
     bottom: 0;
     width: 9999px;   /* some huge width */
   }
   &:before {
     right: 100%;
   }
   &:after {
     left: 100%;
   }
}

You could then drop this in where you like it. Let’s for example apply it to a h3 heading:

h3 {
   .extended-bar;
}

But we’re missing something. The effect relies on a parent container to have a width. In this example  it’s the section element. We can reasonably assume that this will be specified in the normal course of the stylesheet anyway.

The property applied to the html and body element though is really important (it prevents a horizontal scrollbar from displaying). How do we put that in there? We can’t just drop this into the mixin because it will be prefixed by whatever element the mixin is applied to, in this case the h3 selector. A problem.

So I looked at a different technique as a workaround and arrived at cool feature called selector expressions. This functionality allows you to dynamically create a selector based on a given parameter. With this we can create a mixin that we can call from the top level and let it generate our desired selector dynamically.

.extended-bar( @selector ) {
  html, body {
    overflow-x: hidden;
  }
 }
 (e (@selector ) {
   position: relative;
   background: black; 

 &:before, &:after {
   content: '';
   position: absolute;
   background: black;  /* Match the background */
   top: 0;
   bottom: 0;
   width: 9999px;   /* some huge width */
 }
 &:before {
   right: 100%;
 }
 &:after {
   left: 100%;
 }

}

To create our effect we just need to call it with the desired selector:

.extended-bar( h2 )

This solution will print out the styles like the original css example. This mixin is not the best piece of less to actually use in the wild. In the event that you call the mixin multiple times ugly style duplication issues arise.

I am working on a CSS post processing tool that remedies style duplication issues, if you’re interested, sign up to my newsletter list below and I’ll let you know when I release it.
[yks-mailchimp-list id=”85fd778c18″]