Anas Nakawa
Anas Nakawa

Reputation: 2015

Dynamic CSS properties in LESS?

I am writing a less stylesheet for a bi-directional website, and I need to write a single less file and produce two different stylesheets one for left-to-right layouts (ltr) and the other for right-to-lef layouts (rtl)

so when i am styling for rtl layouts, i want to convert every left value to be right, wither it is a float, padding or margin,

to do so, I have defined the required variables at the top of my less file as follows:

@left: right;
@right: left;

simply, i can float elements dynamically like this

float: @left; // this will be floated to left in ltr layout while it will be right in rtl layout.

but my problem is that I want be able also to use these variables to produce dynamic properties, for example when positioning elements absolutely, or relatively, i want to be able to write the left or right property dynamically depending on the value of the @left variable as follows

position: relative;
@left: 10px;

but this gives an error on compiling, any clue how to do that, Note that I have done this using SASS, but till now I couldn't get it to work with LESS ?

Upvotes: 6

Views: 6541

Answers (4)

Moshe Simantov
Moshe Simantov

Reputation: 4503

You may check this library: https://github.com/DevelopmentIL/direction.less

It has large support for many css-rules. For example:

@direction: rtl; // use `rtl` or `ltr`
@import "direction.less";

body {
  .direction();
}

.float {
  .float();
}

.box {
  display: inline-block;
  .margin-left(4px);
  .padding-right(1em);
}

Will output:

body {
  direction: rtl;
}

.float {
  float: right;
}

.box {
  display: inline-block;
  margin-right: 4px;
  padding-left: 1em;
}

Upvotes: 1

jsgoupil
jsgoupil

Reputation: 3997

If you are using DotLess, there is something more interesting that can be used instead of defining a finite number of mixins here.

It is built-in as the RTL plugin.

https://github.com/dotless/dotless/wiki/Plugins

dotless.Compiler.exe -p:Rtl:forceRtlTransform=true,onlyReversePrefixedRules=false file.less

Upvotes: 2

Anas Nakawa
Anas Nakawa

Reputation: 2015

Below is the solution that lets you write your styles once using LESS, then compile them to be two different css stylesheets for both rtl, and ltr layouts.

basically we'll have three LESS files (they can be more!):

style-ltr.less  // this where we hold the rtl variables
style-rtl.less  // rtl variables
main.less       // here we'll write our styles

in style-ltr.less define the following variables:

@left: left;
@right: right;

@import "main.less";

while in style-rtl.less they will have the following values:

// reflect variables
@left: right;
@right: left;

@import "main.less";

now in main.less, we'll define the following mixins

.left(@distance) when (@left = left) {
    left: @distance;
}
.left(@distance) when (@left = right) {
    right: @distance;
}
.right(@distance) when (@right = right) {
    right: @distance;
}
.right(@distance) when (@right = left) {
    left: @distance;
}

// now we can style our elements using these mixins
div.something {
    position: relative;
    .left(10px);
    float: @left;
}

now all we have to do is to include style-rtl.less in rtl pages include (or the compiled css version), as well to include style-ltr.less in ltr pages, and div.something will be floated to the left on ltr pages, while it will be floated to the right on the rtl pages

Note that you can define padding, margin, border-radius ...etc. using the same way here.

UPDATE

I created two projects on github to help building a bi-directional applications

Credits:

inspired by my dear friend Victor Zamfir

Upvotes: 9

bzx
bzx

Reputation: 1364

Update: Take a look at Guard Expressions in LESS - maybe this can help a bit, you can create kinda conditional statements but you have to use mixins..

I understand your point, but on the other hand I'd approach this problem differently. Here's another simplified take - you define variables for differences in both cases, and then import as you wish - for example, place in master-ltr/master-rtl and import the rest of stylesheet after:

// master-ltr.less
// LTR:
@sidebar-pos-left: 10px;
@sidebar-pos-right: 0px;
@content-float: left;
...
@import 'styles.less';

// your styles.less would have:
.content {
  float: @content-float;
  ...
}
.sidebar {
  position: relative;
  left: @sidebar-pos-left;
  right: @sidebar-pos-right;
  ...
}

.. or maybe more effective - create separate file with vars-ltr/rtl, and then import into your master-ltr/rtl.

Not sure how really complicated the layout is, but maybe this gives you something you can use?


I've tried this using String Interpolation but it looks like the variables can be only embedded on the "variable" side of the definition. I might be wrong here, maybe folks know how to achieve it.

I'd do that bit different, not because it's not possible with LESS, but because of organization of code and general more-sane approach (it's not good to name something "left" when sometimes it's going to be actually "right" :)

For your second problem with position:relative, here's my take:

<div class="element element-ltr">...</div>

.element {
  position: relative;
  &.element-ltr {
    left: @element-side-position;
  }
  &.element-rtl {
    right: @element-side-position;
  }
}

That solution is kinda OK, but if you're stylesheet is huge, you're going to end up with lots of additional classes for -ltr and -rtl. Not really a big pain but you could actually make it even more useful by broadening the scope of the -ltr/-rtl to the parent containers (like header/content/sidebar/footer) and then adjust from there. They may have lots in common, like background, colors, font-size.. and you will just work on the differences in their -ltr/-rtl versions.

Also, read up on LESS Namespaces, they can offer you even more organizational powers.

Hope this helps!

Upvotes: -1

Related Questions