I am trying to make sure a mixin that may be called several times in different files, only outputs css once.
First try (less)
I first was thinking something like this:
mixin
#_mod: "false";
.mod(#_mod) when (#_mod = "false") {
#_mod: "true";
.mod {
border-radius: 6px;
border: 1px solid gray;
}
}
call 1
.mod(#_mod);
call 2
.mod(#_mod);
output
.mod {
border-radius: 6px;
border: 1px solid gray;
}
.mod {
border-radius: 6px;
border: 1px solid gray;
}
That doesn't work because the variables are namespaced and scoped to mixins.
Read more
CodePen
Second try (less)
I then though this:
mixin
#_mod: "false";
.mody() {
.mody {
border-radius: 6px;
border: 1px solid gray;
}
}
call 1
& when (#_mod = "false") {
.mody();
}
#_mod: "true";
call 2
& when (#_mod = "false") {
.mody();
}
#_mod: "true";
output
-nothing-
This doesn't work because of Less's lazy loading "feature" for all variables...
CodePen
Does anyone know a way to make sure a mixin, called multiple times, runs only once?
Solution: use default() and make the initial mixin to expose yet another empty mixin (which will suppress the default one).
// (using same name for a mixin and a ruleset is a bad idea)
._mod() when (default()) {
._mod() {}
.mod {
border-radius: 6px;
border: 1px solid gray;
}
}
._mod();
._mod();
._mod();
._mod();
Related
The deep selector in vue3 has been changed into :deep(xxx), but it doesn't work when I use it in .less imported externally to .tsx.
BTW, Although Vue3 is no longer use /deep/ or >>>, I still tried and didn't work too
.register_form {
&__base_info {
display: flex;
flex-direction: row;
justify-content: space-between;
background-color: palegoldenrod;
&__input_container {
padding: 1rem;
border: 1px solid green;
border-radius: 5px;
&__label {
font-weight: bolder;
}
:deep(input) {
border: none;
background-color: yellow;
}
}
}
}
I like to use :extend() in Less like I can do it in Sass.
Example in SCSS: http://codepen.io/Grawl/pen/qEeQPG
Example in Less: http://codepen.io/Grawl/pen/qEeQpz (not worked)
Expected output:
.datalist-item {
display: block;
}
.datalist-item-term {
font-weight: normal;
}
.datalist-item-description {
font-weight: bold;
}
.datalist-float .datalist-item {
display: inline-block;
}
.datalist-float .datalist-item:not(:last-of-type) {
margin-right: 1em;
padding-right: 1em;
border-right: 1px solid;
}
The purpose is to not self-repeat, so if I rename one class in Sass I have not to rename others.
I know I can put root class in a variable and use it twice with it http://codepen.io/Grawl/pen/qEeQpz but it looks ugly :(
Your Sass (SCSS) example uses #extend-Only Selectors which is some special form of extending which does not exists in Less.
Firstly a "normal" extend:
SCSS:
.class {
p: 1;
}
.class2 {
#extend .class;
}
and Less:
.class {
p: 1;
}
.class2 {
&:extend(.class);
}
both compile into:
.class,
.class2 {
p: 1;
}
In Less .class2 { &:extend(.class); } can also be written as .class2:extend(.class1){}
Now consider the following SCSS code which uses #extend-Only Selectors:
%class {
p: 1;
}
.class2 {
#extend %class;
}
The preceding code compile into CSS code as follows:
.class2 {
p: 1; }
Sass documentation tells you:
#extend-Only Selectors
Sometimes you’ll write styles for a class that you only ever want to
#extend, and never want to use directly in your HTML. This is
especially true when writing a Sass library, where you may provide
styles for users to #extend if they need and ignore if they don’t.
If you use normal classes for this, you end up creating a lot of extra
CSS when the stylesheets are generated, and run the risk of colliding
with other classes that are being used in the HTML. That’s why Sass
supports “placeholder selectors” (for example, %foo).
Placeholder selectors look like class and id selectors, except the #
or . is replaced by %. They can be used anywhere a class or id could,
and on their own they prevent rulesets from being rendered to CSS.
In Less you will have two options to have code that does not generate output:
1) use a mixin, mixins do not generate output:
.class() {
p: 1;
}
.class2 {
.class();
}
outputs:
.class2 {
p: 1;
}
2) put your classes which should not output in a different file and import this file with the reference kewyword:
file1.less:
.class {
p: 1;
}
file2.less:
#import (reference) "file1";
.class2 {
&:extend(.class);
}
lessc file2.less will output now:
.class2 {
p: 1;
}
But i agree with #seven-phases-max in the comments in the first place. In your example there is no need to use extend. #seven-phases-max shows you some examples to solve this use case. Alternatively you can consider; changing selector order with parent reference, which should work in both Less and SASS:
.datalist-item {
display: block;
&-term {
font-weight: normal;
}
&-description {
font-weight: bold;
}
.datalist-float & {
display: inline-block;
&:not(:last-of-type) {
margin-right: 1em;
padding-right: 1em;
border-right: 1px solid;
}
}
}
Compile into:
.datalist-item {
display: block;
}
.datalist-item-term {
font-weight: normal;
}
.datalist-item-description {
font-weight: bold;
}
.datalist-float .datalist-item {
display: inline-block;
}
.datalist-float .datalist-item:not(:last-of-type) {
margin-right: 1em;
padding-right: 1em;
border-right: 1px solid;
}
Finally notice that you are using nesting of properties such as:
border: {
right: 1px solid;
};
which should compile into:
border-right {
1px solid;
}
Less does NOT support nesting of properties.
I need to override some global variables used within a mixin. However I'm reluctant to just change the variables in global space (wouldn't be overriding then)
Consider this given mixin with the required variable defined.
// Mixin and adjust the regular image class
.thumbnail {
display: block;
padding: #thumbnail-padding;
margin-bottom: #line-height-computed;
line-height: #line-height-base;
background-color: #thumbnail-bg;
border: 1px solid #thumbnail-border;
border-radius: #thumbnail-border-radius;
.transition(all .2s ease-in-out);
> img,
a > img {
.img-responsive();
margin-left: auto;
margin-right: auto;
}
// Add a hover state for linked versions only
a&:hover,
a&:focus,
a&.active {
border-color: #link-color;
}
// Image captions
.caption {
padding: #thumbnail-caption-padding;
color: #thumbnail-caption-color;
}
}
Yep, from Bootstrap... Now I want that mixin to work with different variables, without changing the global scoped ones. Can this be done?
article.publications {
.news-thumb {
#thumbnail-padding: 10px;
#line-height-computed: 20px;
#line-height-base: 30px;
#thumbnail-bg : black;
.thumbnail();
}
}
When using LESS, i found usefull to mix classes, in order to create a new class based on other class properties, but sometimes i need to override them.
like:
.btn {
border-radius: 10px;
background-color: blue;
font-size:10px;
}
.btn_warning {
.btn;
background-color: yellow;
font-size: 12px;
}
The output has duplicated properties:
.btn {
border-radius: 10px;
background-color: blue;
font-size:10px;
}
.btn_warning {
border-radius: 10px;
background-color: blue;
font-size:10px;
background-color: yellow;
font-size: 12px;
}
I know there are multiple aproaches for this, like multiple classes on dom, or even #extend to build multiple selectors, but navigator still overriding at runtime the properties.
Is there any reason to duplicate same properties when mixin? Seems a simple way for making "independent" groups of properties, but not nice if has duplicated values.
LESS does not account for removal of duplicate properties within a block, at least in part because of this reason stated here (quote slightly modified for grammar fix):
The trouble is that people frequently use multiple properties in order
to provide a fallback for older browsers. Removing the properties is
not something that it would be good to do generically.
It is left up to the programmer to not program it for duplication. You can set up a basic mixin like what Danny Kijkov noted in his answer, or...
Solution #1 (Complex, but Powerful to Fully Define)
You can get elaborate in building a master button maker mixin. Something like this:
LESS (Mixin)
.makeBtn(#ext: null; #rad: 10px; #color: blue; #size: 10px;) {
.set-extension() when (#ext = null) {
#class-extension: ~'';
}
.set-extension() when not (#ext = null) {
#class-extension: ~'_#{ext}';
}
.set-extension();
.btn#{class-extension} {
border-radius: #rad;
background-color: #color;
font-size: #size;
//define various addtions based on extensions here
.specialExtensionProps() when (#ext = danger) {
border: 3px solid red;
}
.specialExtensionProps() when (#ext = someExtName) {
my-special-prop: yep;
}
.specialExtensionProps();
}
}
LESS (Use the Mixin Various Ways)
.makeBtn(); //makes base button
.makeBtn(warning; #color: yellow; #size: 12px); //makes modified button
.makeBtn(danger; #color: red;); //makes modified button
.makeBtn(someExtName, 15px); //makes modified button
CSS Output
.btn {
border-radius: 10px;
background-color: #0000ff;
font-size: 10px;
}
.btn_warning {
border-radius: 10px;
background-color: #ffff00;
font-size: 12px;
}
.btn_danger {
border-radius: 10px;
background-color: #ff0000;
font-size: 10px;
border: 3px solid red;
}
.btn_someExtName {
border-radius: 15px;
background-color: #0000ff;
font-size: 10px;
my-special-prop: yep;
}
In case you did not know, note the above demonstrated LESS functionality of setting only some variables from the set of mixin variables. So for the first two specialized .makeBtn() calls, I only set a few variables, out of order from the mixin, by explicitly calling the variable name to set (e.g. #color: yellow). This allows me to "skip" over setting the #size. In the last example, I was only setting the first two values, so I did not need to put any variable names.
I don't know if the above helps you get what you want, but it does offer a different way of being able to reduce code size.
Solution #2
You mentioned :extend(), which could be well used here to avoid duplication:
LESS
.btn {
border-radius: 10px;
background-color: blue;
font-size:10px;
}
.btn_warning {
&:extend(.btn);
background-color: yellow;
font-size: 12px;
}
CSS Output
.btn,
.btn_warning {
border-radius: 10px;
background-color: blue;
font-size: 10px;
}
.btn_warning {
background-color: yellow;
font-size: 12px;
}
Solution #3
In your case, if all the buttons will be of either class .btn or a .btn_SOMETHING form, and you are not using .btn_ for anything else but buttons, then you might be able to just use the CSS cascade to apply styles and prevent duplication of CSS code like so (no special LESS required):
LESS and CSS Output
.btn, [class *= btn_] {
border-radius: 10px;
background-color: blue;
font-size:10px;
}
.btn_warning {
background-color: yellow;
font-size: 12px;
}
Any html with the class btn_warning will first get the base button styles from the attribute selector [class *= btn_] while the actual btn_warning class will override the things set to be overridden.
Solution #4
If you split the class names in the html (so class="btn warning" rather than class="btn_warning"), then this works to avoid duplication:
LESS and CSS Output
.btn {
border-radius: 10px;
background-color: blue;
font-size:10px;
}
.btn.warning {
background-color: yellow;
font-size: 12px;
}
What about this solution?
.btn(#size: 10px, #color:blue) {
border-radius: 10px;
background-color: #color;
font-size:#size;
}
.btn_warning {
.btn(12px, yellow);
}
I have the following button mixin:
.Button(#type) {
color: #White;
&:hover {color: #White;} // :hover
} // Button
.Button(#type) when (#type = 'Delete') {
background-color: lighten(#Red, 20%);
border: 1px solid lighten(#Red, 20%);
&:hover {
background-color: lighten(#Red, 12%);
border: 1px solid lighten(#Red, 12%);
} // :hover
} // Button
.Button(#type) when (#type = 'Search') {
background-color: lighten(#Blue, 20%);
border: 1px solid lighten(#Blue, 20%);
&:hover {
background-color: lighten(#Blue, 12%);
border: 1px solid lighten(#Blue, 12%);
} // :hover
} // Button
This is working fine and, as you can see, what changes in each button is the color.
If it possible to have only one Mixin and according to the type define a color variable.
This way I wouldn't need to use so many Button mixin versions ...
There is no other way to do that. Guarded mixins in LESS is fixed to you use that format instead of if/else statements. But in your case, I suggest to do this :
//create a mixin for global rules.
.rules(#color){
background-color: lighten(#color, 20%);
border: 1px solid lighten(#color, 20%);
&:hover {
background-color: lighten(#color, 12%);
border: 1px solid lighten(#color, 12%);
}
}
And you just only to call .rules mixin to every your css rules.
.Button(#type) when (#type = 'Delete') {
.rules(#Red);
}
.Button(#type) when (#type = 'Search') {
.rules(#Blue);
}
This is simpler and no need a lot of space to write the same code. Hope this helps.
Yes, It Can Be Done
It can be folded into a single mixin that uses the #type to switch color values with a creative use of variable variables.
LESS
#White: #fff;
#Red: #f00;
#Blue: #00f;
.Button(#type) {
//define the variables with the name
//of the button you want to pass: Delete, Search, etc.
//associated to the color variable you desire
#Delete: #Red;
#Search: #Blue;
//set up a generic variable name to use, and
//then call the color value through a variable variable call (##)
#ContrastColor: ##type;
color: #White;
background-color: lighten(#ContrastColor, 20%);
border: 1px solid lighten(#ContrastColor, 20%);
&:hover {
color: #White;
background-color: lighten(#ContrastColor, 12%);
border: 1px solid lighten(#ContrastColor, 12%);
} // :hover
} // Button
.deleteClass {
.Button(Delete);
}
.searchClass {
.Button(Search);
}
CSS Output
.deleteClass {
color: #ffffff;
background-color: #ff6666;
border: 1px solid #ff6666;
}
.deleteClass:hover {
color: #ffffff;
background-color: #ff3d3d;
border: 1px solid #ff3d3d;
}
.searchClass {
color: #ffffff;
background-color: #6666ff;
border: 1px solid #6666ff;
}
.searchClass:hover {
color: #ffffff;
background-color: #3d3dff;
border: 1px solid #3d3dff;
}