Sass mixin with nested ampersand prefix - css

What's the best way to accomplish the ability to use class prefixes using a mixin with placeholder selectors.
To elaborate, say I have a box class that has 3 sizes but I would like to have the option of having it a different color.
My base classes would be:
.box
.box-md
.box-sm
If I wanted any of the base class boxes to be green, I would like to be able to specify as such:
.box-green
.box-md-green
.box-sm-green
How would I be able to do so in as DRY a method as possible?
Similar to this answer but using mixins AND placeholder extends: SCSS, how to #extend Nested ampersand "prefix"?
Here's what I put together so far (which doesn't work)
HTML:
<div class="box"></div>
<div class="box-green"></div>
<div class="box-sm"></div>
<div class="box-sm-green"></div>
CSS (SCSS):
// Main style placholder as mixin
#mixin box {
height: 300px;
width: 300px;
margin: 20px;
display: inline-block;
background-color: blue;
&-green {
background-color: green;
}
}
// Placeholders
%box {
#include box;
}
%small-box {
#include box;
width: 100px;
height: 100px;
}
// Class Definition
.box { #extend %box; }
.box-sm { #extend %small-box; }
Pen: https://codepen.io/Aricha_MW/pen/xxKZWbV

This isn't a complete answer but does pose as a solution to the problem:
Our champions are the #at-root directive and interpolation here. The solution requires both the use of mixins and placeholder selectors and is a little messy.
If we set up our placeholder selector styles:
%box {
height: 300px;
width: 300px;
margin: 20px;
display: inline-block;
background-color: blue;
}
%small-box {
#extend %box;
width: 100px;
height: 100px;
}
Then we can let mixins do the rest of the work for us.
First we define our mixin for the variation we want:
#mixin green-bg($selector, $root) {
// Takes element out of any nesting
// Then we interpolate our argument variables
#at-root{
#{$root}-green {
// We can't set a placeholder as an argument so we'll just borrow the string and append the placeholder definer '%'
#extend %#{$selector};
background-color: green;
}
}
}
Then we define our mixins that will help us define our classes
#mixin box($parent) {
#extend %box;
#include green-bg(box, #{$parent});
}
#mixin small-box($parent) {
#extend %small-box;
#include green-bg(small-box, #{$parent});
}
When we define our classes they'll look clean like so:
.box { #include box(&); }
.box-sm { #include small-box(&); }
Here's what the final product looks like in action: https://codepen.io/Aricha_MW/pen/oNvxjEw
Edit: 08/15/2019 - Much cleaner version here:
https://codepen.io/Aricha_MW/pen/mdbPVXY

Related

What does `&#my-id` mean in CSS or SASS?

I inherited some CSS code, which is making use of the & character prior to the id name to style it. It looks something like this:
&#my-id {
// Content and attributes
}
There are also other instances of it, such as:
&:before {
// content and attributes
}
and
&:hover {
// content and attributes
}
What do those mean? I can't find a good way to express this in a search, so I can't find anything. My apologies if this is a duplicate.
It refers to the parent selector.
Input:
.parent {
&.child {
color: red;
}
}
Output:
.parent.child { color: red }
It's really helpful if you're writing CSS in BEM format, something like:
.block {
&__element {
width: 100px;
height: 100px;
&--modifier {
width: 200px;
}
}
}
.block__element { width: 100px; height: 100px;}
.block__element--modifier { width: 200px;}
<div class="block__element"></div>
<div class="block__element block__element--modifier"></div>
And finally, all examples I've shared have been concatenating the class names, but you can also use it as a reference, like:
.parent {
& .child {
color: red;
}
}
.parent {
.child & {
color: blue;
}
}
.parent .child { color: red }
.child .parent { color: blue }
Additional references:
http://lesscss.org/features/#parent-selectors-feature
https://blog.slaks.net/2013-09-29/less-css-secrets-of-the-ampersand/
Using the ampersand (SASS parent selector) inside nested selectors
It's a built-in feature of Sass: https://css-tricks.com/the-sass-ampersand/
You can use it when you're nesting selectors and you need a more specific selector, like an element that has both of two classes:
If your CSS looks like this:
.some-class.another-class { }
And you wanted to nest, the Sass equivalent is:
.some-class {
&.another-class {}
}

What is the best way to repeat a single declaration in Sass?

In my project I have many elements that will take this property text-align: center.
How can we avoid writing this declaration in all elements using Sass?
In other words, is there more efficient way to do something like this:
.one {
text-align: center;
}
.two {
text-align: center;
}
Try to use the power of pure CSS!
.one, .two {
text-align: center;
}
Use mixins
example
Declare
#mixin centerAlign {
text-align : center;
}
Use
.one {
#include centerAlign;
}
.two {
#include centerAlign;
}
You can also try like this
.text-center{
text-align:center;
}
Include text-center in html code where you want to display center align.
ex
<p class='text-center'>...</p>
<div class='text-center'>...</div>
You could also use extensions:
.center-align {
text-align:center
}
.one, .two {
#extend .center-align;
/*additional styling*/
}
It pretty much works out the same way as has been previouly posted

