Christoph Christoph - 2 months ago 12
Less Question

LESS CSS: abusing the & Operator when nesting?

Less uses the

&
Operator to enhance the possibilities for nesting.

.header { color: black;
.navigation { font-size: 12px;
&.class { text-decoration: none }
}
}


which causes a substitution of the
&
with the parent selector and results in a concatentation of the actual selector right to the parent selector:
.header .navigation.class
instead of the normal appending, which would result in
.class
being a decendant:
.header .navigation .class
.

Now what also is possible is the following (see also here):

.header { color: black;
.navigation { font-size: 12px;
#some-id & .foo { text-decoration: none }
}
}


which would result in the following:
#some-id .header .navigation .foo
try here . The substition takes place and i have prepended a selector (
#some-id
) to my parent selector.

Besides the fact that I would never code this way, since this probably messes up your stylesheet in no time, my question:

As this functionality is not documented, is it a feature or more likely a bug?

Which are possible side-effects?

Answer

I also have been pondering this use further since we encountered it in that question you referenced. While I cannot answer definitively that this is a "bug" use for & (BoltClock seems to make a good argument that it is not a bug), I want to argue for the value of it (which argues it is not a bug from a logical standpoint).

However, before the logical argument, I did find another short, simple quote (in the "nested rules" section) that seems to indicate it is at least not unintended: "& represents the current selector parent." That's it. As BoltClock argues, whether prepending or appending seems irrelevant. All that was intended was that it be a placeholder for that "selector parent" that is current up to that point. The fact that it is always mentioned in conjunction with the "nesting" use of the language implies it is designed to designate the full selector string of the nest up to the point of the nest that it resides within. How that string is used (to pre- or append) would seem up to the coder.

Now, you mention (and previously mentioned) that you "would never code this way," and yet I find myself seeing what appears to be a very valuable use for this. Consider the following argument.

The HTML Setup for the Illustration

Assume, for the sake of illustration, there is a dynamic setting of three possible classes ('light', 'medium', 'dark' themes) on the body element that change the "look" of the site. We have two columns, and some various types of links we want to style (textLink, picLink, textWithIconLink) differently in each column for each theme.

<body class="light">
  <div class="leftCol">
     <a class="textLink"></a>
     <a class="picLink"></a>
     <a class="textWithIconLink"></a>
  </div>
  <div class="rightCol">
     <a class="textLink"></a>
     <a class="picLink"></a>
     <a class="textWithIconLink"></a>
  </div>
</body>

Now the questions to ask are, just looking at the links, of the two following methods, which...

  1. Is less code in LESS
  2. Best organzies the code in LESS
  3. Outputs less code in CSS
  4. Best organizes the outputted CSS

"Best" may be somewhat subjective. I'll let you weigh that evidence yourself from below.

Option #1 Typical Nesting

LESS (approx. 99 lines of code)

/*Light Color Theme */
    .light {
      .leftCol {
        .textLink {
          color: fooL1;
          &:hover { color: barL1;}
        } 
        .picLink {
          background-image: url(/fooL1.jpg);
          &:hover { background-image: url(/barL1.jpg);}
        }
        .textWithIconLink {
          color: fooL2;
          background-image: url(/fooL2.jpg);
          &:hover { color: barL2; background-image: url(/barL2.jpg);}
        }   
      }
      .rightCol {
        .textLink {
          color: fooL3;
          &:hover { color: barL3;}
        } 
        .picLink {
          background-image: url(/fooL3.jpg);
          &:hover { background-image: url(/barL3.jpg);}
        }
        .textWithIconLink {
          color: fooL4;
          background-image: url(/fooL4.jpg);
          &:hover { color: barL4; background-image: url(/barL4.jpg);}
        }   
      }
    }
