How can I target an element within a class using less? - less

I would like to target specific elements within a class using less.
In this case, I would like to target elements of class button, but within that I would like to target an anchor tag a.
Currently I have:
.button {
/* lots of bits and pieces go here, hence
the need to keep it at a class level
*/
/* further down, but still in .button */
/* Attempt 1 - fails: compiled = a .button (note the space)
a& {
height:20px;
}
/* Attempt 2 - fails: compiled = .buttona
&a {
height:20px;
}
}
I basically want it to compile to:
a.button
This is so I can create elements such as:
<a class="button">
<button class="button">
But slightly alter it when its an anchor. I don't want to throw in the it's a bug in less! card too early, but if I use &.another-class it works as expected (compiled: .button.another-class, but not when targeting elements

You're using an old version of less. The code below generates the correct CSS using less 1.3.3
.button {
a& {
height:20px;
}
}
generates:
a.button {
height: 20px;
}

#Allen Bargi answer is correct, yet only for this specific scenario. I am a little confuse about what you want to achive.
As #Allen Bargi pointed out, this will target "a" lements with a "button" class and generates a
.button {
a& {
height:20px;
}
}
It generates:
a.button {
height: 20px;
}
Meanwhile, this below will target "a" elements contained whitin an element with a "button" class. which seems to me was your original objective.
.button {
a {
height:20px;
}
}
It generates:
.button a {
height:20px;
}
Both solutions migt work fine in this case because you are using the same "button" class for both the parent and the child elements, but they are not targeting the same elements.
I hope this helps.

Related

How can I use ampersand-prefixed selector as mixin? [duplicate]

In Less, I can write:
.outer {
.inner {
color: red;
}
}
.test {
.outer .inner;
}
But when I write:
.outer {
&-inner {
color: red;
}
}
.test {
.outer-inner;
}
When I remove the .test, the .outer-inner output properly, but when I add it back, the compiler says
.outer-inner is undefined.
Is there anyway to re-use the styles of .outer-inner?
Calling a mixin whose selector is formed by concatenation is currently not possible with Less. However the same is possible for selectors formed at compilation time using interpolation (also referred to as dynamically formed selectors).
The below (interpolated/dynamically formed selector) would work fine.
#selector: .box;
#{selector}{
color: red;
.child{
color:blue;
}
}
.demo{
.box; /* will create both parent & child */
}
.container{
    &.box{
    background: black;
}
}
.demo2{
    .container.box;
}
whereas, the following example will not work.
.container{
&-box{
color: blue;
}
}
.demo2{
.container-box; /* this will not work */
}
Currently, one work-around to the scenario in question is to create two separate Less files.
In the first file (test.less) add the below code and compile it into a CSS file.
.outer {
&-inner {
color: red;
}
}
In the second file, import the CSS created from the first file with the (less) directive and then call/re-use the mixin.
#import (less) "test.css";
.test {
.outer-inner;
}
Note: As mentioned in comments by seven-phases-max, this issue is similar to this item. However both these issues are not the same as extend will not work with both interpolated selector (dynamically formed) and concatenated selector.
Option 2: Another option would be to write a dummy mixin or a separate detached ruleset with common properties and make use of it like below.
#dummy: {color: red}; // detached ruleset
.outer{
&-inner{
#dummy();
}
}
.test{
#dummy();
}
or
.dummy() {color: blue}; // dummy mixin and would produce no extra selector in output as it has parentheses.
.outer{
&-inner{
.dummy;
}
}
.test{
.dummy;
}

LESS: how to convert a list of called mixins to a for loop with a unique call

