Using interpolation for calling compass sprite mixins - sass

the problem that i have with this mixing is that i can't interpolation for calling different sprite mixins that compass provides.
I want to be able to have this in one place, create the sprites in different scss and them just include this shared mixing and use it.
So far seems that Sass doesn't allow me to do that. Maybe i just have a crazy and bad way of doing things ( i'm no designer really and i learn about sass a few months ago ).
Thanks for your time.
#mixin icon-button($width, $height, $icon-bg-color, $icon, $sprite-name){
.icon-base{
width: $width;
height: $height;
background-color: $icon-bg-color;
.icon{
$icon-height: #{$sprite-name}-sprite-height(#{$icon});
$icon-width: #{$sprite-name}-sprite-width(#{$icon});
#include #{$sprite-name}-sprite(#{$icon});
width: $icon-width;
height: $icon-height;
position: relative;
left: ($width - $icon-width)/2;
top: ($height - $icon-height)/2;
}
}
}

The #{something} is a ruby-way of interpolation. Compass framework uses ruby compiler to make css from scss/sass. Interpolation is just the way to insert some value (of a variable) into a string, like this: puts "I want to say #{smth}" will print "I want to say something" into irb console if you previously defined the smth = "something" variable. You also may notice the difference between ' and ". So, sometimes you would like to use standalone functions in compass like this:
headings(all) {
color: $color;
}
to compile it into css:
h1, h2, h3, h4, h5, h6 {
color: #2a2a2a;
}
Instead, it throws an error. So, to do this you need to interpolate the call of the function:
#{headings(all)} {
color: $color;
}
And it runs. But if you try to make interpolation with variables:
$color: #abc;
$color2: "#abc";
#mixin some($color) {
color: #{$color};
}
will also throw an error because in this case interpolation outputs a string "#abc".
Try to avoid using ruby syntax in compass if it is possible for cleaner style.
Note: I would appreciate if someone can explain with better compilation details because i'm not a rubyist, i'm pythonist.
Update
Check this code:
#mixin setFonting($from, $to, $size) {
$curr: $from;
$to: $to + 3;
#while $curr != $to {
h#{$curr} {
font-size: $size;
}
$curr: $curr + 1;
$size: $size + 2;
}
}
#include setFonting(1, 3, 20px);
It compiles to :
h1 {font-size: 20px;} h2 {font-size: 22px;} h3 {font-size: 24px;} h4 {font-size: 26px;} h5 {font-size: 28px;}
So as you see, #{} interpolation in compass works outside curly brackets used to define statements for selector.

Related

SASS: Incompatible units: 'vw' and 'px'

How to solve the incompatible units problem?
#mixin square-size($size, $min: $size, $max: $size) {
$clamp-size: min(max($size, $min), $max);
width: $clamp-size;
height: $clamp-size;
}
The input is:
#include square-size(10vw, 40px, 70px);
Problem:
Incompatible units: 'vw' and 'px'.
node_modules\#ionic\app-scripts\dist\util\helpers.js:253.replace(/&/g, '&')
But if I use calc(1vw - 1px) it works. (no unit problem).
e.g. max(calc(1vw - 1px)) does not work. Because no number for max.
In my case I want a mixin to square the size of an element. Including clamp.
min-width, max-width, etc. does not work. It will be a rect or an ellipse. Because it does not keep the aspect ratio.
I want a element with dynamic size but with min and max size.
I understand that the dynamic unit vw (Viewport) must be present after sass compilation. Therefore it is not possible to convert the value to a fixed unit.
But is there no way?
I was able to fix the error in React SASS using the calc function.
font-size: calc(max(8vw, 30px));
If you're unable to work around it in any other way, SCSS has a function for ignoring things in quotes:
width: unquote("max(50px, 5rem)");
This will be compiled without the quotes and be valid CSS.
width: max(50px, 5rem);
It will be strange to have this in your scss, but it's a sure-fire way of allowing modern CSS to not interrupt your scss functions
The Problem
CSS didn't use to have its own runtime min() and max() functions, and before they existed SASS had a compile-time version. Since SASS doesn't run live it would be impossible for it to determine whether 10vw or 40px is larger - hence the error.
The Solution
Since SASS is case sensitive while CSS isn't, you can force the parser to use the CSS version of min or max by just calling MIN() or MAX() instead. If you need to resume SASS parsing inside of MAX() ( like to reference a SASS variable ) just surround the SASS code with #{...}.
Here's a fixed version of your code to demonstrate:
#mixin square-size($size, $min: $size, $max: $size) {
/* $clamp-size: min(max($size, $min), $max); */
$clamp-size: MIN(MAX(#{$size}, #{$min}), #{$max});
width: $clamp-size;
height: $clamp-size;
}
Good luck!
You would need to bypass the scss compiler & use a literal instead.
#mixin square-size($size, $min: $size, $max: $size) {
$clamp-size: #{'min(max(#{$size}, #{$min}), #{$max})'};
width: $clamp-size;
height: $clamp-size;
}
Try this
when doing min or max in sass, if you get incompatible units error, you can simply put the value in a quotation, and it will let it pass.
min(10vw, 20px) to"min(10vw, 20px)"
This works for other functions too.
And if you are using variables in calculations, you use #{} on the variables to make it pass
$a: 10px;
$b: 25%;
.mydiv {
width: calc(#{$a} - #{$b});
}
And the #{} converts them to string so that sass wouldn't make arithmetic using them while compiling
You could do it like this using min-width/height and max-width/height to avoid mixing units:
#mixin square-size($size, $min: $size, $max: $size) {
min-width: $min;
max-width: $max;
min-height: $min;
max-height: $max;
width: $size;
height: $size;
}
.class {
#include square-size(10vw, 40px, 70px);
}
For this, instead of SCSS #include
.foo {
#include square-size(10vw, 40px, 70px);
}
use better css function "clamp":
.foo {
width:clamp(10vw, 40px, 70px);
height:clamp(10vw, 40px, 70px);
}