/*Medium Color Theme */
    .medium {
      .leftCol {
        .textLink {
          color: fooM1;
          &:hover { color: barM1;}
        } 
        .picLink {
          background-image: url(/fooM1.jpg);
          &:hover { background-image: url(/barM1.jpg);}
        }
        .textWithIconLink {
          color: fooM2;
          background-image: url(/fooM2.jpg);
          &:hover { color: barM2; background-image: url(/barM2.jpg);}
        }   
      }
      .rightCol {
        .textLink {
          color: fooM3;
          &:hover { color: barM3;}
        } 
        .picLink {
          background-image: url(/fooM3.jpg);
          &:hover { background-image: url(/barM3.jpg);}
        }
        .textWithIconLink {
          color: fooM4;
          background-image: url(/fooM4.jpg);
          &:hover { color: barM4; background-image: url(/barM4.jpg);}
        }   
      }
    }
/*Dark Color Theme */
    .dark {
      .leftCol {
        .textLink {
          color: fooD1;
          &:hover { color: barD1;}
        } 
        .picLink {
          background-image: url(/fooD1.jpg);
          &:hover { background-image: url(/barD1.jpg);}
        }
        .textWithIconLink {
          color: fooD2;
          background-image: url(/fooD2.jpg);
          &:hover { color: barD2; background-image: url(/barD2.jpg);}
        }   
      }
      .rightCol {
        .textLink {
          color: fooD3;
          &:hover { color: barD3;}
        } 
        .picLink {
          background-image: url(/fooD3.jpg);
          &:hover { background-image: url(/barD3.jpg);}
        }
        .textWithIconLink {
          color: fooD4;
          background-image: url(/fooD4.jpg);
          &:hover { color: barD4; background-image: url(/barD4.jpg);}
        }   
      }
    }

CSS Output (approx. 87 lines of output, organized by theme of course)

 /*Light Color Theme */
.light .leftCol .textLink { color:fooL1; }
.light .leftCol .textLink:hover { color:barL1; }
.light .leftCol .picLink { background-image:url(/fooL1.jpg); }
.light .leftCol .picLink:hover { background-image:url(/barL1.jpg); }
.light .leftCol .textWithIconLink {
  color:fooL2;
  background-image:url(/fooL2.jpg);
}
.light .leftCol .textWithIconLink:hover {
  color:barL2;
  background-image:url(/barL2.jpg);
}
.light .rightCol .textLink { color:fooL3; }
.light .rightCol .textLink:hover { color:barL3; }
.light .rightCol .picLink { background-image:url(/fooL3.jpg); }
.light .rightCol .picLink:hover { background-image:url(/barL3.jpg); }
.light .rightCol .textWithIconLink {
  color:fooL4;
  background-image:url(/fooL4.jpg);
}
.light .rightCol .textWithIconLink:hover {
  color:barL4;
  background-image:url(/barL4.jpg);
}
/*Medium Color Theme */
.medium .leftCol .textLink { color:fooM1; }
.medium .leftCol .textLink:hover { color:barM1; }
.medium .leftCol .picLink { background-image:url(/fooM1.jpg); }
.medium .leftCol .picLink:hover { background-image:url(/barM1.jpg); }
.medium .leftCol .textWithIconLink {
  color:fooM2;
  background-image:url(/fooM2.jpg);
}
.medium .leftCol .textWithIconLink:hover {
  color:barM2;
  background-image:url(/barM2.jpg);
}
.medium .rightCol .textLink { color:fooM3; }
.medium .rightCol .textLink:hover { color:barM3; }
.medium .rightCol .picLink { background-image:url(/fooM3.jpg); }
.medium .rightCol .picLink:hover { background-image:url(/barM3.jpg); }
.medium .rightCol .textWithIconLink {
  color:fooM4;
  background-image:url(/fooM4.jpg);
}
.medium .rightCol .textWithIconLink:hover {
  color:barM4;
  background-image:url(/barM4.jpg);
}
/*Dark Color Theme */
.dark .leftCol .textLink { color:fooD1; }
.dark .leftCol .textLink:hover { color:barD1; }
.dark .leftCol .picLink { background-image:url(/fooD1.jpg); }
.dark .leftCol .picLink:hover { background-image:url(/barD1.jpg); }
.dark .leftCol .textWithIconLink {
  color:fooD2;
  background-image:url(/fooD2.jpg);
}
.dark .leftCol .textWithIconLink:hover {
  color:barD2;
  background-image:url(/barD2.jpg);
}
.dark .rightCol .textLink { color:fooD3; }
.dark .rightCol .textLink:hover { color:barD3; }
.dark .rightCol .picLink { background-image:url(/fooD3.jpg); }
.dark .rightCol .picLink:hover { background-image:url(/barD3.jpg); }
.dark .rightCol .textWithIconLink {
  color:fooD4;
  background-image:url(/fooD4.jpg);
}
.dark .rightCol .textWithIconLink:hover {
  color:barD4;
  background-image:url(/barD4.jpg);
}

