Use a parameter as a tag in a mixin - sass

How can I do that :
#mixin addMargin($el) {
$el {
margin-left: 5px;
}
$el:hover {
margin-left: 10px;
}
}
using sass ?
Thanks for your help

In a mixin, you can not only add properties directly to an element, but you can also add more rules:
#mixin addMargin {
margin-left: 5px;
&:hover {
margin-left:10px;
}
}
Note that you have to prefix the :hover with & so that we get this rule:
#something-with-the-mixin-applied:hover
instead of
#something-with-the-mixin-applied :hover

Use interpolation:
#mixin addMargin($el) {
#{$el} {
margin-left: 5px;
}
#{$el}:hover {
margin-left: 10px;
}
}
#include addMargin(h1);
But Yogu is right, you don't need it here. You may omit selectors, leaving only directives in your mixin, and apply the mixin inside a selector:
#mixin addMargin {
margin-left: 5px;
&:hover {
margin-left:10px;
}
}
h1 {
#include addMargin;
}

Related

SASS if function to check property value

With SASS' if() function, is it possible to check for a property value? E.g. I want a button to have a margin-right: 10px by default. But, if the button is float: right, I want the margin to be margin-left: 10px.
Something like below:
margin: if(float=="right" $margin-right $margin-left);
Obviously, the above doesn't work (otherwise I wouldn't be writing here). But, is this possible?
This mixin can set float and margin properties for any element.
#mixin set-button($float: null) {
#if ($float == right) or ($float == left) {
float: $float;
}
#if ($float == right) {
margin-left: 10px;
} #else {
margin-right: 10px; // default value
}
}
.but-1 {
#include set-button(right);
}
.but-2 {
#include set-button();
}
.but-3 {
#include set-button(left);
}
.but-4 {
#include set-button(lol);
}
Css output:
.but-1 {
float: right;
margin-left: 10px;
}
.but-2 {
margin-right: 10px;
}
.but-3 {
float: left;
margin-right: 10px;
}
.but-4 {
margin-right: 10px;
}
Sassmeister demo.
It's been a while since I did any css stuff so this might be LESS but you could try
.buttonClass {
#if $float == "right" {
$margin-left;
} #else {
$margin-right;
}
}

What is the difference between using the & symbol and using nesting when defining a more detailed class?

What is the difference between the code examples below:
Using nesting:
.btn {
display: inline-block;
padding: 1em 2.5em;
btn-small {
padding: .4em 1.7em;
}
Using the & symbol:
.btn {
display: inline-block;
padding: 1em 2.5em;
&.btn-small {
padding: .4em 1.7em;
}
When should I use one over the other?
The & symbol will concatenate the CSS (without whitespace).
.btn {
btn-small {
padding: .4em 1.7em;
}}
converts to
.btn btn-small {
padding: .4em 1.7em;
}
whereas
.btn {
&.btn-small {
padding: .4em 1.7em;
}}
converts to
.btn.btn-small {
padding: .4em 1.7em;
}
This is particularly useful when using pseudo classes.
scss
a {
&:hover {
color:yellow;
}}
css
a:hover {
color: yellow;
}
You can use & when you want to make more specific selector, for elements that has more classes. For example you can write something like this:
.button {
&:visited{}
&:hover{}
&:active {}
}
This compiles to:
.button:visited{}
.button:hover{}
.button:active{}
And if you left out the & from this, it would compile to this, with space between which isn't the same
.button :hover

SCSS repeat value?

I'm trying to work out on SCSS how I would go about something like this:
I would like to have a margin anywhere between 1px and 1000px and have a class for it.
For example
.MarginTop-x
X being where I can write any value. Obviously I couldn't write out
.MarginTop-1 {margin-top:1px}
.MarginTop-2 {margin-top:2px}
.MarginTop-3 {margin-top:3px}
.MarginTop-4 {margin-top:4px}
etc...
Well you need a #for loop to do that .
SCSS :
$class-slug: ".MarginTop";
$stop-loop: 4;
#for $i from 0 through $stop-loop {
#{$class-slug}-#{$i} {
margin-top: #{$i}px;
}
}
Compiled CSS:
.MarginTop-0 {
margin-top: 0px; }
.MarginTop-1 {
margin-top: 1px; }
.MarginTop-2 {
margin-top: 2px; }
.MarginTop-3 {
margin-top: 3px; }
.MarginTop-4 {
margin-top: 4px; }
Not sure of the utility of this, but...
Sass:
#mixin marginTop($amount) {
.marginTop-#{$amount} {
margin-top: unquote($amount + 'px');
}
}
#include marginTop(1);
#include marginTop(100);
Compiled CSS:
.marginTop-1 {
margin-top: 1px;
}
.marginTop-100 {
margin-top: 100px;
}

Access the parent selector from within a SASS mixin

