I'm trying to accomplish the following CSS output with Sass:
.selector1 + .selector1, .selector2 + .selector2 {
margin-top: -80px;
}
The #extend functionality should do the job:
%sibling-selector {
& + & {
margin-top: -80px;
}
}
.selector1 {
#extend %sibling-selector;
}
.selector2 {
#extend %sibling-selector;
}
But it seems the extend function has problems with the two parent references (&) in the extend only class definition (%sibling-selector).
This is the output:
.selector1 + .selector1, .selector2 + .selector1,
.selector1 + .selector2, .selector2 + .selector2 {
margin-top: -80px;
}
So the #extend function is making sibling selectors for each combination of selectors that use the #extend definition.
While I would expect the #extend stays in the "scope" of the current selector, and so the ampersand is replaced by the current selector.
Is this a bug or a feature? :-)
I know I could use a mixin for this job,
#mixin sibling-selector {
& + & {
margin-top: -80px;
}
}
.selector1 {
#include sibling-selector;
}
.selector2 {
#include sibling-selector;
}
but that would create duplicate CSS definitions:
.selector1 + .selector1 {
margin-top: -80px;
}
.selector2 + .selector2 {
margin-top: -80px;
}
Is there a way to get it right with Sass?
It seems #extend is not the way to get the wanted result: https://github.com/nex3/sass/issues/848#issuecomment-20903684
So to "automate" the creation of the sibling selectors I've used a small #each loop to create a list of selectors.
$siblingSelectors: ();
#each $selector in selector1 selector2 selector3 {
$classSelector: unquote('.prefix-' + $selector);
$siblingSelectors: append($siblingSelectors, unquote($classSelector + ' + ' + $classSelector), comma);
}
#{$siblingSelectors} {
margin-top: 80px;
&.large {
margin-top: -100px;
}
}
Which gives the following result:
.prefix-selector1 + .prefix-selector1, .prefix-selector2 + .prefix-selector2 {
margin-top: -80px;
}
.prefix-selector1 + .prefix-selector1.large, .prefix-selector2 + .prefix-selector2.large {
margin-top: -100px;
}
Related
I want to generate 9 typography classes, each with the following:
font-size: 2rem;
line-height: 1rem;
I'll be using standard typographic multipliers for font sizes and line-height. Instead of hard-coding all of these CSS classes, I was wondering if there was a more elegant way of generating them in a loop using LESS.
I found the following from another thread:
#iterations: 5;
.span-loop (#i) when (#i > 0) {
.span-#{i} {
width: ~"#{i}%";
}
.span-loop(#i - 1);
}
.span-loop (#iterations);
Which generates:
.span-5 {
width: 5%;
}
.span-4 {
width: 4%;
}
.span-3 {
width: 3%;
}
.span-2 {
width: 2%;
}
.span-1 {
width: 1%;
}
This is pretty close, but I'd love for my class names to more "named". How can I use a loop to generate classes for:
.small { }
.caption { }
.body { }
.subheader { }
.title { }
.headline { }
etc...
I'm also not tied to LESS, so if there's a better CSS preprocessor language, then I'm happy to use that instead :)
Thank you!
An example from documentation for further modification;)
for more complicated code, it is better to use scss than less
.for(#list, #code) {
& {
.loop(#i: 1) when (#i =< length(#list)) {
#value: extract(#list, #i);
#code();
.loop(#i + 1);
}
.loop();
}
}
#elements: small, caption, body, subheader, title, headline;
.for(#elements, {
#remfont: #i+1;
#remline: ((#i+1) * 1.5 / 3);
.#{value} {
font-size: ~"#{remfont}rem";
line-height: ~"#{remline}rem";
}
});
I am using the following code to generate a column layout using LESS CSS:
.columnBuilder (#index) when (#index =< #columnCount) {
.container_#{columnCount} .grid_#{index} {
width: unit(((#baseWidth / #columnCount) * #index)-10, px);
}
.columnBuilder(#index + 1);
}
Which gives me an output:
.container_24 .grid_1 {
width: 69px;
}
.container_24 .grid_2 {
width: 148px;
}
.container_24 .grid_3 {
width: 227px;
}
etc...
How would i now create a new less function that would give an output of:
.grid_1,
.grid_2,
....,
.grid_N {
display: inline;
float: left;
margin-left: 5px;
margin-right: 5px;
}
Where N is defined as #columnCount: 24;, though the column count is not set, it can be changed to any number. I am aware i could create a body for each grid_X would like to avoid it to keep clutter down.
Using :extend() in LESS 1.4+
This seems to accomplish it more elegantly. You first define the initial values you will want extended in a hard coded .grid_1 class (at present, LESS will not extend dynamically generated classes), then add an extender mixin in your loop to extend to that class. Like so:
.grid_1 { //this acts as the "launch point" for extending them all
display: inline;
float: left;
margin-left: 5px;
margin-right: 5px;
}
.columnBuilder (#index) when (#index =< #columnCount) {
//we are going to use this class twice, so just calculate it once
#gridClass: ~'.grid_#{index}';
//this is your original code except the variable now used for the grid class
.container_#{columnCount} #{gridClass} {
width: unit(((#baseWidth / #columnCount) * #index)-10, px);
}
//this is your extender feature, which does not do so for the initial .grid_1
//which was set above as our launch point.
#{gridClass} {
.extender() when (#index > 1) {
&:extend(.grid_1 all);
}
.extender() when (#index = 1) {}
.extender();
}
//iterate the loop just as you were doing
.columnBuilder(#index + 1);
}
//call the loop starting at 1
.columnBuilder(1);
Output is your expected:
.grid_1,
.grid_2,
....,
.grid_N {
display: inline;
float: left;
margin-left: 5px;
margin-right: 5px;
}
As it turns out, LESS has no native support for something like this and will always create multiple blocks of CSS each with its own body, so you need to run a little hack with mixins. I used the following:
.columnBuilderX (#index) when (#index = 1) {
#isel: ~".grid_#{index}, ";
.columnBuilderX (#index + 1, #isel);
}
.columnBuilderX (#index, #innerSel) when (#index =< (#columnCount - 1)) {
#isel: #innerSel + ~".grid_#{index}, ";
.columnBuilderX (#index + 1, #isel);
}
.columnBuilderX (#index, #innerSel) when (#index = #columnCount) {
#isel: #innerSel + ~".grid_#{index} ";
#{isel} {
display: inline;
float: left;
margin-left: 5px;
margin-right: 5px;
}
.columnBuilderX (#index + 1, #isel);
}
Which produced for me:
.grid_1, .grid_2, .grid_3, .grid_4, .grid_5, .grid_6, .grid_7, .grid_8, .grid_9, .grid_10, .grid_11, .grid_12, .grid_13, .grid_14, .grid_15, .grid_16, .grid_17, .grid_18, .grid_19, .grid_20, .grid_21, .grid_22, .grid_23, .grid_24 {
display: inline;
float: left;
margin-left: 5px;
margin-right: 5px;
}
The first mixin is the initial mixin that is called that does not already have an inner selector, the second mixin requires a second param, which is the variable we create in the first, which then runs recursively until we hit our last column as defined by the when clause, where we add our last selector without the comma, and then use the selector list we have built in to apply our CSS too.
If anybody can come up with something simpler than this, please create an answer.
What I'm trying to achieve, is simply a shorter version of the #extend function in sass.
I have a heap of classes which I use all over my site for layout.
Example:
.grid1 {width:40px;}
.grid2 {width:80px;}
.grid3 {width:120px;}
.grid4 {width:160px;}
.grid5 {width:200px;}
I know you can use the extend function to remove duplicate css all over the site with:
.class {#extend .grid1}
which would output
.class {width:40px}
What I'm after is something a little more simple.
.class{grid(1)};
Here's what I've tried:
#function grid($n){
#extend unquote(".grid#{$n}");
}
Obviously this doesn't work, any ideas?
#functions in SASS are meant to be used to manipulate values. So the reason why this will not work is due to the fact that you are trying to return selectors and declaration blocks. This is what mixins are for.
One way of doing this would be:
$grids: ((5, 40), (10, 80), (15, 120), (20, 160));
#mixin grid($n, $fluid: false) {
#if($fluid) {
width: nth(nth($grids, $n), 1) + "%";
} #else {
width: nth(nth($grids, $n), 2) + "px";
}
}
.foo {
#include grid(3);
}
.bar {
#include grid(4, true);
}
Which produces:
.foo {
width: "120px"; }
.bar {
width: "20%"; }
SASS has a feature called #extend which allows a selector to inherit the properties of another selector, but without copying the properties (like mixins).
Does LESS have this feature as well?
Yes, Less.js introduced extend in v1.4.0.
:extend()
Rather than implementing the at-rule (#extend) syntax used by SASS and Stylus, LESS implemented the pseudo-class syntax, which gives LESS's implementation the flexibility to be applied either directly to a selector itself, or inside a statement. So both of these will work:
.sidenav:extend(.nav) {...}
or
.sidenav {
&:extend(.nav);
...
}
Additionally, you can use the all directive to extend "nested" classes as well:
.sidenav:extend(.nav all){};
And you can add a comma-separated list of classes you wish to extend:
.global-nav {
&:extend(.navbar, .nav all, .navbar-fixed-top all, .navbar-inverse);
height: 70px;
}
When extending nested selectors you should notice the differences:
nested selectors .selector1 and selector2:
.selector1 {
property1: a;
.selector2 {
property2: b;
}
}
Now .selector3:extend(.selector1 .selector2){}; outputs:
.selector1 {
property1: a;
}
.selector1 .selector2,
.selector3 {
property2: b;
}
, .selector3:extend(.selector1 all){}; outputs:
.selector1,
.selector3 {
property1: a;
}
.selector1 .selector2,
.selector3 .selector2 {
property2: b;
}
,.selector3:extend(.selector2){}; outputs
.selector1 {
property1: a;
}
.selector1 .selector2 {
property2: b;
}
and finally .selector3:extend(.selector2 all){};:
.selector1 {
property1: a;
}
.selector1 .selector2,
.selector1 .selector3 {
property2: b;
}
Easy way to extend a function in Less framework
.sibling-1 {
color: red
}
.sibling-2 {
background-color: #fff;
.sibling-1();
}
Output
.sibling-1 {
color: red
}
.sibling-2 {
background-color: #fff;
color: red
}
Refer https://codepen.io/sprushika/pen/MVZoGB/
Less allows us to do :extend(.class) or :extend(#id)
Is it possible to #extend a SCSS placeholder with nesting, and have that nesting reflected in the resulting class?
Given a nested placeholder:
%my-form-field {
...
&__label {
...
}
&__feedback {
...
}
}
I currently have to do the following:
.one-of-many-targets {
#extend %my-form-field;
&__label {
#extend %my-form-field__label;
}
&__feedback {
#extend %my-form-field__feedback;
}
}
But I'd like to be able to simplify this to:
.one-of-many-targets {
#extend %my-form-field;
}
... and have it resolve to:
.one-of-many-targets { ... }
.one-of-many-targets__label { ... }
.one-of-many-targets__feedback { ... }
Is there a different way to write my placeholder and #extends to make the SCSS cleaner, as in the 2nd example?
You can use a mixin instead:
#mixin my-form-field() {
width: 10px;
&__label {
width: 20px;
}
&__feedback {
width: 30px;
}
}
.one-of-many-targets {
#include my-form-field();
}
will generate:
.one-of-many-targets {
width: 10px;
}
.one-of-many-targets__label {
width: 20px;
}
.one-of-many-targets__feedback {
width: 30px;
}
You could try use selector.append()
See: https://github.com/sass/sass/issues/2808#issuecomment-574413393
Also see more info why parent selector didn't work as you expect in extend-only selectors: https://github.com/sass/sass/issues/2262#issuecomment-291645428