Option #2 End Target Grouping

I've named it "End Target Grouping," because that's really how I see using the & in this other way of adding parent selectors. One is now coding based off the final end target element that is actually being styled.

LESS (approx. 88 lines of code)

/*Links */
/*Text  Links*/
.textLink {
  .light .leftCol &  {
      color: fooL1;
      &:hover { color: barL1;}
    }      
  .light .rightCol &  {
      color: fooL3;
      &:hover { color: barL3;}
    } 
  .medium .leftCol &  {
      color: fooM1;
      &:hover { color: barM1;}
    } 
  .medium .rightCol &  {
      color: fooM3;
      &:hover { color: barM3;}
    } 
  .dark .leftCol &  {
      color: fooD1;
      &:hover { color: barD1;}
    } 
  .dark .rightCol &  {
      color: fooD3;
      &:hover { color: barD3;}
    } 
}
/*Picture Links */
.picLink {
  .light .leftCol &  {
      background-image: url(/fooL1.jpg);
      &:hover { background-image: url(/barL1.jpg);}
    } 
  .light .rightCol &  {
      background-image: url(/fooL3.jpg);
      &:hover { background-image: url(/barL3.jpg);}
    } 
  .medium .leftCol &  {
      background-image: url(/fooM1.jpg);
      &:hover { background-image: url(/barM1.jpg);}
    } 
  .medium .rightCol &  {
      background-image: url(/fooM3.jpg);
      &:hover { background-image: url(/barM3.jpg);}
    } 
  .dark .leftCol &  {
      background-image: url(/fooD1.jpg);
      &:hover { background-image: url(/barD1.jpg);}
    } 
  .dark .rightCol &  {
      background-image: url(/fooD3.jpg);
      &:hover { background-image: url(/barD3.jpg);}
    } 
}
/*Text with Icon Links */
.textWithIconLink {
  .light .leftCol &  {
      color: fooL2;
      background-image: url(/fooL1.jpg);
      &:hover { color: barL2; background-image: url(/barL1.jpg);}
    } 
  .light .rightCol &  {
      color: fooL4;
      background-image: url(/fooL3.jpg);
      &:hover { color: barL4;  background-image: url(/barL3.jpg);}
    } 
  .medium .leftCol &  {
      color: fooM2;
      background-image: url(/fooM1.jpg);
      &:hover { color: barM2; background-image: url(/barM1.jpg);}
    } 
  .medium .rightCol &  {
     color: fooM4;
      background-image: url(/fooM3.jpg);
      &:hover { color: barM4; background-image: url(/barM3.jpg);}
    } 
  .dark .leftCol &  {
     color: fooD2;
      background-image: url(/fooD1.jpg);
      &:hover { color: barD2; background-image: url(/barD1.jpg);}
    } 
  .dark .rightCol &  {
      color: fooD4;
      background-image: url(/fooD3.jpg);
      &:hover { color: barD4; background-image: url(/barD3.jpg);}
    } 
}

CSS (approx. 88 lines of output [due to one extra comment], organized by end target element; but notice, there is still a suborganization by theme because of the class structure)

