Sass loop: start from second instance of this element - sass

I have a series of divs in a step type layout. I am learning to use Scss at the moment and I thought maybe a mixin could work through the 12 divs and arrange them for me. So far I've got:
#mixin steps(){
$stepBlocks: 12;
#for $i from 1 through $stepBlocks {
.steps-#{$i} {
position: absolute;
top: (($i * 296) + px);
display: block;
}
}
}
This is what my div structure looks like:
I've made a HTML mockup as well:
http://jsfiddle.net/vdecree/CGGyL/
As you can see, the fiddle works fine, however how can I negate the effect of the first one? I need the first element to be top: 0; is there an if statement I can use? If you think you've a better way in which I can do this, I'd appreciate any help.

What you're likely wanting to is start with an offset of 0, rather than 296px.
#mixin steps(){
$stepBlocks: 12;
#for $i from 1 through $stepBlocks {
.steps-#{$i} {
position: absolute;
top: ($i - 1) * 296px;
display: block;
}
}
}

Related

Stripping variables of hashtag in SCSS before using them in selectors?

Another problem here.. my team wants to use borders in different widths, colors and positions. So, I made this:
$position-list: top right bottom left;
$colors-list: fff ccc ddd eee;
#for $i from 1 through 3 {
#each $position in $position-list {
#each $color in $colors-list {
.border-#{$position}-#{$i}-#{$color} {
border-#{$position}: #{$i}px solid #{"#"}#{$color} !important;
}
}
}
}
This works great, however, I want to include the colors as variables from my colors.scss sheet ($light-color, $dark-color etc). The problem is that the hashtags from the colors.scss sheet will be transfered as well ($dark-color: #000), so it will most likely generate a weird selector (.border-top-1-#000) or doesn't compile at all.
Is there a way of stripping the variables from the colors.scss sheet of their hashtags before putting them in the selector? Or does anyone have a different/better approach?
We can convert the color to a string (#inspect) and slice it (#str-slice).
$dark-color: #000;
$light-color: #fff;
$abc-color: #abc;
$position-list: top right bottom left;
$colors-list: $dark-color $light-color $abc-color;
#for $i from 1 through 3 {
#each $position in $position-list {
#each $color in $colors-list {
$stripped-color: str-slice(inspect($color), 2);
.border-#{$position}-#{$i}-#{$stripped-color} {
border-#{$position}: #{$i}px solid #{$color} !important;
}
}
}
}
Output (example):
.border-top-1-abc {
border-top: 1px solid #abc !important;
}

Dividing percentage variable in Sass?

I have a variable which is a number and a % eg 10%. How can I use it as a value in my SASS but apply a division on it?
I have this:
$value: 0.1;
$value-percent: $value * 1%;
$value-from-50: (50 - $value) * 1%;
.test {
padding-left: $value-percent;
}
.test2 {
width: $value-from-50;
}
Which outputs this:
.test {
padding-left: 10%;
}
.test2 {
width: 40%;
}
What I now need to do is apply half of the value of $value-percent:
.test3 {
padding-left: $value-percent / 2;
}
So that I can output:
.test3 {
width: 5%;
}
Ive tried various combinations of that example code with normal and curly brackets. I can get the correct number of 10 outputted into the CSS but the % is always missing from it.
If your initial var isn't a percentage and is just a number you may need to try this:
.test {
padding-right: ($var / 2) + 0%
}
Which is better practice as it'll convert the value you pass it into what you're adding it to, in this case a percentage.

How to preserve units with SASS computed variable?

