less / less.js

Less. The dynamic stylesheet language.
http://lesscss.org
Apache License 2.0
17.02k stars 3.41k forks source link

Enable &:extend() to take a class as a variable #1485

Open paulhhowells opened 11 years ago

paulhhowells commented 11 years ago

Feature Request

It would be very powerful if :extend could be passed a class name as a variable.

e.g. something like:

&:extend(@{extending-class-string});

or:

&:extend(@extending-class);

Use Cases:

(tl;dr this would be brilliant for making mixins reusable and for grid systems)

If you want to extend a class with numbered additions it is possible, as long as you create a mixin for every class you want to extend.

for example:

.dec-loop (@length) {
  @i: @length;

  .extended-class {
    float: left;
  }

  .loop (@i) when (@i > 0) {   
    @loop-class: ~".dec-@{i}";   
    @{loop-class} {
      &:extend(.extended-class);
    }    
    .loop(@i - 1);
  }
  .loop(@i);  
}
.dec-loop(4);

will create:

.extended-class,
.extended-class-4,
.extended-class-3,
.extended-class-2,
.extended-class-1 {
  float: left;
}

However the name of the class being extended is hard coded into the mixin, requiring a new mixin for every class you want to extend in this way. But imagine if this worked:

.inc-loop (@extending-class-name; @total) {
  @i: 1;
  @extending-class-string: ~".@{extending-class-name}";

  .loop (@i) when (@i =< @total) {   
    @loop-class: ~".@{extending-class-name}-@{i}";   
    @{loop-class} {     
      &:extend(@{extending-class-string}); // feature request
    }    
    .loop(@i + 1);
  }
  .loop(@i);  
}

.extended-class {
  float: left;
}
.inc-loop(extended-class, 8);

or even better, if this worked!:

.inc-loop (@extending-class-name; @total) {
  @i: 1;
  @extending-class-string: ~".@{extending-class-name}";

  .@{extending-class-name} { // another feature request?
    float: left;
  }

  .loop (@i) when (@i =< @total) {   
    @loop-class: ~".@{extending-class-name}-@{i}";   
    @{loop-class} {
      &:extend(@{extending-class-string}); // feature request
    }    
    .loop(@i + 1);
  }
  .loop(@i);  
}

.inc-loop(extended-class, 8);

Because this would allow us to have a SINGLE mixin, that could create and extend many different classes. Which might be just what you want if you are working on a grid layout system.

P.S. I suspect that:

.@{extending-class-name}{}

may not work for quite a different reason. Somehow it does not create a class that can be extended, however this probably ought to be a separate Feature Request or Bug Issue.

lukeapage commented 11 years ago

technically, its feasible.. though it again relies on interpolating the variable and then parsing the result into a selector

kent78 commented 11 years ago

Plus one for this feature.

NetzwergX commented 10 years ago

This is more or less a must-have. You can not extend Font-Awesome 4.0 anymore due to this. They have the basic functionality defined as .@{fa-css-prefix}{ /* stuff here */}. Might not be the smartest move in their part, but the way LESS currently works you have to hope that the other lib you want to use does it "right" (in this case, not using the full capabilities of LESS).

Soviut commented 10 years ago

A considerable amount of discussion was had regarding the development of the extend() but not every edge case can always be caught. The more examples you can provide, the easier it is to justify additional development AND gives concrete tests that can be applied.

NetzwergX commented 10 years ago

Well, I think Font-Awesome 4.0 is already a pretty good example of why it is bad. It just seems very odd that you can not use one feature of LESS (in this case, .@{fa-css-prefix}) without breaking another one (here extend).

Things that are implemented should work in any case (I hardly think this is an "edge case").

I don't have additional examples, because FA is where this caught my eye. And it isn't even mentioned in the docs anywhere, that extend supports only a very limited functionality.

mailvidi commented 9 years ago

Not sure what's the priority on this one, but would be very useful to extend using variables; so +1 for these feature!

~VS

EloB commented 9 years ago

+1

awcross commented 9 years ago

+1

pawelphilipczyk commented 9 years ago

+1 I have a simple example for this feature:

.icon(@name) {
    @prefix: "fa";
    @icon: "@{prefix}-@{name}";
    &:extend(.@{prefix}, .@{icon} all);
}

And now this allows me to simply add icons like this: .icon(times);

pm-desjonqueresg commented 8 years ago

+1

toastal commented 8 years ago

+1 ... literally tried to do the same things as all ya'll with fonts and the compilation said it was no bueno.

machiaveli88 commented 8 years ago

+1

kerryj89 commented 8 years ago

+2

shooftie commented 7 years ago

+1

dashawk commented 7 years ago

+1

junyo commented 7 years ago

+1

sonnyprince commented 7 years ago

+1

DennisJohnsen commented 7 years ago

+1 for this feature

I recently wanted to move the LESS compilation of our icons into it's own NPM package, and then import the .css file into our .less to keep our compile time down. The icon less is looping over a large list, which causes the compile time to increase a lot. The only reason we want to move it out of our pipeline.

Then i thought we could just extend the classes instead of using a mixin to get the 'content' from said list. BUT i wanted to make it easier for us to extend the icons, so i figured to move the extend into a mixin and then you simply have to remember the name of your icon and magic.

