sass / scss - create class shortcuts - sass

While doing some css I came up with the idea of combining multiple classes into one. The reason is, to use short class names which could probably already be in use.
Assume the following:
.f-box-cell {
flex : 1 1 auto;
// colors
&.red { background-color: red; }
&.green { background-color: green; }
...
// sizes
&.b25 { flex-basis: 25%; }
&.b50 { flex-basis: 50%; }
...
// other options
...
}
Instead of doing ...
<div class="f-box-cell">
<div class="f-box-cell red">
<div class="f-box-cell b25">
<div class="f-box-cell red b25">
... I want do have something like ...
<div class="f-box-cell">
<div class="f-box-cell-red">
<div class="f-box-cell-b25">
<div class="f-box-cell-red-b25">
... with the same effect as the normal class listings.
Obviously, the more "subclasses" are added the more combinations would exist. So I'm interessted - if possible - in creating all these combinations automatically. Completely awesome would be, if the position of the "classes" inside the "combined class" would not matter.
Therefore my 2 main questions are:
Is there a way to achieve something like this with sass / scss?
Is the idea completely stupid?
Thanks in advance

If you only want to have all your classes prefixed and don't mind adding two classes (class="f-box-cell f-box-cell-red"), then you can just use the ampersand multiple times:
.f-box-cell {
flex : 1 1 auto;
// colors
& &-red { background-color: red; }
& &-green { background-color: green; }
...
// sizes
& &-b25 { flex-basis: 25%; }
& &-b50 { flex-basis: 50%; }
...
// other options
...
}
If you only want to use the most specific class, which should inherit from the base one, you can use the #extend rule:
.f-box-cell {
flex : 1 1 auto;
// colors
&-red { #extend .f-box-cell; background-color: red; }
&-green { #extend .f-box-cell; background-color: green; }
...
// sizes
&-b25 { #extend .f-box-cell; flex-basis: 25%; }
&-b50 { #extend .f-box-cell; flex-basis: 50%; }
...
// other options
...
}
Which compiles to:
.f-box-cell,
.f-box-cell-b50,
.f-box-cell-b25,
.f-box-cell-green,
.f-box-cell-red {
flex: 1 1 auto;
}
.f-box-cell-red {
background-color: red;
}
.f-box-cell-green {
background-color: green;
}
.f-box-cell-b25 {
flex-basis: 25%;
}
.f-box-cell-b50 {
flex-basis: 50%;
}

Related

One shared selector between multiple classes

How to apply the same focus state to multiple different classes?
Problem:
.btn {
&.error {
border-color: red;
}
&.primary {
border-color: green;
}
&:focus {
border-color: blue;
// this is not applied but i don't want to
// declare the same style to both classes
}
}
I understand this would be one option, but it is also not the prettiest option as i need to list them separately here
.btn {
&.error {
border-color: red;
}
&.primary {
border-color: green;
}
&.primary:focus, &.error:focus {
border-color: blue;
}
}
Are there any better ways?
Using & again in the nested rule is a good way for your purpose.
.btn {
&.error {
border-color: red;
}
&.primary {
border-color: green;
}
&.primary, &.error {
&:focus{
border-color: blue;
}
}
}

SCSS use parent style for children (texts)

I have next scss code:
.btn {
font-size: 24pt;
color: red;
&-small {
font-size: 17pt;
}
&-meddium {
font-size: 24pt;
}
&-big {
font-size: 36pt;
}
}
and i want that all my buttons (btn, btn-small, btn-meddium, btn-big) will have color RED.
P.S. I want realize it by only one class
Since you're using SCSS, you can use #mixin and #include:
#mixin btn {
color: red;
}
.btn {
#include btn;
}
.btn-smaall {
#include btn;
font-size: 17pt;
}
And to to do that for the rest of the button classes you want to create.
Another way would be to addjust your selectors as such:
.btn {
font-size: 24pt;
color: red;
&.btn-small {
font-size: 17pt;
}
&.btn-meddium {
font-size: 24pt;
}
&.btn-big {
font-size: 36pt;
}
}
And then you can create a small button by adding the class btn and btn-small to a button. e.g. <button class="btn btn-small>Test</button>
You can define classes like this (my preference): btn btn__small
Then you can trigger it with scss like you wanted:
.btn {
font-size: 24pt;
color: red;
&__small {
font-size: 17pt;
}
}

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;
}

Sass extend arguments or a workaround

I am working on a new project that will be in two languages. Arabic and English.
I will be using SASS on this project, and before I start doing any development I want to make sure that I use the best approach regarding the floats and RTL / LTR
I was wondering if it's possible to have arguments to the extend SASS function.
The body of my HTML will have a class depending on the user language. Based on that class, I want to float to the left if EN and right if AR. To do that, I have a quick draft here: http://jsfiddle.net/WJ6wC/
.lang-en {
direction: ltr;
%float-dir {
float:left;
}
%text-align {
text-align:left;
}
}
.lang-ar {
direction: rtl;
%float-dir {
float:right;
}
%text-align {
text-align:right;
}
}
.content {
width:500px;
margin:20px auto;
padding:20px;
background:#e5e5e5;
overflow:auto;
h1, p {
margin:0;
}
.thumbnail {
#extend %float-dir;
margin-right:20px;
}
.description {
#extend %float-dir;
#extend %text-align;
}
}
.meta {
a {
font-size:12px;
text-decoration:none;
}
}
.icon {
display:inline-block;
width:10px;
height:10px;
&:after {
content:"+";
display:inline-block;
}
}
.control-panel {
margin:20px 0 0 0;
padding:10px 0 0 0;
border-top:1px solid #ccc;
}
There is a jQuery bit that changes the class on click.
Unfortunately, the extent does not accept any arguments from what I know. How can I do so that, depending on the body class to have instead of 'margin-left', 'margin-right' or instead of 'padding-left', 'padding-right'?
I was thinking something like:
.content {
%marginSwap(10px 0 0 10px);
}
and somewhere inside the language class a mixing to swap margin-left value with margin right value.
like a mixin but dependent on the parent class like an extend:
#mixin marginSwap($top, $right, $bottom, $left) {
margin-top:$top;
margin-right:$left; // Left instead of right
margin-bottom:$bottom;
margin-left:$right;
}
Hope you understand better from my jsfiddle
Might I recommend taking advantage of the lang attribute? You could just as easily use mixins instead of extends if you wanted.
http://codepen.io/cimmanon/pen/tshuK
#mixin margin($v) {
#if length($v) == 4 {
margin: $v;
&:lang(ar) {
margin: nth($v, 1) nth($v, 4) nth($v, 3) nth($v, 2);
}
} #else {
margin: $v;
}
}
#mixin margin-right($v) {
margin-right: $v;
&:lang(ar) {
margin-left: $v;
}
}
:lang(en) {
direction: ltr;
}
:lang(ar) {
direction: rtl;
}
%float-left {
//&:lang(en) {
float: left;
//}
&:lang(ar) {
float: right;
}
}
%align-left {
//&:lang(en) {
text-align: left;
//}
&:lang(ar) {
text-align: right;
}
}
.content {
width:500px;
margin:20px auto;
padding:20px;
background:#e5e5e5;
overflow:auto;
h1, p {
margin:0;
}
.thumbnail {
#extend %float-left;
//#include margin(0 20px 0 0);
#include margin-right(20px);
}
.description {
#extend %float-left;
#extend %align-left;
}
}
Instead of using a class, you would set the lang attribute on whatever ancestor element you feel is appropriate (it could be all the way up on the html or body tag if you want).
<div lang="en"></div>

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.

Resources