Streamline LESS mixin - less

I have some LESS for making margins based on the side being passed (top, right, bottom, left or all):
.margin(#px,#side) when (#side = top) {
(){ margin-top: ~"#{px}px"; }
}
.margin(#px,#side) when (#side = right) {
(){ margin-right: ~"#{px}px"; }
}
.margin(#px,#side) when (#side = bottom) {
(){ margin-bottom: ~"#{px}px"; }
}
.margin(#px,#side) when (#side = left) {
(){ margin-left: ~"#{px}px"; }
}
.margin(#px,#side) when (#side = all) {
(){ margin-top: ~"#{px}px";
margin-right: ~"#{px}px";
margin-bottom: ~"#{px}px";
margin-left: ~"#{px}px"; }
}
My question is that if I have an ID like this:
#testfeature {
.margin(10px,l);
.margin(10px,r);
}
Then LESS compiles it like this:
#testfeature {
margin-left:10px;
}
#testfeature {
margin-right:10px;
}
How do I get it to compile like this:
#testfeature {
margin-left:10px;
margin-right:10px;
}

Get rid of the unnecessary () { ... }'s that are wrapping all of your mixin styles. They're causing the selectors to be interpreted oddly and separating them into different selections, rather than joining everything under one selection:
.margin(#px,#side) when (#side = top) {
margin-top: ~"#{px}px";
}
.margin(#px,#side) when (#side = right) {
margin-right: ~"#{px}px";
}
.margin(#px,#side) when (#side = bottom) {
margin-bottom: ~"#{px}px";
}
.margin(#px,#side) when (#side = left) {
margin-left: ~"#{px}px";
}
.margin(#px,#side) when (#side = all) {
margin-top: ~"#{px}px";
margin-right: ~"#{px}px";
margin-bottom: ~"#{px}px";
margin-left: ~"#{px}px";
}
You could probably also drop the ~"#{px}px" in favor of simply #px, also the last mixin should probably be:
.margin(#px, #side) when (#side = all) {
margin: #px;
}