Use sass parent (ampersand) selector with fixed root class

In the following code example I generate two squares that ideally should turn red.
The first div .with-root currently stays blue, the second div .without-root turns red. I expect this behaviour, but don't see a proper solution to turn the .with-root div red as well.
Note the difference in the scss file: the first div works with a fixed parent selector, the second one doesn't have a parent. For CSS specificity I need to work with the .with-root {} wrapper.
.with-root {
.with-root__element {
display: block;
width: 5rem;
height: 5rem;
background: blue;
&--red & {
&__item {
background: red;
}
}
}
}
.without-root {
&__element {
display: block;
width: 5rem;
height: 5rem;
background: blue;
&--red & {
&__item {
display: block;
width: 5rem;
height: 5rem;
background: red;
}
}
}
}
The codepen can be found here: https://codepen.io/studiotwist/pen/OzMOmr
Well now that I hopefully understood your question I deleted my wrong idea before and the following solution should work.
Maybe there could be a logic erorr. You have actually three class definitions of .with-root__element and two of them are extended with --red and __item, but the 3rd one is however an extra class which comes in conflict with the other two. You're basically concatenating the endings --red and __item with the parent selector *__element. Also, the --red class is nested inside the *__element one without ending in your CSS but in HTML it is not. *__element and *__element--red are attached in the same HTML tag.
DEBUG
Only showing the first DIV.
.with-root {
.with-root__element {
display: block;
width: 5rem;
height: 5rem;
background: blue;
&--red {
//#error &; // this reference contains the entire document root including the root element .with-root which is wrong
#{&} &__item {
//#error #{&} &__item; // this is a wrong concatenation plus it takes the entire root with it
background: red; // thus, this won't render
}
}
}
}
Debug in action # Sassmeister
POSSIBLE FIX
#mixin bg($bg) {
width: 5rem;
height: 5rem;
background: $bg;
}
.with-root__element {
#include bg(blue);
$this: &;
#at-root {
.with-root {
#{$this}--red #{$this}__item {
#include bg(red);
}
}
}
}
.without-root {
&__element {
#include bg(blue);
&--red &__item {
#include bg(red);
}
}
}
Fork
#at-root is a directive which is useful for your issue as it basically crops the nesting level of the selector and styles can be defined inside the root-body by referencing the parent selector instead of the entire root. So I added a variable $this which will cache the reference. display: block is not needed as div elements have it by default. Sorry about the mixin, it's a habit. --red and __item have now the refence selector *__element.
#at-root Documentation

Using Susy, is there a way to include a piece of CSS inside breakpoint?