I have set up a mixin for a button using display:inline-block. I am trying to get to the parent of whatever class that will eventually end up using the mixim, so I can add the font-size: 0px line there to make sure that I don't need to make adjustments to my HTML to avoid unwanted space between each button.
Here's an example... I want the. parent class to receive the font-size: 0px line.
#mixin button() {
display:inline-block;
font-size: 1em;
//other stuff to make a pretty button
&& { font-size: 0px; }
}
.parent{
.child {
#include button();
}
}
As of Sass 3.4 this is now possible.
#mixin parent {
#each $selector in & {
$l: length($selector);
#if ($l == 1) {
#error "Used parent mixin on a top-level selector";
} #else {
$parent: nth($selector,1);
#for $i from 2 to $l {
$parent: append($parent,nth($selector,$i));
}
#at-root #{$parent} {
#content;
}
}
}
}
// Use
.grandparent {
.parent{
.child {
font-size: 1em;
#include parent {
font-size: 0px;
}
}
}
}
// Result
.grandparent .parent .child {
font-size: 1em;
}
.grandparent .parent {
font-size: 0px;
}
// Errors:
.root {
#include parent {
content: "Won't work";
}
}
.grandparent .parent, .root {
#include parent {
content: "Also won't work";
}
}
No, this is not possible. You could do something like this, though:
#mixin button($child: '.child') {
font-size: 0px;
//other stuff to make a pretty button
#{$child} {
display:inline-block;
font-size: 1em;
}
}
.parent{
#include button();
}
Output:
.parent {
font-size: 0px;
}
.parent .child {
display: inline-block;
font-size: 1em;
}
There is a XXX! selector in the draft for the CSS 4 spec, which will act as the way you like. It announces the subject of the CSS style declarations, if the selectors match
So if you have this selector
.a > .b! > .c
It will match e.g. for this
<div class="a">
<div class="b">
<div class="c">
</div>
</div>
</div>
but the style declarations will not take effect on .c, but on .b, because I announced by the exclamation mark, that this element should be the subject of the style
http://dev.w3.org/csswg/selectors4/#subject
You cannot use it right now out of the box. But there is one jQuery plugin, that is a polyfill for that. http://dev.w3.org/csswg/selectors4/
See also this stack: Is there a CSS parent selector?
How to apply?
Well, I don't know exactly in SASS, but in LESS it would be
*! > & {
/* ... */
}
While Karol's answer is near perfect, it doesn't take into account pseudo-elements or pseudo-selectors. Furthermore, code is duplicated if using more than one complex selector. I came up with a simplified version:
#mixin parent {
$parents: ();
$parent: '';
#each $selector in & {
$length: length($selector);
$index: 0;
$last-selector: nth($selector, $length);
#if ($length == 1) {
#error "Used parent mixin on a top-level selector";
} #else {
$index: str-index($last-selector, '::');
#if ($index) {
$last-selector: str-slice($last-selector, 1, $index - 1);
} #else {
$last-selector: null;
}
// Inspect allows us to combine two selectors in one block.
$parent: inspect(set-nth($selector, $length, #{$last-selector}));
$parents: join($parents, $parent, comma);
}
}
#at-root #{$parents} {
#content;
}
}
There's a first loop to iterate over the selector list (selectors with commas at the end). Because complex selectors are also treated as a list, we just need to remove the last element of the list. There's no loop to iterate over the compound or simple selectors since we only need to discard the last one.
There's no function in Sass to remove an element of a list, but we can set the value of an element with set-nth. By making the last element as an empty string and unquoting it, we can remove the last element from the printed representation (string) of the list. Since selectors can be strings, we simply use the new string as a selector.
When using the following:
.grandmother,
.grandfather {
.parent {
.child {
font-size: 10em;
#include parent {
font-size: 5em;
}
&::after {
font-size: 1px;
#include parent {
font-weight: bold;
}
}
}
}
}
We get the following:
.grandmother .parent .child,
.grandfather .parent .child {
font-size: 10em;
}
.grandmother .parent,
.grandfather .parent {
font-size: 5em;
}
.grandmother .parent .child::after,
.grandfather .parent .child::after {
font-size: 1px;
}
.grandmother .parent .child,
.grandfather .parent .child {
font-weight: bold;
}
Note: pseudo-elements and pseudo-selectors are not children of an element but are attached to it and have therefore no parents in themselves. I assumed parents would mean the parent in the sense of Sass nesting.

Using SASS to extend selectors with elements

I'm working on an SCSS stylesheet, and I have a rule that looks something like this:
.footer-link-row {
color: red;
ul& {
padding: 0;
}
}
I want the ul& line compile to the selector ul.footer-link-row. However, this selector returns a compiler error, and using a &ul compiles to .footer-link-row ul. What's the correct way to select something like this?
--Added--
To clarify, the eventual CSS I want out of this is:
.footer-link-row {
color: red;
}
ul.footer-link-row {
padding: 0;
}
You want something like the following:
ul {
padding: 0;
.footer-link-row {
color: red;
}
}
The ampersand is used to require that both selectors match
a { text-decoration: none;
&:hover { border-width: 1px }
}
// compiles to
a {
text-decoration: none;
}
a:hover {
border-width: 1px;
}
If you want the ul.footer-link-row try
ul {
&.footer-link-row {
padding: 0;
}
.footer-link-row {
color: red;
}
}
Your clarification indicates that you need two scopes.
ul {
&.footer-link-row {
padding: 0;
}
}
.footer-link-row {
color: red;
}

Resources