Using a General Purpose Selector as a Mixin in Less CSS

Using a general purpose selector as a mixin in LESS CSS

First off, I would strongly discourage doing such things. Instead, try to use the power of CSS and build your HTML such that the bootstrap rules apply, for example. Anyway, since you asked for it, here is the solution.

The problem is not the complexity of the selector, or the child rule, but the tag name selector part (i. e. the li). So, what we have to fix is the mixin parser only matching classes and ids. I guess we would not want to tamper with the first class or id test, since that is probably needed to distinguish mixins from normal CSS rules (although the tests run fine with that check commented out). (Actually, there is a parser preference in action, and the only thing tried after mixins are comments and directives, so we can safely remove that check as well). However, we can easily allow tag names in later parts of the mixin selector by adding a question mark after [#.] in the matching regular expression. So

while (e = $(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)) {

– i. e. line 825 – becomes

while (e = $(/^[#.]?(?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)) {

The test cases still run through fine, afterwards, but subtle breakage my occur, so be cautious.

Edit: There is a GitHub Issue for the same problem. Apparently the less folks rather want the mixin feature to be more narrow and function-like, instead of allowing a more flexible … well … mixing in of rules. With regard to the CSS output, that's probably reasonable.

Use function/mixin in Less selector

Yes, you can form the selector dynamically by using a mixin like below. The mixin accepts two parameters out of which one is the frame number for which the selector has to be generated and the other is the set of rules (ruleset) that is applicable for this frame.

Passing Rulesets to Mixins was introduced only in Less v1.7.0 and hence this code would not work with lower versions of the less compiler.

Note: If the properties/rules for all frames had some common pieces this can be reduced further using loops, but since they are different we have to pass the ruleset corresponding to each frame as part of the mixin call.

Less:

.frameselector(@number, @ruleset){
@sel: ~"[data-fragment = @{number}]";
@{sel}.active ~ .frame{
@ruleset();
}
}
.slide1{
.frame{
/* some code */
}
.frameselector(1,{
/* all rules or props belonging to frame 1 */
color:red;
background: beige;
});
.frameselector(2,{
/* all rules or props belonging to frame 2 */
color:green;
background: white;
});
}

Compiled CSS Output:

.slide1 .frame {
/* some code */
}
.slide1 [data-fragment = 1].active ~ .frame {
color: red;
background: beige;
}
.slide1 [data-fragment = 2].active ~ .frame {
color: green;
background: white;
}

CodePen Demo

Use selector name as variable in LESS mixin

You cannot "read" the selector name.

However, you can build the selector in conjunction to linking with the file name in a mixin, something like so:

LESS

.buildSelectorConnection(@selectorName, @pre: ~'') {
@{pre}@{selectorName} {
background-image: url('images/@{selectorName}.png');
}
}

.buildSelectorConnection(header, ~'#');
.buildSelectorConnection(do-a-class, ~'.');

CSS Output

#header {
background-image: url('images/header.png');
}
.do-a-class {
background-image: url('images/do-a-class.png');
}

However, it would take quite a bit more logic, decision making on your part, and some javascript coding in LESS if you wanted to make such a thing be able to handle something like div#menu ul li where the actual filename generated was something like div-menu-ul-li.png.

Nevertheless...

Something like this could be done:

LESS

.buildSelectorConnection(@selectorName, @pre: ~'', @post: ~'') {
@{pre}@{selectorName}@{post} {
background-image: url('images/@{selectorName}.png');
}
}

.buildSelectorConnection(menu, ~'div#', ~' ul li');

CSS Output

div#menu ul li {
background-image: url('images/menu.png');
}

This basically lets you build any selector string, but the file name itself will only be that initial selector passed in, which needs to be something valid for a file name.

Can I use mixins to generate new mixins in LESS?

I think the simplest way for you to achieve this is by using a single parametric mixin like given below. This avoids the need for all those iterations, dynamic mixin creations etc.

@sizes: xxs, xs, sm, md, lg;
@screen-xxs: 100px;
@screen-sm: 200px;

.MQ(@content, @sizeName, @max-min) { /* get ruleset, size name and min/max as input */
@selector: ~"(@{max-min}-width: @{screen-@{sizeName}})"; /* form the media selector */
@media @selector { /* use it */
@content();
}
}

.generic-class {
background: black;
.MQ({
background: transparent;
}, /* ruleset */
sm, /* screen size */
max /* min/max */
);
}

If the mixins are for your own usage then this is all that you need. If it is for distribution as library then you may want to put some guards on @sizeName and @max-min variables to restrict invalid values.

Note: Less compiler always had a problem with the interpolation here - @media (min-width: @screen-@{sizeName}) also (I am not sure if it has been addressed) and that's why I created a temp variable.

LESS CSS: Reuse generated .@{name} class as a mixin

Unfortunately. The selector interpolation is just string interpolation, and the string gets then printed into css, so no class object is generated in the less run.

So you can design a generator/mixin, that includes your operation:

#genMarginTop (@size) {
margin-top: @size;
}

But then build classes by calling the mixins / generators:

.mtStandard {#genMarginTop(40px);}
.mtHalf {#genMarginTop(20px);}

And this way they are class objects that you can use for mixin =)

.someClass {
background-color: #FFF;
.mtStandard;
//more of this stuff
}

This looks a bit silly in this simple example, but maybe something like this:

 #bggenerator (@color) {
background-color: @color;
}
#bggenerator (@color, dark) {
@blend : @color + #842210;
background-color: darken(@blend, 30%);
}
#bggenerator (@color, @url, @rest) {
background: "@{color} url('@{url}') @{rest}";
}

.mtStandard {
#genMarginTop(40px);
}

.someClass {
.mtStandard;
#bggenerator(#FFF, "bgimage.png", left top no-repeat);
//more of this stuff
}

Or something that does even more exciting stuff with the arguments

use variables on mixin or extend in Less.js

You are trying to call a mixin using selector interpolation, which is not possible.

As for extend, Less documentation states it clearly:

Extend is NOT able to match selectors with variables. If selector contains variable, extend will ignore it.

Use mixin argument to create class name in LESS

I think I have the solution using Variable Names.

Less

@green: #5FBEAA;

.text-color(@colorname) {
@color: ~"@{colorname}";
.text-@{color}{
color: @@color;
}
}

.text-color(green);

Output

.text-green {
color: #5FBEAA;
}

LESS CSS guards without mixin

no, it is not possible in that form.

you could use a variable equal to 0 or 1 and multiply by 20 and then always output a rule, or use JavaScript (i would advise you to avoid this) to convert true to 0 or 20 and always output a rule, but if you want the property added conditionally, you need guards.



Related Topics



Leave a reply



Submit