Succinctly, By using Susy's at-breakpoint responsive mixin, is there a way or function to include an external CSS file in the body of the breakpoint call?
Something like this:
.page {
border: 1px dashed #000;
height: 650px;
#include container;
.content {
color: red;
text-align: center;
border: 1px dashed red;
height: 400px;
#include span-columns(4 omega, 6);
.main {
color: green;
border: 1px dashed green;
text-align: center;
#include span-columns(1, 2);
}
.secondary {
color: blue;
border: 1px dashed blue;
#include span-columns(2, 3);
}
}
#include at-breakpoint(800px 8 250px) {
#include container;
.content {
#include span-columns(1, 1);
}
//IMPORT or INCLUDE CSS FILE HERE somehow...
} //end of breakpoint
}
I was hoping it was possible, because that'd be a whole lot cleaner than writing all the CSS rules I wish to be applied right there inline. Let me know if it's possible or what's the best practice in this case.
Thank you!
Sure. This isn't really a question about Susy, so much as a question about Sass. The same answers are true for working in the context of any wrapping mixin.
You can only import files at the root level (for now, at least) — but that's not your only option. I've seen people write all their site styles inside mixins, and then simply include those mixins as needed:
#mixin medium-layout {
// your medium css
}
.page {
#include at-breakpoint($medium) {
#include medium-layout;
}
}
You can use as many mixins as you like, call them whatever you want, and put them in other files (as long as you #include those files before calling the mixins).
I use a different approach. Rather than nesting everything under a .page selector, with big groups for each breakpoint, I break things up into modules, and keep the breakpoint styles attached to each module as needed.
// _main.scss
.main {
color: green;
#include at-breakpoint($medium) { /* changes to main */ }
}
// _secondary.scss
.secondary {
color: blue;
#include at-breakpoint($medium) { /* changes to secondary */ }
}
From a mobile-first perspective, where breakpoint styles may need to override or build on existing styles, this keeps all the related code in one place.

Nested mixins in LESS behaviour

Would it be possible in LESS to have a mixin nested inside another one so that the former gets called only when the element is child of an element with the latter mixin?
I know, confusing, here is a simple example (not working code, just concept):
LESS
.foo(#x) {
width: #x;
.foo(#y) {
width: #y/#x;
}
}
.a {
.foo(20px);
.b {
.foo(2);
}
}
Output CSS
.a {
width: 20px;
}
.a .b {
width: 10px;
}
When I do this, calling .foo(2) on .b gives compiles to width: 2.
Is this supposed to be like this by design, or am I getting something wrong in the syntax? Also, am I approaching the problem from a completely wrong angle and there is perhaps a much simpler solution that I am not considering?
EDIT
Ok, apparently that was fixed with the newest versions of LESS, what I am trying to achieve, though, is slightly more complicated than the minimal example I gave above.
Basically what I would like to happen is that every .foo which is a child of another element with the .foo mixin would take its parent variable for calculation, so, ideally
LESS
.foo(#x) {
width: #x;
.foo(#y) {
width: (#x/#y);
}
}
.a {
.foo(100px);
.b {
.foo(2px);
.c {
.foo(5px);
/* ...and so on */
}
}
}
Output CSS
.a {
width: 100px;
}
.a .b {
width: 50px;
}
.a .b .c {
width: 10px;
}
What I get is, instead:
.a .b .c {
width: 50px;
}
I tried to modify the LESS as follows:
.foo(#x) {
width: #x;
.foo(#y) {
#x: (#x/#y)
width: (#x);
}
}
But I get a syntax error for recursive variable definition. Apparently LESS doesn't allow for definitions like:
#a: 1;
#a: (#a+1);
I think the main problem here would be the #y/#x.
Try #x/#y and it should give something more expected ;-)
As the nested mixins get interpreted different throughout different less implementations I will now split the answer in two parts:
1. Sollution that worked on less2css.org with LESS >1.3.1 (using your nested mixins)
Otherwise I think the above code actually does what you want on less2css.org.
Just as a notion in LESS 1.4, you need to be careful with the math as by default it needs to be in brackets.
If you now just call such a mixin on nonnested rules, like
.b {
.foo(#y);
}
you neen to use units in the input variable, or ad unit() into your mixin, otherwise you will only get the number you put in, 2 for example:
.foo(#x) {
width: unit(#x,px);
.foo(#y) {
width: (#x/#y);
}
}
.a {
.foo(20px);
.b{
.foo(2);
}
}
.c {
.foo(2);
}
will output CSS:
.a {
width: 20px;
}
.a .b {
width: 10px;
}
.c {
width: 2px;
}
You could get even fancier, and check with guards if the attribute in the subclass has pixles as a unit, so that you can also nest classes where you don't pass a factor:
.foo(#x) {
width: #x;
.foo(#y) when (ispixel(#y)) {
width: #y;
}
.foo(#y) when not (ispixel(#y)) {
width: (#x/#y);
}
}
However testing this nested mixin solution appears to work only on less2css.org, but not on jsbin.com, lesstester.com and some other services [where you need to call the nested (second) level of mixin with .foo .too, to apply the second level of styling from the mixin].
So I propose an alternative approach, that I tested and it seems to work on all mentioned pages using less-css compilers with less >1.2.
2. Solution using guards with ispixel that should work on most LESS >1.2 installations
Instead of your nested mixin you could build two mixins, that are based on guards checking for pixels as the unit.
if attribute #x is in pixels => return width:#x; and assign #x to variable #width
if attribute #x is not in pixels => return width:#width/#x; (note: #width needs to be previously assigned by calling the mixin with the #x in px first)
example LESS:
.foo(#x) when (ispixel(#x)) {
width:#x;
#width:#x;
}
.foo(#x) when not (ispixel(#x)) {
width: (#width/#x);
}
.a, .b {
background-color: #ddd;
height: 100px;
}
.a {
.foo(100px);
.b {
background-color: red;
.foo(2);
}
}
th output CSS:
.a, .b {
background-color: #ddd;
height: 100px;
}
.a {
width: 100px;
}
.a .b {
background-color: red;
width: 50px;
}
Differs from your approach but is perhaps a bit more straight forward, and seems to work well.
Edit:
So as you don't want to distinguish between input with unit and input without unit, I can only think of calling a two parametric mixin, where one parameter is used for the base (width in your case) and the second as a factor that defaults to 1. And this you can call now recursively as many times as you want.
LESS:
.foo(#x,#y:1){
width:unit(#parent,px);
#parent:(#x/#y);
}
.a {
.foo(100px);
.b{
.foo(#parent,2px);
.c{
.foo(#parent,5px);
.d{
.foo(#parent,0.05);
}
}
}
}
output CSS:
.a {
width: 100px;
}
.a .b {
width: 50px;
}
.a .b .c {
width: 10px;
}
.a .b .c .d {
width: 200px;
}
So now it does not matter what unit the input has because you can assign the desired unit for the output in the mixin. When you call the mixin it overwrites the #parent variable for the current scope, which gets then inherited to nested rules, where you can use it as the width parameter #x when calling the mixin again. This should give you the desired result.

Resources