#function v/s #mixin in Sass-lang. Which one to use?

After searching a lot in difference between #function and #mixin I ended up here.
Is there any advantage of using #mixin over #funcion or vice versa. In what context they'll be different, how to use them interchangeably, please come up with examples.
Functions are useful specifically because they return values. Mixins are nothing like functions--they usually just provide valuable blocks of code.
Usually, there are cases where you might have to use both.
For example, if I wanted to create a long-shadow with SASS, I would call a function like so:
#function makelongshadow($color) {
$val: 0px 0px $color;
#for $i from 1 through 200 {
$val: #{$val}, #{$i}px #{$i}px #{$color};
}
#return $val;
}
Which would then be called with this mixin:
#mixin longshadow($color) {
text-shadow: makelongshadow($color);
}
Which provides us with the actual code.
That gets included in the element:
h1 {
#include longshadow(darken($color, 5% ));
}
#function is useful when you want to reuse it on different CSS properties.
Example, You have dynamic values that you want to use on both height and min-height, then using #function is the one you would use:
#function dynamic-height($height, $padding) {
#return $height + $padding;
}
.foo {
min-height: dynamic-height(300px, 30px);
}
.bar {
height: dynamic-height(300px, 30px);
}
But if you want to reuse it with same CSS properties, then you would use a #mixin:
#mixin dynamic-height($height, $padding) {
min-height: $height;
padding: $padding;
}
.foo {
#include #dynamic-height(300px, 30px);
}
think of #mixin as it is just a #function that returns a block of style rules.
also, you can use placeholders (like %selector) and #extend

Giving default arguments to mixins when there is more than one variable

I am trying to make a #mixin where I want to give a default value to the first variable but not to the second variable.
#mixin myCoolFont($color:purple, $size) {
color: $color;
font-size: $size;
}
Now when I call that mixin I do something like this:
p {
#include myCoolFont(white, 63px)
}
When compiling it outputs an error that says $size must come before any other arguments.
Please, tell me, what am I doing wrong?
You just do as error says, it will work, simple change order of arguments so that last argument(s) are those with default values. In your case:
#mixin myCoolFont($size, $color:purple) {
color: $color;
font-size: $size;
}
p {
#include myCoolFont(63px, white)
}

Using #include vs #extend in Sass?