I crated many mixins to generate different kinds of classes for various purposes. Specifically I have to use a unique colorizer set using the standard bootstrap variable name, such as (only an example):
#type-primary: #fff;
#type-success: #f00;
#type-info: #ff0;
#type-default: #000;
#type-warning: #0f0;
#type-danger: #0ff;
Actually I created my mixins in the following form, with a "mother" as prefix to which I attached various suffixes
.text
{
&-primary { .color_text(#type-primary); }
&-success { .color_text(#type-success); }
&-info { .color_text(#type-info); }
&-default { .color_text(#type-default); }
&-warning { .color_text(#type-warning); }
&-danger { .color_text(#type-danger); }
}
After this, I can then create the final called mixin such as (so simple because it's only an example)
.color_text (#color)
{
color:#color;
}
I woud like to automate and optimize .text mixin to avoid many repeated rows, I think with a for loop. How could be possible?
Final results should be (in this case)
.text-primary {
color: #fff;
}
.text-success {
color: #f00;
}
.text-info {
color: #ff0;
}
.text-default {
color: #000;
}
.text-warning {
color: #0f0;
}
.text-danger {
color: #0ff;
}
In PSEUDO-CODE something like this could be ideal
#type-primary: #fff;
#type-success: #f00;
#type-info: #ff0;
.createContextClass("classNamePrefix",{#type-primary,#type-success,#type-info},mixinToBeCalled);
// Another call could be
.createContextClass("otherClassNamePrefix",{#type-primary,#type-success},otherMixinToBeCalled);
where, in relation to my original code, classNamePrefix should be the name of first part of final CSS class, then is passed an array with all kind of suffix that I wish in final CSS code, and mixinToBeCalled is the mixin that creates all css rules for final .text-primary, .text-success, .text-info.
For the moment, following Seven-Phases-Max' suggestion, I improved his solution in the following way

How to re-use a mixin whose selector is formed using concatenation

In Less, I can write:
.outer {
.inner {
color: red;
}
}
.test {
.outer .inner;
}
But when I write:
.outer {
&-inner {
color: red;
}
}
.test {
.outer-inner;
}
When I remove the .test, the .outer-inner output properly, but when I add it back, the compiler says
.outer-inner is undefined.
Is there anyway to re-use the styles of .outer-inner?
Calling a mixin whose selector is formed by concatenation is currently not possible with Less. However the same is possible for selectors formed at compilation time using interpolation (also referred to as dynamically formed selectors).
The below (interpolated/dynamically formed selector) would work fine.
#selector: .box;
#{selector}{
color: red;
.child{
color:blue;
}
}
.demo{
.box; /* will create both parent & child */
}
.container{
    &.box{
    background: black;
}
}
.demo2{
    .container.box;
}
whereas, the following example will not work.
.container{
&-box{
color: blue;
}
}
.demo2{
.container-box; /* this will not work */
}
Currently, one work-around to the scenario in question is to create two separate Less files.
In the first file (test.less) add the below code and compile it into a CSS file.
.outer {
&-inner {
color: red;
}
}
In the second file, import the CSS created from the first file with the (less) directive and then call/re-use the mixin.
#import (less) "test.css";
.test {
.outer-inner;
}
Note: As mentioned in comments by seven-phases-max, this issue is similar to this item. However both these issues are not the same as extend will not work with both interpolated selector (dynamically formed) and concatenated selector.
Option 2: Another option would be to write a dummy mixin or a separate detached ruleset with common properties and make use of it like below.
#dummy: {color: red}; // detached ruleset
.outer{
&-inner{
#dummy();
}
}
.test{
#dummy();
}
or
.dummy() {color: blue}; // dummy mixin and would produce no extra selector in output as it has parentheses.
.outer{
&-inner{
.dummy;
}
}
.test{
.dummy;
}

Simplifying Repetitive LESS

I am creating a themeing system for a WordPress network that supports multiple layout themes that can support color schemes for a variety of universities. To do so, I periodically compile a LESS file (using lessphp) with school-specific variables and essentially use it as a library of helper classes in the themes.
Each school has 3 colors defined in LESS as: #primary, #secondary and #tertiary. The method is straightforward and functional but requites a lot of repetition in the code. For example:
//Modifier Classes
.primary-lighter-text {
color: lighten(#primary,20);
}
.sec-lighter-text {
color: lighten(#secondary,20);
}
.tert-lighter-text {
color: lighten(#tertiary,20);
}
//Backgrounds
.primary-bg {
background-color: #primary;
}
.sec-bg {
background-color: #secondary;
}
.tert-bg {
background-color: #tertiary;
}
//Borders
.primary-border{
border-color: #primary;
}
.sec-border {
border-color: #secondary;
}
.tert-border {
border-color: #tertiary;
}
Nothing complicated from a LESS standpoint, but if I want to add a new helper class, I have to create 3. Is there a more succinct way to achieve this?
You can simplify it by making use of array loops. All you have to modify in case of a new addition would be to modify the array variables at the end.
.loop-column(#index) when (#index > 0) { /* Recursive Mixin with Guard condition. Mixin is processed only when the condition is satisfied */
.loop-column(#index - 1); /* Call the mixin again with a decremented counter */
#ctype: extract(#type, #index); /* Extract the type value corresponding to the index from the array */
#color: extract(#colors, #index); /* Extract the color value corresponding to the index from the array */
/* Form and Output the necessary classes and properties */
.#{ctype}-lighter-text { /* Selector interpolation to dynamically form the selector */
color: lighten(#color,20);
}
.#{ctype}-bg {
background-color: #color;
}
.#{ctype}-border{
border-color: #color;
}
}
.loop-column(length(#type));
#type: primary, sec, tert; /* The color types array */
#colors:#fff, #777, #000; /* The color value array for each type */
/* If required the colors can be kept as separate variables also. Refer 2nd demo. */
Demo | Demo 2
Update: (Based on comments from Andrew Cafourek and seven-phases-max)
Since LessPHP is outdated, the following line should be added and the length(#type) should be replaced with the actual count.
.loop-column(0) {};
.loop-column(4);

Referencing parent with multiple levels of nesting in LESS

I have the following LESS:
.container {
.column, .columns {
.one& {
width: 40px;
}
}
}
When I compile I'm getting the following for my CSS:
.one.container .column,
.one.container .columns {
width: 40px;
}
But I expected to get:
.container .one.column,
.container .one.columns {
width: 40px;
}
It appears the parent operator (&) in LESS is actually referencing what I'd expect to be the grandparent. Am I nesting things properly? The docs don't show any examples of nesting more than one level deep. Can I achieve my desired output with nesting?
I'm using lessc 1.3.3 installed via npm.
It's important to think of & as more of a "parentage" combinator, rather than a "parent" combinator. That is, it takes the whole nested string of selectors up to that point (no matter how many levels deep) and acts as the equivalent of a string replacement holder. So with a reduced version of your example...
.container {
.column {
.one& {
width: 40px;
}
}
}
...the selector string at that level is .container .column. This is what is "replaced" in the position of the &, so when you concatenate to the beginning as you do above, it gets attached at the beginning of the whole selector string and you end up with your:
.one.container .column {width 40px;}
But if you concatenate from the end (no space between & and .) then...
.container {
.column {
&.one {
width: 40px;
}
}
}
...becomes:
.container .column.one {width 40px;}
This last one is really the class combination you want, though just not quite in the same order you were hoping for. But order does not matter to the browser, .one.column or .column.one are the same, it just means an element that has both individual classes applied to it like so:
<div class="column one"></div>
<div class="one column"></div>
Both of those are equivalent, and either .one.column or .column.one as a selector is going to select both elements, because both elements have both classes.
If order is absolutely vital to you (you just must have your generated CSS be as you are seeking), then you would need to do a bit of repetition like this:
.container {
.column {
...generic column code here...
}
.one {
&.column {
width: 40px;
...any other code dependent on combination of .one.column here...
}
}
}