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>
Related
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%;
}
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;
}
}
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;
}
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.
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;
}