/*Links*/
/*Text  Links*/
.light .leftCol .textLink { color:fooL1; }
.light .leftCol .textLink:hover { color:barL1; }
.light .rightCol .textLink { color:fooL3; }
.light .rightCol .textLink:hover { color:barL3; }
.medium .leftCol .textLink { color:fooM1; }
.medium .leftCol .textLink:hover { color:barM1; }
.medium .rightCol .textLink { color:fooM3; }
.medium .rightCol .textLink:hover { color:barM3; }
.dark .leftCol .textLink { color:fooD1; }
.dark .leftCol .textLink:hover { color:barD1; }
.dark .rightCol .textLink { color:fooD3; }
.dark .rightCol .textLink:hover { color:barD3; }
/*Picture Links */
.light .leftCol .picLink { background-image:url(/fooL1.jpg); }
.light .leftCol .picLink:hover { background-image:url(/barL1.jpg); }
.light .rightCol .picLink { background-image:url(/fooL3.jpg); }
.light .rightCol .picLink:hover { background-image:url(/barL3.jpg); }
.medium .leftCol .picLink { background-image:url(/fooM1.jpg); }
.medium .leftCol .picLink:hover { background-image:url(/barM1.jpg); }
.medium .rightCol .picLink { background-image:url(/fooM3.jpg); }
.medium .rightCol .picLink:hover { background-image:url(/barM3.jpg); }
.dark .leftCol .picLink { background-image:url(/fooD1.jpg); }
.dark .leftCol .picLink:hover { background-image:url(/barD1.jpg); }
.dark .rightCol .picLink { background-image:url(/fooD3.jpg); }
.dark .rightCol .picLink:hover { background-image:url(/barD3.jpg); }
/*Text with Icon Links */
.light .leftCol .textWithIconLink {
  color:fooL2;
  background-image:url(/fooL1.jpg);
}
.light .leftCol .textWithIconLink:hover {
  color:barL2;
  background-image:url(/barL1.jpg);
}
.light .rightCol .textWithIconLink {
  color:fooL4;
  background-image:url(/fooL3.jpg);
}
.light .rightCol .textWithIconLink:hover {
  color:barL4;
  background-image:url(/barL3.jpg);
}
.medium .leftCol .textWithIconLink {
  color:fooM2;
  background-image:url(/fooM1.jpg);
}
.medium .leftCol .textWithIconLink:hover {
  color:barM2;
  background-image:url(/barM1.jpg);
}
.medium .rightCol .textWithIconLink {
  color:fooM4;
  background-image:url(/fooM3.jpg);
}
.medium .rightCol .textWithIconLink:hover {
  color:barM4;
  background-image:url(/barM3.jpg);
}
.dark .leftCol .textWithIconLink {
  color:fooD2;
  background-image:url(/fooD1.jpg);
}
.dark .leftCol .textWithIconLink:hover {
  color:barD2;
  background-image:url(/barD1.jpg);
}
.dark .rightCol .textWithIconLink {
  color:fooD4;
  background-image:url(/fooD3.jpg);
}
.dark .rightCol .textWithIconLink:hover {
  color:barD4;
  background-image:url(/barD3.jpg);
}

Concluding Thoughts

A few other considerations:

First, most of your theme colors (and possibly other themeing aspects) will be set up with variables, which can be grouped at the top of the LESS code by theme even using Option #2 above--so having the theme structure for the output CSS itself scattered in the code is not necessarily bad.

Second, any "standard" code for a type of element is defined above any theme code. My examples did not show this, but say all .textLink elements had text-decoration: none; set. That would occur once under Option #2 without any further selector code, and would appear above all the theme changes below. For Option #1, I need to set up a new, unnested .textLink selector (at least one other line of code) to apply it to all themed links, and that "base" code for the class will, again, be somewhere unrelated to where the rest of the code for the theme link info is.

Third, as a developer, if I am having issues with my picLinks (a specific type of element on my page), Option #2 makes it far easier to examine my code for the element I am having issues with, as all my code for all the themes is right in one spot.

Obviously, how one wants the final LESS and CSS organized is going to be a major factor in how one sees the value of this. My point here is to merely demonstrate that there is a very useful, logical reason to use the & in this way of adding parent level selectors to the & reference.

Comments