Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
paulhhowells opened this issue Aug 13, 2013 · 26 comments
Open

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

paulhhowells opened this issue Aug 13, 2013 · 26 comments

Comments

@paulhhowells
Copy link

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
Copy link
Member

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

@kent78
Copy link

kent78 commented Sep 25, 2013

Plus one for this feature.

@NetzwergX
Copy link

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
Copy link

Soviut commented Oct 27, 2013

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
Copy link

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.

@seven-phases-max seven-phases-max changed the title enable &:extend() to take a class as a variable, just as it takes a class name as a string Enable &:extend() to take a class as a variable Sep 18, 2014
@mailvidi
Copy link

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
Copy link

EloB commented Jul 27, 2015

+1

1 similar comment
@awcross
Copy link

awcross commented Sep 16, 2015

+1

@pawelphilipczyk
Copy link

+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
Copy link

+1

@toastal
Copy link

toastal commented Jan 14, 2016

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

@machiaveli88
Copy link

+1

@kerryj89
Copy link

kerryj89 commented Aug 1, 2016

+2

@shooftie
Copy link

+1

3 similar comments
@dashawk
Copy link

dashawk commented Jun 15, 2017

+1

@junyo
Copy link

junyo commented Jun 23, 2017

+1

@sonnyprince
Copy link

+1

@DennisJohnsen
Copy link

+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
Copy link

+1

@kincaider
Copy link

+2

@stale
Copy link

stale bot commented Jan 25, 2018

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.

@stale stale bot added the stale label Jan 25, 2018
@DennisJohnsen
Copy link

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.

@stale stale bot closed this as completed Feb 9, 2018
@MadLittleMods
Copy link

MadLittleMods commented Feb 9, 2018

^ Stale bot fail? #1485 (comment)

@Jhanwarlal
Copy link

Jhanwarlal commented Jun 7, 2018

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
Copy link

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
Copy link

luxlogica commented Mar 28, 2020

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests