How to solve and compensate the sub-pixel rounding issue? - css

I try to iron out the sub-pixel rounding error for quite a while now, but so far i've failed miserably again and again. I try to accomplish that endeavour with the help of Sass and Susy. The mixin i've used in my last try i got from the Susy issue tracker on Github (i've used space, columns as well as push on the margin-left/right property like suggested there):
#mixin isolate($location, $context: $columns, $dir: 'ltr') {
#if $dir == 'ltr' {
margin-right: -100%;
margin-left: space($location, $context);
}
#else if $dir == 'rtl' {
margin-left: -100%;
margin-right: space($location, $context);
}
}
My Scss looks like the following:
.imggrid{
#include with-grid-settings($gutter: 0.1%){
$y:2;
li{
#include span-columns(2,12);
#for $x from 1 through 30
{
&:nth-child(#{$x}){
#include isolate($y,12);
$y: $y + 2;
#if $y > 12{
$y: 2;
}
}
}
#include nth-omega(6n);
}
}
}
First i've created a custom gutter for the image grid. Then i've defined a variable y to iterate up in the steps of two to be able to call the isolate mixin (isolate(2,12) isolate (4,12) etc). For values larger than 12 the value gets reset to two within the for loop in the end. Then i span a column for each li walking through the 30 images. Each time calling the iterating isolate mixin. After the for loop i've appended the #include nth-omega(6n); to get a new line after each sixth element.
But somehow it doesn't work at all. The first four rows are missing the first image and on the last row the first five elements are missing. Any ideas and suggestions are appreciated. Thanks Ralf

UPDATE: I adjusted these mixins to be 1-indexed rather than 0-indexed. I think that is the more common.
You're close, but the math isn't quite right. It get's a bit complex to keep everything straight, so I wrote up a mixin to take care of it for you. I also adjusted the isolate mixin so that it uses the existing Susy $from-direction variable:
#mixin isolate($location, $context: $total-columns, $from: $from-direction) {
$to: opposite-position($from);
margin-#{$to}: -100%;
margin-#{$from}: space($location - 1, $context);
}
#mixin isolate-list($columns, $context: $total-columns, $from: $from-direction) {
$position: 1;
$line: floor($context / $columns);
#include span-columns($columns, $context, $from: $from);
#for $item from 1 through $line {
$nth: '#{$line}n + #{$item}';
&:nth-child(#{$nth}) {
#include isolate($position, $context, $from);
#if $position == 1 { clear: $from; }
$position: $position + $columns;
#if $position > $context { $position: 1; }
}
}
}
Use it just like span-columns, width & context. That's all there is too it:
.imggrid{
li{
#include isolate-list(4,12);
}
}
That should work with any width, in any context, for any number of list items. Cheers!

Related

SCSS function with for loop inside not working

I'm trying to write a function that creates a grid layout based on multiple arrangements (as opposed to just the 12 column grid) in Sass (scss syntax). The for loop inside works properly on its own, but when I wrap it in a function it no longer works. I'm new to using Sass functions, so maybe I'm messing up the syntax? Or is this just not possible? I'm just trying to avoid having to write a new for loop for each layout I want to achieve. Thanks in advance.
#function create-grid($num-cols) {
#for $col from 1 through $num-cols {
.col-#{$col}-of-#{$num-cols} {
width: percentage($col / $num-cols);
}
}
}
Sass functions return a value.
If you want to generate css programatically like this, you want to use a mixin.
https://www.sitepoint.com/sass-basics-the-mixin-directive/
#mixin grid($num-cols) {
#for $col from 1 through $num-cols {
.col-#{$col}-of-#{$num-cols} {
width: percentage($col / $num-cols);
}
}
}
#include grid(12)
For anyone who stumbles across this post, the final product is:
#mixin create-grid($num-cols, $col-gutter) {
#for $col from 1 through $num-cols {
.col-#{$col}-of-#{$num-cols} {
width: calc(
((100% - ((#{$num-cols} - 1) * #{$col-gutter})) / #{$num-cols}) *
#{$col} +
((#{$col} - 1) * #{$col-gutter})
);
}
}
}
Thanks a lot to #HorusKol for introducing me to the world of mixins!

SASS Customize Class Names with Variables

Is there any way to customize the variables in SASS?
For example:
.m-b-{$number} {
margin-bottom: $number;
}
If I give class="m-b-50" to an element, it should take margin-bottom 50. I just want to know if it is possible with SASS.
Yes it is possible with the help of variable interpolation or variable substitution which uses #{} for variable substitution in SASS and mixins which is a block of code just like function.
Interpolation is the process of evaluating an expression or a string containing one or more variables, yielding a result in which the variables are replaced with their corresponding values.
Simple example of interpolation and set values to the css property in SASS:
$number:60;
$n: 20px;
.m-b-#{$number}{
margin-bottom: #{$number}px;
margin-top: $n;
}
To create customize class names, will use mixins:
#mixin margin-class($side, $number) {
$firstLetter: str-slice($side, 0, 1);
.m-#{$firstLetter}-#{$number}{
margin-#{$side}: #{$number}px;
}
}
$margins: (10, 20);
$sides: ("top", "right", "bottom", "left");
#mixin generate-margin(){
#each $margin in $margins{
#each $side in $sides{
#include margin-class($side, $margin);
}
}
}
#include generate-margin();
Here, generate-margin() will get executed which will call margin-class() for each $margins and $sides, and will generate the below CSS classes:
.m-t-10 {
margin-top: 10px;
}
.m-r-10 {
margin-right: 10px;
}
.m-b-10 {
margin-bottom: 10px;
}
.m-l-10 {
margin-left: 10px;
}
.m-t-20 {
margin-top: 20px;
}
.m-r-20 {
margin-right: 20px;
}
.m-b-20 {
margin-bottom: 20px;
}
.m-l-20 {
margin-left: 20px;
}
That's the one way when you want only for specific values, but if you want to create margin class for 0-20, you can loop thru 0 to 20 as shown below:
#mixin generate-margin(){
#for $margin from 1 through 20{
#each $side in $sides{
#include margin-class($side, $margin);
}
}
}
For anyone else facing this issue, here is how one can achieve this:-
#for $i from 1 through 10 {
.mb-#{$i} {
margin-bottom: #{$i}rem;
}
}
The answer is: no it is not possible. SASS is just a language to pre-generate CSS for you. There is no on-demand, dynamic creation of classes triggered by the contents of your HTML markup. When it comes time for the browser to render your HTML and apply your specified classes, it is still just using CSS. I.e. if you assign class="m-b-50" to an element, the class .m-b-50 must already be explicitly defined somewhere. As noted in the other answers, SASS can make it easier to generate a bunch of pre-defined classes but you must know which values you want to support up front.
Now, you could generate classes for some very large, all-inclusive range like -1000 to 1000 to effectively support all values you might ever try to use and it would seem to do what you wanted, but you would be forcing your users to download a larger CSS file with, most likely, a large percentage of it being unused CSS which is wasteful and can be inconsiderate in a world of paid & limited data plans.

Sass recursion mixin

I would like to know, if in sass is allowed to write a recursion mixin like this Less example :
.size($val) when ($val < 200 ) {
width: $val;
height: $val;
.size($val + 50px);
}
I'm trying something like this, but i don't have any output
#mixin test($val) {
width: $val;
height: $val;
#for $i from 1 through 5 {
#include test($val);
}
}
.block {
#include test(5);
}
Unlike your LESS example, your Sass mixin does not have a way to break out of the loop.
#mixin test($val) {
width: $val;
height: $val;
#if $val > 0 {
#include test($val - 1);
}
}
In sass, you can create recursive functions, but your example is wrong.
Looking at your code, I see that you are in infinite loop, because $var variable is not used in the #for iterator. You only iterates from 1 to 5 infinitely.
The recursive approach here is the same as another languages, you have to use the parameter to call the function again, and when this variable is the same as 0 (for example), returns a value ... its value is used to the last call of the function to set the new value ... and again the first step.
Here are examples of Sass recursive functions:
https://gist.github.com/paramburu/9613303
http://hugogiraudel.com/2013/08/08/advanced-sass-list-functions/#section-3
Hope it helps.
Regards.

How to create a sprite from a folder with and without background-size (using Compass)

I want to use a Compass generated icon-sprite for two different scenarios.
Use the icon(s) in original size.
Use it the same icon(s) as a smaller version using CSS3 property background-size.
I first do this:
$logo-spacing: 20px;
#import "logo/*.png";
#include all-logo-sprites;
Now I can use the general created CSS-classes like .logo-twitter etc.
Two achieve the second result I could use this (darren131 / gist:3410875 - resize sprites in Compass):
#mixin resize-sprite($map, $sprite, $percent) {
$spritePath: sprite-path($map);
$spriteWidth: image-width($spritePath);
$spriteHeight: image-height($spritePath);
$width: image-width(sprite-file($map, $sprite));
$height: image-height(sprite-file($map, $sprite));
#include background-size(ceil($spriteWidth * ($percent/100)) ceil($spriteHeight * ($percent/100)));
width: ceil($width*($percent/100));
height: ceil($height*($percent/100));
background-position: 0 floor(nth(sprite-position($map, $sprite), 2) * ($percent/100) );
}
.my-other-div-with-small-logos {
.logo-twitter {
$spriteName: twitter;
$percentage: 40;
#include resize-sprite($logo-sprites, $spriteName, $percentage);
}
}
But if I have around 30 logos I would need to repeat this manually for each sprite-class.
Is it possible to import the folder twice, once for the original size and a second time with the backround-size property?
Or modify the mentioned method to create all classes automatically within another <div class="my-other-div-with-small-logos"> where the icons should appear smaller?
Or am I thinking in the wrong direction here?
That does it. It iterates over the whole map:
#each $sprite in sprite_names($logo-sprites) {
.logo-#{$sprite} {
#include resize-sprite($logo-sprites, $sprite, 40);
}
}
This helped: Way to iterate over sprites in a map
It's great to scale down sprites in modern Browsers without loading another generated sprite-image. If the logos sometimes are 50px, but should also be 20px somewhere else, this is perfectly fine.
Thank you all for this. It works! Now, i have gone ahead and expand on it as i needed something a little more dynamic where icons are constructed based on ico-[size] ico-[image] & swatch-[color]
$myicons-spacing: 20px;
#import "icons/myicons/*.png";
#include all-myicons-sprites;
#mixin resize-sprite($map, $sprite, $percent) {
$spritePath : sprite-path($map);
$spriteWidth : image-width($spritePath);
$spriteHeight : image-height($spritePath);
$width : image-width(sprite-file($map, $sprite));
$height : image-height(sprite-file($map, $sprite));
#include background-size(ceil($spriteWidth * ($percent/100)) ceil($spriteHeight * ($percent/100)));
width : ceil($width*($percent/100));
height : ceil($height*($percent/100));
background-position : 0 floor(nth(sprite-position($map, $sprite), 2) * ($percent/100) );
}
#each $sprite in sprite_names($myicons-sprites) {
.ico-xsmall.myicons-#{$sprite} {
#extend .myicons-#{$sprite};
#include resize-sprite($myicons-sprites, $sprite, 35);
}
.ico-small.myicons-#{$sprite} {
#extend .myicons-#{$sprite};
#include resize-sprite($myicons-sprites, $sprite, 50);
}
.ico-medium.myicons-#{$sprite} {
#extend .myicons-#{$sprite};
#include resize-sprite($myicons-sprites, $sprite, 87.5);
}
.ico-large.myicons-#{$sprite} {
#extend .myicons-#{$sprite};
#include resize-sprite($myicons-sprites, $sprite, 100);
}
}
Create placeholders for each in a loop and then include the placeholder wherever you need. For example:
#mixin resize-sprite($map, $sprite, $percent) {
$spritePath: sprite-path($map);
$spriteWidth: image-width($spritePath);
$spriteHeight: image-height($spritePath);
$width: image-width(sprite-file($map, $sprite));
$height: image-height(sprite-file($map, $sprite));
#include background-size(ceil($spriteWidth * ($percent/100)) ceil($spriteHeight * ($percent/100)));
width: ceil($width*($percent/100));
height: ceil($height*($percent/100));
background-position: 0 floor(nth(sprite-position($map, $sprite), 2) * ($percent/100) );
}
#each $image in twitter, facebook, pinterest {
%logo-#{$image} {
#include resize-sprite($logo-sprites, $image, 40);
}
}
.my-other-div-with-small-logos {
.logo-twitter {
#extend %logo-twitter;
}
}
Note that this assumes all images should be resized by 40%. You'll need to do more creative iterating if you need to specify different percentages for different logos.
Better yet, maybe just generate the classes in the loop?
.my-other-div-with-small-logos {
#each $image in twitter, facebook, pinterest {
.logo-#{$image} {
#include resize-sprite($logo-sprites, $image, 40);
}
}
}

How to combine 2 SASS functions as they do almost the same thing just using a different calculation?

I was wondering if someonce could advise me on how I could combine the SASS functions below, basically they do the same thing but one is used specifically for IE so having 2 is a waste. I've tried adding if..else statements with an additional parameter like if ( $property == 'ie' ) but with no success.
Functions:
// To use width, margin, padding...
#function percentageWidth( $elementWidth, $containerWidth ){
#return $elementWidth/$containerWidth * 100%;
}
// To use *width ie6/7 workarounds
#function iePercentageWidth( $elementWidth, $containerWidth ){
#return $elementWidth/$containerWidth - 0.5/$containerWidth * 100%;
}
Current CSS:
width: percentageWidth( 214,945 );
*width: iePercentageWidth( 214,945 );
I would also like to use this function on things like:
margin: percentageWidth( 23,945 );
Here's a general mixin you can use to create a pair of properties:
#mixin ieprop($name, $elementWidth, $containerWidth) {
#{$name}: percentageWidth($elementWidth, $containerWidth);
*#{$name}: iePercentageWidth($elementWidth, $containerWidth);
}
You pass the property name as the first parameter, like so:
.cls {
#include ieprop('width', 214, 945);
#include ieprop('margin', 23, 945);
}
Is this what you need?
#mixin twoWidths($x, $y){
width: percentageWidth($x, $y);
*width: iePercentageWidth($x, $y);
}
Then in your stylesheet you'd call it like:
#include twoWidths(500,700);

Resources