Convert units with LESS mixin
Tags: less, css, mixin, frontend
Published: 2020-07-29 14:31:38

This post will be useful for those who use [LESS](http://lesscss.org/) preprocessor for writing CSS styles. 

> Less (which stands for Leaner Style Sheets) is a backward-compatible language extension for CSS.

Sometimes we are faced with the task of converting units from one value to another. An example from real life - a designer gave us a layout in which all elements are sized in pixels, and according to the terms of the technical task, we need rem units in the styles.

Of course, you can sit and manually calculate the value for each property, but you can do it easier - write a mixin that will do this work for us - we will give it pixels, and it will generate our properties with rem units. 

### We want to:

#### Rule 1
```less
.box {
    .px2rem(height, 16px);
}
```
will convert to:
```css
.box {
    height: 1rem;
}
```
#### Rule 2
```less
.box {
    .px2rem(padding, 16px 32px 24px 0);
}
```
will convert to:
```css
.box {
    padding: 1rem 2rem 1.5rem 0;
}
```
Let's write our **mixin**. At the input, it receives property and a set of values (it can be one value or several). If one of the values is `0`, **no units should be added**.

```less
.px2rem(@attr: width; @size: 16) {
    .get-values(length(@size));
    .get-values(@s, @i: 1) when (@i =< length(@size)) {
        @value: extract(@size, @i);

        & when not(@value = 0) {
                @{attr}+_: unit( @value / 16, rem );
        }

        & when (@value = 0) {
                @{attr}+_: 0;
        }

        .get-values(@s, @i + 1);
    }
}
```

Let's do the same for the reverse transformation from `rem` to `pixels`:

```less
.rem2px(@attr: width; @size: 16) {
    .get-values(length(@size));
    .get-values(@s, @i: 1) when (@i =< length(@size)) {
        @value: extract(@size, @i);

        & when not(@value = 0) {
                @{attr}+_: unit( 16 * @value, px );
        }

        & when (@value = 0) {
                @{attr}+_: 0;
        }

        .get-values(@s, @i + 1);
    }
}
```

Similarly, we can write converters for other units, for example, `pt` to `pixels`, `pt` to `rem`, and reverse.

```less
.pt2px(@attr: width; @size: 16) {
    .get-values(length(@size));
    .get-values(@s, @i: 1) when (@i =< length(@size)) {
        @value: extract(@size, @i);

        & when not(@value = 0) {
                @{attr}+_: unit( round(@value * 1.333333) , px );
        }

        & when (@value = 0) {
                @{attr}+_: 0;
        }

        .get-values(@s, @i + 1);
    }
}

.px2pt(@attr: width; @size: 16) {
    .get-values(length(@size));
    .get-values(@s, @i: 1) when (@i =< length(@size)) {
        @value: extract(@size, @i);

        & when not(@value = 0) {
                @{attr}+_: unit( round(.75 * @value), pt );
        }

        & when (@value = 0) {
                @{attr}+_: 0;
        }

        .get-values(@s, @i + 1);
    }
}

.pt2rem(@attr: width; @size: 16){
    .get-values(length(@size));
    .get-values(@s, @i: 1) when (@i =< length(@size)) {
        @value: extract(@size, @i);

        & when not(@value = 0) {
                @{attr}+_: unit( round(@value * 1.333333 / 16), rem );
        }

        & when (@value = 0) {
                @{attr}+_: 0;
        }

        .get-values(@s, @i + 1);
    }
}

.rem2pt(@attr: width; @size: 16){
    .get-values(length(@size));
    .get-values(@s, @i: 1) when (@i =< length(@size)) {
        @value: extract(@size, @i);

        & when not(@value = 0) {
                @{attr}+_: unit( round(.75 * 16 * @value), pt );
        }

        & when (@value = 0) {
                @{attr}+_: 0;
        }

        .get-values(@s, @i + 1);
    }
}
```