In Sass, I can't quite discern the difference between using #include with a mixin and using #extend with a placeholder class. Don't they amount to the same thing?
Extends do not allow customization, but they produce very efficient CSS.
%button
background-color: lightgrey
&:hover, &:active
background-color: white
a
#extend %button
button
#extend %button
Result:
a, button {
background-color: lightgrey;
}
a:hover, button:hover, a:active, button:active {
background-color: white;
}
With mixins, you get duplicated CSS, but you can use arguments to modify the result for each usage.
=button($main-color: lightgrey, $active-color: white)
background-color: $main-color
border: 1px solid black
border-radius: 0.2em
&:hover, &:active
background-color: $active-color
a
+button
button
+button(pink, red)
Results in:
a {
background-color: lightgrey;
border: 1px solid black;
border-radius: 0.2em;
}
a:hover, a:active {
background-color: white;
}
button {
background-color: pink;
border: 1px solid black;
border-radius: 0.2em;
}
button:hover, button:active {
background-color: red;
}
Please follow this consecutive set of code examples to see how you can make your code cleaner and more maintainable by using extends and mixins effectively: http://thecodingdesigner.com/posts/balancing
Note that SASS unfortunately does not allow using extends inside media queries (and corresponding example from the above link is wrong). In the situation where you need to extend based on media queries, use a mixin:
=active
display: block
background-color: pink
%active
+active
#main-menu
#extend %active // Active by default
#secondary-menu
#media (min-width: 20em)
+active // Active only on wide screens
Result:
#main-menu {
display: block;
background-color: pink;
}
#media (min-width: 20em) {
#secondary-menu {
display: block;
background-color: pink;
}
}
Duplication is inevitable in this case, but you shouldn't care too much about it because web server's gzip compression will take care of it.
PS Note that you can declare placeholder classes within media queries.
Update 2014-12-28: Extends produce more compact CSS than mixins do, but this benefit is diminished when CSS is gzipped. If your server serves gzipped CSS (it really should!), then extends give you almost no benefit. So you can always use mixins! More on this here: http://www.sitepoint.com/sass-extend-nobody-told-you/
A good approach is to use both - create a mixin that will allow you lots of customisation and then make extends for common configurations of that mixin. For example (SCSS Syntax):
#mixin my-button($size: 15, $color: red) {
#include inline-block;
#include border-radius(5px);
font-size: $size + px;
background-color: $color;
}
%button {
#include my-button;
}
%alt-button {
#include my-button(15, green);
}
%big-button {
#include my-button(25);
}
This saves you from calling the my-button mixin over and over. It also means you don't have to remember the settings for common buttons but you still have the ability to make a super unique, one-off button should you choose.
I take this example from a blog post I wrote not long ago. Hope this helps.
In my opinion extends are pure evil and should be avoided. Here is why:
given the scss:
%mystyle {color: blue;}
.mystyle-class {#extend %mystyle}
//basically anything not understood by target browser (such as :last-child in IE8):
::-webkit-input-placeholder {#extend %mystyle}
The following css will be generated:
.mystyle-class, ::-webkit-input-placeholder { //invalid in non-webkit browsers
color: blue;
}
When a browser doesn’t understand a selector, it invalidates the entire line of selectors. This means that your precious mystyle-class is no longer blue (for many browsers).
What does this really mean? If at any time you use an extend where a browser may not understand the selector every other use of the extend will be invalidated.
This behavior also allows for evil nesting:
%mystyle {color: blue;}
#mixin mystyle-mixin {#extend %mystyle; height: 0;}
::-webkit-input-placeholder {#include mystyle-mixin}
//you thought nesting in a mixin would make it safe?
.mystyle-class {#extend %mystyle;}
Result:
::-webkit-input-placeholder, .mystyle-class { //invalid in non-webkit browsers
color: blue;
}
::-webkit-input-placeholder {
height: 0;
}
Tl;dr: #extend is perfectly ok for as long as you never use it with any browser spesific selectors. If you do, it will suddenly tear down the styles wherever you have used it. Try to rely on mixins instead!
Use mixins if it accepts a parameter, where the compiled output will change depending on what you pass into it.
#include opacity(0.1);
Use extend (with placeholder) for any static repeatable blocks of styles.
color: blue;
font-weight: bold;
font-size: 2em;
I totally agree with the previous answer by d4nyll. There is a text about extend option and while I was researching this theme I found a lot of complaints about extend, so just have in mind that and if there is a possibility to use mixin instead of extend, just skip extend.

How to use !important keyword in a mixin?

I can't get Sass to output the !important keyword with a mixin, I've tried:
#include font-size($font-size-sml) !important;
And:
#include font-size($font-size-sml !important);
It always throws an error.
EDIT
I ended up with this which works fine:
#mixin font-size($font-size, $sledge-hammer: "") {
font-size: $font-size #{$sledge-hammer};
font-size: ($font-size / $base-font-size)+rem #{$sledge-hammer};
line-height: ceil($font-size / $base-line-height) * ($base-line-height / $font-size);
}
You can't add !important to whole mixin in SASS (It is possible in LESS I think) like you're trying to do in first example.
Second example works for me (you can pass !important with a parameter), I mean, if you use $font-size-sml directly as a property value it works, so maybe check your syntax.
But if it's really not working for you, you can do something with flag, set a important_flag as a mixin parameter and then use if-else statement in mixin. Something like this:
#mixin large-text($prop, $is_imp: false) {
#if $is_imp == false {
font-size: $prop;
} #else {
font-size: $prop !important;
}
}
Maybe it's not a glamorous way to do it, but it works ;-)
You can use the if(condition, when-true, when-false) function.
So the code will be simplest and compact:
#mixin large-text($size, $isImportant: false) {
font-size: $size if($isImportant, !important, null);
}

Resources