To get them to "group" you need to create a nested and grouped mixin. Below is a grouped mixin function, "streamlined" for margin setting.
It takes anywhere from 1 to 8 parameters; always in pairs of position then value (except as noted below; it can accept any order of position and values of auto or inherit are allowed).
Passing a single "position" just sets a 0px margin on that position.
Defaults non-unit numbers to px, but keeps explicitly set units as passed.
Passing a single parameter that is number value will set all the margins to that number equally.
LESS Mixin
.setMargins(#s1: ~'', #v1: 0, #s2: ~'', #v2: 0, #s3: ~'', #v3: 0, #s4: ~'', #v4: 0) {
.setPos(top, #T) {
.setNum() when (isnumber(#T)) {
margin-top: #T * 1px;
}
.setNum() when not (isnumber(#T)){
margin-top: #T;
}
.setNum();
}
.setPos(right, #R) {
.setNum() when (isnumber(#R)) {
margin-right: #R * 1px;
}
.setNum() when not (isnumber(#R)) {
margin-right: #R;
}
.setNum();
}
.setPos(bottom, #B) {
.setNum() when (isnumber(#B)) {
margin-bottom: #B * 1px;
}
.setNum() when not(isnumber(#B)) {
margin-bottom: #B;
}
.setNum();
}
.setPos(left, #L) {
.setNum() when (isnumber(#L)) {
margin-left: #L * 1px;
}
.setNum() when not (isnumber(#L)) {
margin-left: #L;
}
.setNum();
}
//allows all to be called with one number or value
.setPos(#A, 0) when (isnumber(#A)) {
margin: #A * 1px;
}
.setPos(auto, 0) {
margin: auto;
}
.setPos(inherit, 0) {
margin: inherit;
}
//default null if no valid side given
.setPos(#other, 0) {}
//call all possible positions
.setPos(#s1, #v1);
.setPos(#s2, #v2);
.setPos(#s3, #v3);
.setPos(#s4, #v4);
}
Examples
.setMargins(right); produces margin-right: 0px;
.setMargins(right, 15); produces margin-right: 15px;
.setMargins(left, 10em); produces margin-left: 10em;
.setMargins(top, 12, right, 10); produces: margin-top: 12px; margin-right: 10px;
.setMargins(25); produces: margin: 25px;
.setMargins(auto); produces: margin: auto;
So you use it in a selector:
LESS
#testfeature {
.setMargins(left, 10, right, 10);
}
CSS Output
#testfeature {
margin-left: 10px;
margin-right: 10px;
}

Related

Reduce the repetition in less project

I have a couple of less files that are very repetitious. I would like to follow the DRY (Don't Repeat Yourself) principle as much as possible.
Here is a snippet:
.margin-loose {
margin-top: #loose;
margin-bottom: #loose;
}
.margin-loose-top {
margin-top: #loose;
}
.margin-loose-bottom {
margin-bottom: #loose;
}
.margin-loose-sm {
margin-top: #loose-sm;
margin-bottom: #loose-sm;
}
.margin-loose-top-sm {
margin-top: #loose-sm;
}
.margin-loose-bottom-sm {
margin-bottom: #loose-sm;
}
.margin-loose-xs {
margin-top: #loose-xs;
margin-bottom: #loose-xs;
}
.margin-loose-top-xs {
margin-top: #loose-xs;
}
.margin-loose-bottom-xs {
margin-bottom: #loose-xs;
}
Is it possible to use Less mixins or loops to shorten this file?

Nested loops in Less

I have some nested loops in Less.
It should create css rules for padding with 3 sizes and 4 direction.
Code sample:
#sizes: normal, small, large;
#size-normal: 1em;
#size-small: 0.8 * #size-normal;
#size-large: 1.2 * #size-normal;
.l-padding {
#directions: top, left, right, bottom;
.s(#i: length(#sizes)) when (#i > 0) {
.s((#i - 1));
.d(#j: length(#directions)) when (#j > 0) {
.d((#j - 1));
#dir: extract(#directions, #j);
#s: extract(#sizes, #i);
#size: "size-#{s}";
&_#{dir}_#{s} {
.l-padding-mixin(#dir, ##size);
}
}
.d();
}
.s();
}
I don't know where is the problem but it compile to many duplacates. However each independet loop do their job.
.l-padding_top_normal {
padding-top: 1em;
}
.l-padding_left_normal {
padding-left: 1em;
}
.l-padding_right_normal {
padding-right: 1em;
}
.l-padding_bottom_normal {
padding-bottom: 1em;
}
.l-padding_top_normal {
padding-top: 1em;
}
.l-padding_left_normal {
padding-left: 1em;
}
.l-padding_right_normal {
padding-right: 1em;
}
.l-padding_bottom_normal {
padding-bottom: 1em;
}
.l-padding_top_normal {
padding-top: 1em;
}
...
.l-padding_top_normal {
padding-top: 1em;
}
.l-padding_top_small {
padding-top: 0.8em;
}
.l-padding_left_small {
padding-left: 0.8em;
}
.l-padding_right_small {
padding-right: 0.8em;
}
.l-padding_bottom_small {
padding-bottom: 0.8em;
}
.l-padding_top_normal {
padding-top: 1em;
}
.l-padding_left_normal {
padding-left: 1em;
}
.l-padding_right_normal {
padding-right: 1em;
}
.l-padding_bottom_normal {
padding-bottom: 1em;
}
...
To many duplicates. Can any one help with this?
UPD
thanks to #Harry find solution:
.l-padding-mixin(#direction, #size: 1em) {
padding-#{direction}: #size;
}
#sizes: normal, small, large;
#size-normal: 1em;
#size-small: 0.8 * #size-normal;
#size-large: 1.2 * #size-normal;
//==== layouts ====
.l-padding {
#directions: top, left, right, bottom;
#i: length(#sizes);
#j: length(#directions);
.d(#j) when (#j > 0) {
.d((#j - 1));
#dir: extract(#directions, #j);
#s: extract(#sizes, #i);
#size: "size-#{s}";
&_#{dir}_#{s} {
.l-padding-mixin(#dir, ##size);
}
}
.-(#i) when (#i > 0) {
.-((#i - 1));
.d(#j);
} .-(#i);
}

Use computed value in element using less.js

Have the following bit of code:
generate-margins(4);
.generate-margins(#n, #i: 1) when (#i =< #n) {
.marginleft#{i} {
margin-left: (5px * #i);
}
.marginright(5*#{i}) {
margin-right: (5px * #i);
}
.generate-margins(#n, (#i + 1));
}
Which gives me:
.marginleft1 {
margin-left: 5px;
}
.marginright(5*1) {
margin-right: 5px;
}
.marginleft2 {
margin-left: 10px;
}
.marginright(5*2) {
margin-right: 10px;
}
.marginleft3 {
margin-left: 15px;
}
.marginright(5*3) {
margin-right: 15px;
}
.marginleft4 {
margin-left: 20px;
}
.marginright(5*4) {
margin-right: 20px;
}
What I wanted was:
.marginright5 {... }
.marginright10 {... }
...
How can use computed value in element name? I tried using all string functions with no luck.
Did some more testing and came up with this:
.generate-margins(#n, #i: 1) when (#i =< #n) {
#l : #i*5;
.marginleft#{l} {
margin-left: (5px * #i);
}
.marginright#{l} {
margin-right: (5px * #i);
}
.generate-margins(#n, (#i + 1));
}
Would still like to know if there is another way. Cheers.

Splitting up a list of variables in Sass

In Sass is there a way to split up a list of variables / classes with hyphens?
It's a fuzzy question title so it's probably best I show what I'm trying to achieve.
In the below example I'm trying to create some utility classes that I can apply to HTML elements to help with vertical rhythm.
For example I may want to give an element a small margin that is consistent with my vertical rhythm strategy and so I'll add the class .m-t-s (which stands for margin-top-small).
I then want to output versions of those utility classes against for each media query I have for fine grain control e.g. I may want a class .m-t-s-768 which will add a small top margin when there is a minimum viewport width of 768px.
I have achieved this below, but it is a very long-winded and repetitive way of doing it. Can anyone suggest a more concise way?
Variables
––––––––––
$mediaQueries-px:
640,
768,
1024
;
$s: 20px; /* FYI I've simplified these examples for the sake of demonstration, normally I use something like ($baseLineHeight / 1.5) + rem */
$m: 50px;
$l: 60px;
Creating the classes
–––––––––––––––––––––
.m-t-s {
margin-top: $s;
}
/* Create versions for each defined media query */
#each $mediaQuery in $mediaQueries-px {
#media screen and (min-width: ($mediaQuery / 16px)) {
.m-t-s-#{$mediaQuery} {
margin-top: $s;
}
}
}
.m-t-m {
margin-top: $m;
}
/* Create versions for each defined media query */
#each $mediaQuery in $mediaQueries-px {
#media screen and (min-width: ($mediaQuery / 16px)) {
.m-t-m-#{$mediaQuery} {
margin-top: $m;
}
}
}
This repeats for .m-t-l too (margin top large), and then it continues for padding classes (e.g. .p-t-s and so on), so it gets to be a pretty long list of utility classes.
To programatically generate that output, you need another list and an inner loop:
$mediaQueries-px:
640,
768,
1024
;
$s: 20px;
$m: 50px;
$l: 60px;
$sizes: s $s, m $m, l $l;
#each $size in $sizes {
.m-t-#{nth($size, 1)} {
margin-top: nth($size, 2);
}
}
#each $mediaQuery in $mediaQueries-px {
#media screen and (min-width: ($mediaQuery / 16 * 1em)) { // modified for compilation purposes
#each $size in $sizes {
.m-t-#{nth($size, 1)}-#{$mediaQuery} {
margin-top: nth($size, 2);
}
}
}
}
Output:
.m-t-s {
margin-top: 20px;
}
.m-t-m {
margin-top: 50px;
}
.m-t-l {
margin-top: 60px;
}
#media screen and (min-width: 40em) {
.m-t-s-640 {
margin-top: 20px;
}
.m-t-m-640 {
margin-top: 50px;
}
.m-t-l-640 {
margin-top: 60px;
}
}
#media screen and (min-width: 48em) {
.m-t-s-768 {
margin-top: 20px;
}
.m-t-m-768 {
margin-top: 50px;
}
.m-t-l-768 {
margin-top: 60px;
}
}
#media screen and (min-width: 64em) {
.m-t-s-1024 {
margin-top: 20px;
}
.m-t-m-1024 {
margin-top: 50px;
}
.m-t-l-1024 {
margin-top: 60px;
}
}

Simple Gallery Slider

I'm trying to create a simple slider using divs and javascript. I set up a div with six images and an arrow that movies the containder holding the images 528px (the width of each image) every time it's clicked. When I reach the begining or end of the gallery, I want the respective arrow button to fade out so that the user won't keep pressing next/prev.
Any help is appreciated.
JAVASCRIPT
$("#btn-gallery-next").click(function(){
$("div#gallery li").not(this).removeClass('clicked');
$("div#gallery-slide").animate({left:"-=528px"});
if($("div#gallery-slide").position().left < -3168)
{
$("#btn-gallery-next").fadeOut();
}
else {
$("#btn-gallery-next").fadeIn();
}
});
$("#btn-gallery-prev").click(function(){
$("div#gallery li").not(this).removeClass('clicked');
$("div#gallery-slide").animate({left:"+=528px"});
if($("div#gallery-slide").position().left > 0)
{
$("#btn-gallery-prev").fadeOut();
}
else {
$("#btn-gallery-prev").fadeIn();
}
});
HTML
<div id="gallery-slide">
<img class="gallery-img" src="_/img/gallery/img1.jpg" alt="" />
<img class="gallery-img" src="_/img/gallery/img2.jpg" alt="" />
<img class="gallery-img" src="_/img/gallery/img3.jpg" alt="" />
<img class="gallery-img" src="_/img/gallery/img4.jpg" alt="" />
<img class="gallery-img" src="_/img/gallery/img5.jpg" alt="" />
<img class="gallery-img" src="_/img/gallery/img6.jpg" alt="" />
</div>
Try flex slider from woothemes, it have all ur needs.
Why not use a slider library like Owl Slider? It comes with lots of options and configurations. It is super simple to integrate into any project.
Example #1 www.midwestgathering.com/#galleries
Example #2 www.owlgraphic.com/owlcarousel/demos/images.html
Another option is jcarousel. The basic slider is shown with an example that makes the left next button inactive until the user slides to the right, then once the user gets to the end of the gallery the right next button becomes inactive:
JS
(function($) {
$(function() {
$('.jcarousel').jcarousel();
$('.jcarousel-control-prev')
.on('jcarouselcontrol:active', function() {
$(this).removeClass('inactive');
})
.on('jcarouselcontrol:inactive', function() {
$(this).addClass('inactive');
})
.jcarouselControl({
target: '-=1'
});
$('.jcarousel-control-next')
.on('jcarouselcontrol:active', function() {
$(this).removeClass('inactive');
})
.on('jcarouselcontrol:inactive', function() {
$(this).addClass('inactive');
})
.jcarouselControl({
target: '+=1'
});
$('.jcarousel-pagination')
.on('jcarouselpagination:active', 'a', function() {
$(this).addClass('active');
})
.on('jcarouselpagination:inactive', 'a', function() {
$(this).removeClass('active');
})
.jcarouselPagination();
});
})(jQuery);
CSS
.jcarousel-wrapper {
margin: 20px auto;
position: relative;
border: 10px solid #fff;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
-webkit-box-shadow: 0 0 2px #999;
-moz-box-shadow: 0 0 2px #999;
box-shadow: 0 0 2px #999;
}
.jcarousel-wrapper .photo-credits {
position: absolute;
right: 15px;
bottom: 0;
font-size: 13px;
color: #fff;
text-shadow: 0 0 1px rgba(0, 0, 0, 0.85);
opacity: .66;
}
.jcarousel-wrapper .photo-credits a {
color: #fff;
}
/** Carousel **/
.jcarousel {
position: relative;
overflow: hidden;
width: 600px;
height: 400px;
}
.jcarousel ul {
width: 20000em;
position: relative;
list-style: none;
margin: 0;
padding: 0;
}
.jcarousel li {
float: left;
}
/** Carousel Controls **/
.jcarousel-control-prev,
.jcarousel-control-next {
position: absolute;
top: 200px;
width: 30px;
height: 30px;
text-align: center;
background: #4E443C;
color: #fff;
text-decoration: none;
text-shadow: 0 0 1px #000;
font: 24px/27px Arial, sans-serif;
-webkit-border-radius: 30px;
-moz-border-radius: 30px;
border-radius: 30px;
-webkit-box-shadow: 0 0 2px #999;
-moz-box-shadow: 0 0 2px #999;
box-shadow: 0 0 2px #999;
}
.jcarousel-control-prev {
left: -50px;
}
.jcarousel-control-next {
right: -50px;
}
.jcarousel-control-prev:hover span,
.jcarousel-control-next:hover span {
display: block;
}
.jcarousel-control-prev.inactive,
.jcarousel-control-next.inactive {
opacity: .5;
cursor: default;
}
/** Carousel Pagination **/
.jcarousel-pagination {
position: absolute;
bottom: 0;
left: 15px;
}
.jcarousel-pagination a {
text-decoration: none;
display: inline-block;
font-size: 11px;
line-height: 14px;
min-width: 14px;
background: #fff;
color: #4E443C;
border-radius: 14px;
padding: 3px;
text-align: center;
margin-right: 2px;
opacity: .75;
}
.jcarousel-pagination a.active {
background: #4E443C;
color: #fff;
opacity: 1;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.75);
}
You can find documentation for jcarousel at www.sorgalla.com/jcarousel/docs/.
see demo - http://codepen.io/vsync/pen/waKju?editors=011
javascript
/**
* Slider - loops over images
* SEP 2014
* By - Yair Even-Or
*/
var Slider = function(elm, prev, next){
var that = this;
this.locked = false;
this.slider = elm;
this.children = this.slider.children;
this.itemWidth = this.children[0].clientWidth;
this.preloadImages();
next && next.addEventListener('click', function(){ that.move('next') });
prev && prev.addEventListener('click', function(){ that.move('prev') });
}
Slider.prototype = {
move : function(dir){
var that = this,
itemToMove;
if( this.locked ){
this.locked.removeAttribute('style');
this.slider.appendChild(this.locked);
clearTimeout(this.timer);
moveToEnd();
}
// do nothing if there are no items
if( this.children.length < 2 )
return false;
itemToMove = this.children[0];
this.locked = itemToMove;
if( dir == 'next' )
itemToMove.style.marginLeft = -this.itemWidth + 'px';
else{
itemToMove = this.children[this.children.length-1];
itemToMove.style.marginLeft = -this.itemWidth + 'px';
this.slider.insertBefore(itemToMove, this.children[0]);
setTimeout(function(){
itemToMove.removeAttribute('style');
},50);
this.preloadImages();
this.locked = false;
}
// move the child to the end of the items' list
if( dir == 'next' )
this.timer = setTimeout(moveToEnd, 420);
function moveToEnd(el){
itemToMove = el || itemToMove;
if( !itemToMove ) return;
itemToMove.removeAttribute('style');
that.slider.appendChild(itemToMove);
that.locked = false;
that.preloadImages();
}
},
preloadImages : function(){
this.lazyload(this.children[1].getElementsByTagName('img')[0] );
this.lazyload(this.children[this.children.length-1].getElementsByTagName('img')[0] );
},
// lazy load image
lazyload : function(img){
var lazy = img.getAttribute('data-src');
if( lazy ){
img.src = lazy;
img.removeAttribute('data-src');
}
}
}
// HOW TO USE /////////////////
var sliderElm = document.querySelector('.content'),
next = document.querySelector('.next'),
prev = document.querySelector('.prev'),
slider = new Slider(sliderElm, prev, next);
HTML (JADE syntax)
.slider
a.arrow.next
a.arrow.prev
ul.content
li
img(src='image1.jpg')
li
img(src='image2.jpg')
li
img(src='image3.jpg')
li
img(src='image4.jpg')