Apperently extend dosn't take variables. Sad face. This is my concrete example if it helps in anyway:

CSS file from NPM package: custom-icons.min.css:
.icon-apple:before {
    content: 'apple';
}
.icon-banana:before {
    content: 'banana';
}

LESS:
@import (less) './node_modules/custom-icons/custom-icons.min.css';

.get-icon(@name) {
    @sel: e('.icon-@{name}:before');
    &:extend(@sel);
}

.foo { .get-icon(apple); }
.bar { .get-icon(banana); }

RESULT: 
.icon-apple:before,
.foo {
    content: 'apple';
}

.icon-banana:before,
.bar {
    content: 'banana';
}
Cedric-ruiu commented 7 years ago

+1

kincaider commented 7 years ago

+2

stale[bot] commented 6 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

DennisJohnsen commented 6 years ago

This is a very handy feature, as it combines two powerful less tools.

The ability to extend through mixins would be very powerful to have in LESS.

Commenting to keep it from going stale.

MadLittleMods commented 6 years ago

^ Stale bot fail? https://github.com/less/less.js/issues/1485#issuecomment-360703750

Jhanwarlal commented 6 years ago

How to extend class using prefix variable less? I am using below code

@class-prefix               : y;
@extending-class-string     : ~".@{class-prefix}";
.@{class-prefix}-card-eq-height {
  height: calc(100% ~"-" @card-margin-base);
}

.@{class-prefix}-product-eq-height {
  **&:extend(@{extending-class-string}-card-eq-height);**
}

This is not working for me

fernandogmar commented 6 years ago

Hi everybody,

I aggree +1 to this feature...

just for now in my case it was a limited number of options to extend and I knew them in advance so I could do something like example below. It is a simple example but you can get the point in case it helps you:

    .create-icon-class-with-prefix(awesome, positive);
    .create-icon-class-with-prefix(awesome, negative);

    .create-icon-class-with-prefix(@prefix, @modifier) {
         .@{prefix}-icon--@{modifier} {
                .extend_class(@modifier);
         }
    }

    .extend_class(@value) when (@value=positive) {
        &:extend(.my-positive-class);
    }

    .extend_class(@value) when (@value=negative) {
        &:extend(.my-negative-class);
    }

Explanation: :extend() doesn' t accept variables (for now), the selector has to be specified, so I created a mixin that can accept variables, with the limited number of classes that could be extended.

luxlogica commented 4 years ago

I am trying to create a utility-first framework akin to TailwindCSS in LESS, and it is quite impossible, without this feature. Such frameworks need to auto-generate a large number of selectors, all of which will apply the same CSS property - so it's important to be able to use :expand, so we don't end up with gigantic duplication of properties.

For example, the framework might have classes that apply a certain background colour, like .bg-blue. That background colour might need to be applied only on specific states, so the framework might also need to create classes like bg-blue-hover, bg-blue-focus, bg-blue-visited, and so on. All of these classes would basically be applying just one property: background-color: blue;. The issue is, that we don't know in advance what colours the user will want the framework to generate, so it needs to be generated dynamically.

We can loop easily enough using each(), but that will generate a stylesheet where every class has the background-color attribute repeated - taking up a huge amount of space:

@colours: {
  red: tomato;
  green: forestgreen;
  blue: dodgerblue;
};

@states: hover, focus, link, active, visited;

each(@colours) ,(@v, @k){
  .bg-@{k} { 
    background-color: @v;
    each(@states,{
      &-@{value}:@{value}{ background-color: @v; } // DUPLICATION PROBLEM - need :expand
    } 
}

This produces a needlessly long file, where each colour/state has the background-color property individually defined:

.bg-red { background-color: tomato; }

.bg-red-hover:hover { background-color: tomato; }

.bg-red-focus:focus { background-color: tomato; }

.bg-red-link:link { background-color: tomato; }

.bg-red-active:active { background-color: tomato; }

.bg-red-visited:visited { background-color: tomato; }

What we want instead is:

.bg-red,.bg-red-hover:hover, 
.bg-red-focus:focus, 
.bg-red-link:link, .bg-red-active:active, 
.bg-red-visited:visited { background-color: tomato; }

Ideally, to do this we should just be able to use the parent selector with the :extend, inside the each() loop, like this:

 &-@{value}:@{value}:expand(&){ } // :expand will list selector along parent selector

At the moment, there does not seem to be a way to achieve this with Less. Unfortunately, this is not a minor issue - it's pretty major, when you consider the amount of classes and variants that we'll need to create - for margins, padding, borders, typography, etc., and that each variant has to be repeated under media queries, for every breakpoint (ends up as thousands and thousands of needlessly repeated property lines).

This means that using another language would enable us to create much smaller compiled files - and that ends up being a very compelling argument to switch. It is even more discouraging to see that this issue has been raised 7 years ago, and has been marked as 'low priority'. I sure hope the team will reconsider.

aleen42 commented 1 month ago

So is there any plan for implementing this feature?

wafiamustafa commented 1 week ago

+1 I recommend adding this feature it is powerful to use both mixin and extend.

.solid-icon( @icon,  @size) {
  &:extend(
    .fas,
    .fa-@{size},
    .fa-@{icon} all
  );
}