I try to create some relative font-size values that match "nearly" pixels sizes:
html { font-size: 62.5%; }
$font-10px: 1em/1.1em;
$font-11px: 1.1em/1.1em;
$font-12px: 12em/11em;
.x10 { font-size: $font-10px; }
.x11 { font-size: $font-11px; }
.x12 { font-size: $font-12px; }
However, the output of this sass snipet is:
.x10 {
font-size: 0.90909;
}
.x11 {
font-size: 1;
}
.x12 {
font-size: 1.09091;
}
As you can see, the unit (em) has been stripped.
This results in a incorrect value.
How should I declare my variables to contains the correct unit?
Dividing one length by another length always results in the unit being removed if the lengths are using the same units. So your options are:
Divide using one length and one integer: 1.1em / 1.1
Multiply the unit back on afterwards: 1.1em / 1.1em * 1em
Don't use division at all: 1em
Add PX to the end of your variable and surround the variable with #{ }. This is known as interpolation #{ } and treats the variable as plain css. Interpolation also helps to remove any spaces between the number and unit of measurement:
$base-font-size: 16;
body { font-size: (62.5% * base-font-size); }
$font-10px: 1em/1.1em;
$font-11px: 1.1em/1.1em;
$font-12px: 12em/11em;
.x10 { font-size: #{$font-10px}px; }
.x11 { font-size: #{$font-11px}px; }
.x12 { font-size: #{$font-12px}px; }
Result:
.x10 {
font-size: 0.90909px;
}
.x11 {
font-size: 1px;
}
.x12 {
font-size: 1.09091px;
}
Since you mentioned accessability in the SA talk, I recommend that you add one of the mixins in this blog post by Hugo Giraudel to your project to allow the use REM units while also providing backwards compatibility for older browsers.

Passing a variable from inside a mixin declaration into the attached content block?

In Ruby, you can easily pass a variable from inside a method into the attached code block:
def mymethod
(1..10).each { |e| yield(e * 10) } # Passes a number to associated block
end
mymethod { |i| puts "Here comes #{i}" } # Outputs the number received from the method
I would like to do the same thing in SASS mixin:
=my-mixin
#for $i from 1 to 8
.grid-#{$i}
#content
+my-mixin
color: nth("red green blue orange yellow brown black purple", $i)
This code won't work because $i is declared inside the mixin declaration and cannot be seen outside, where the mixin is used. :(
So... How do i leverage variables declared inside the mixin declaration?
When i work with a grid framework and media queries, i need this functionality badly. Currently i have to duplicate what's inside the mixin declaration every time i need it, violating the DRY rule.
UPD 2013-01-24
Here's a real-life example.
I have a mixin that cycles through breakpoints and applies the provided code once for every breakpoint:
=apply-to-each-bp
#each $bp in $bp-list
+at-breakpoint($bp) // This is from Susy gem
#content
When i use this mixin i have to use this $bp value inside #content. It could be like this:
// Applies to all direct children of container
.container > *
display: inline-block
// Applies to all direct children of container,
// if container does not have the .with-gutters class
.container:not(.with-gutters) > *
+apply-to-each-bp
width: 100% / $bp
// Applies to all direct children of container,
// if container has the .with-gutters class
.container.with-gutters > *
+apply-to-each-bp
$block-to-margin-ratio: 0.2
$width: 100% / ($bp * (1 + $block-to-margin-ratio) - $block-to-margin-ratio)
width: $width
margin-right: $width * $block-to-margin-ratio
&:nth-child(#{$bp})
margin-right: 0
But this won't work, because the value of $bp is not available inside #content.
Declaring the variable before calling the mixin won't help, because #content is parsed once and before the mixin is parsed.
Instead, EACH time i need that, i have to do two ugly thighs:
declare an ad-hoc mixin,
write the cycle, violating the DRY principle:
// Each of the following mixins is mentioned in the code only once.
=without-gutters($bp)
width: 100% / $bp
=with-gutters($bp)
$block-to-margin-ratio: 0.2
$width: 100% / ($bp * (1 + $block-to-margin-ratio) - $block-to-margin-ratio)
width: $width
margin-right: $width * $block-to-margin-ratio
&:nth-child(#{$bp})
margin-right: 0
// Applies to all direct children of container
.container > *
display: inline-block
// Applies to all direct children of container,
// if container does not have the .with-gutters class
.container:not(.with-gutters) > *
#each $bp in $bp-list
+at-breakpoint($bp) // This is from Susy gem
+without-gutters($bp)
// Applies to all direct children of container,
// if container has the .with-gutters class
.container.with-gutters > *
#each $bp in $bp-list // Duplicate code! :(
+at-breakpoint($bp) // Violates the DRY principle.
+with-gutters($bp)
So, the question is: is there a way to do this Ruby-style?
Variables in Sass have scope to them. They're only visible in the block they were created in. If you want the variable to be accessible both inside and outside of the mixin, it has to be defined in the global scope:
$var: 0;
#mixin test {
$var: $var + 1;
color: red;
}
.test {
$var: 5;
#include test;
#debug $var; // DEBUG: 6
}
As long as you don't care about the state of $var for very long, this should work out ok for your purposes.
For your example, this won't work because it looks like the #content is processed first. What you need is a mixin that's written differently:
#mixin test($properties...) {
#for $i from 1 to 8 {
.grid-#{$i} {
#each $p in $properties {
$list: nth($p, 2);
#if length($list) > 1 {
#{nth($p, 1)}: nth($list, $i);
} #else {
#{nth($p, 1)}: $list;
}
}
#content;
}
}
}
.test {
#include test(color (red green blue orange yellow brown black purple));
}
The generated CSS:
.test .grid-1 {
color: red;
}
.test .grid-2 {
color: green;
}
.test .grid-3 {
color: blue;
}
.test .grid-4 {
color: orange;
}
.test .grid-5 {
color: yellow;
}
.test .grid-6 {
color: brown;
}
.test .grid-7 {
color: black;
}
A mixin like this can be fed any number of arguments and still allows you to use #content if you wish.
I have run into this problem myself and AFAIK this is a current limitation in SASS.
So this is currently unavailable in Sass.
There's a relevant ticket in the Sass issue queue: https://github.com/nex3/sass/issues/871 It's in the planned state but will probably not make it until at least Sass 4.0.

How do I combine a range of sass classes that are created in a loop

I am using SASS to construct a range of classes from variables passed into a mixin.
#mixin classes($key, $num) {
#for $i from 1 through $num {
[class*=#{$key}-#{$i}] {
#content
}
}
}
#include classes(grid, 8) {
width:100px;
}
It currently makes the classes like I want, but all as 8 separate classes (which are identical in #contents. Is there a way to merge them all together so I get:
[class*=grid-1],
[class*=grid-2],
....
[class*=grid-8],
{
width:100px;
}
I'm not sure if it's even possible to do this? Any pointers would be greatly appreciated.
Thanks,
Carl
I found the answer eventually
$classes: ();
#for $i from 1 through $cols {
$classes: join($classes, unquote("#{$prefix}#{$i} "), comma);
}
#{$classes} {
float: left;
margin-right: $gutterPercent;
width: $columnWidth;
}

Resources