Reduce redundancy with Less CSS

I am struggling with the following Less code:

It seems like there might be a way to abstract this out further using some sort of mixin, but I can't seem to figure it out. I want to input a variable such as @xs or @xs-gutter and have the function automatically pad out the code.

Any suggestions?

.padding
{
    &.bottom {
        padding-bottom: @xs-gutter;
    }
    &.left {
        padding-left: @xs-gutter;
    }
    &.right {
        padding-right: @xs-gutter;
    }
    &.top {
        padding-top: @xs-gutter;
    }

    @media @sm-screen {
        &.bottom {
            padding-bottom: @sm-gutter;
        }
        &.left {
            padding-left: @sm-gutter;
        }
        &.right {
            padding-right: @sm-gutter;
        }
        &.top {
            padding-top: @sm-gutter;
        }
    }
    @media @md-screen {
        &.bottom {
            padding-bottom: @md-gutter;
        }
        &.left {
            padding-left: @md-gutter;
        }
        &.right {
            padding-right: @md-gutter;
        }
        &.top {
            padding-top: @md-gutter;
        }
    }
    @media @lg-screen {
        &.bottom {
            padding-bottom: @lg-gutter;
        }
        &.left {
            padding-left: @lg-gutter;
        }
        &.right {
            padding-right: @lg-gutter;
        }
        &.top {
            padding-top: @lg-gutter;
        }
    }
}

Answer №1

To streamline the code and minimize repetition, one can utilize loops and array lists. The following snippet demonstrates how to achieve this reduction. Detailed explanations are provided within inline comments.

Important: I've separated the padding generation mixin as a standalone function that accepts sides as arguments. This makes it easier to reuse the mixin for generating padding on multiple sides without redundant media queries.

@gutters: 4px, 6px, 8px, 10px; // sizes of gutters for each screen size
@media-sizes: xs, sm, lg, md; // available screen sizes
@media-conditions: ~"(min-width: 100px)", ~"(min-width: 150px)", ~"(min-width: 200px)", ~"(min-width: 250px)"; // media query conditions for each screen size

.media-generator(){
    .loop-sizes(length(@media-sizes)); // iterate through all screen sizes
    .loop-sizes(@screenIndex) when (@screenIndex > 0) {
        & when (extract(@media-sizes, @screenIndex) = xs){ // defaulting to xs
            .padding-per-side(extract(@gutters, 1); left; right; bottom; top);
        }
        & when not (extract(@media-sizes, @screenIndex) = xs){ // handle non-xs screen sizes
            @condition: extract(@media-conditions, @screenIndex); // get corresponding media condition
            @media @condition{
                .padding-per-side(extract(@gutters, @screenIndex); left; right; bottom; top); // call mixin for padding generation
            }
        }
        .loop-sizes(@screenIndex - 1);
    }
}
.padding-per-side(@gutter; @sides...){
    .loop-sides(length(@sides));
    .loop-sides(@index) when (@index > 0){
        @side: extract(@sides, @index);
        &.@{side}{
            padding-@{side}: @gutter;
        }
        .loop-sides(@index - 1);
    }
}
.padding{
    .media-generator(); // generate padding for all sides and screens as mentioned above
}

#demo{ // extra :)
    .padding-per-side(10px; left;right); // generates specific padding for left and right
}

The enhanced version below enhances the previous code by allowing the selective generation of padding with media queries only for certain sides. Here, you have the flexibility to generate padding for specific sides along with their associated media queries.

@gutters: 4px, 6px, 8px, 10px;
@media-sizes: xs, sm, lg, md;
@media-conditions: ~"(min-width: 100px)", ~"(min-width: 150px)", ~"(min-width: 200px)", ~"(min-width: 250px)";

.media-generator(@sides...){
    & when (length(@sides) = 0){
        .loop-sizes(length(@media-sizes));
        .loop-sizes(@screenIndex) when (@screenIndex > 0) {
            & when (extract(@media-sizes, @screenIndex) = xs){
                .padding-per-side(extract(@gutters, 1); left; right; bottom; top);
            }
            & when not (extract(@media-sizes, @screenIndex) = xs){
                @condition: extract(@media-conditions, @screenIndex);
                @media @condition{
                    .padding-per-side(extract(@gutters, @screenIndex); left; right; bottom; top);
                }
            }
            .loop-sizes(@screenIndex - 1);
        }
    }
    & when not (length(@sides) = 0){
        .loop-sizes(length(@media-sizes));
        .loop-sizes(@screenIndex) when (@screenIndex > 0) {
            & when (extract(@media-sizes, @screenIndex) = xs){
                .padding-per-side(extract(@gutters, 1); @sides);
            }
            & when not (extract(@media-sizes, @screenIndex) = xs){
                @condition: extract(@media-conditions, @screenIndex);
                @media @condition{
                    .padding-per-side(extract(@gutters, @screenIndex); @sides);
                }
            }
            .loop-sizes(@screenIndex - 1);
        }
    }   
}
.padding-per-side(@gutter; @sides...){
    .loop-sides(length(@sides));
    .loop-sides(@index) when (@index > 0){
        @side: extract(@sides, @index);
        &.@{side}{
            padding-@{side}: @gutter;
        }
        .loop-sides(@index - 1);
    }
}
.padding{
    .media-generator(left; right); // specify sides if needed or leave blank
}

Answer №2

My approach involves exploring three potential solutions, starting with the simplest and progressively refining it until reaching the third option. The easiest solution prioritizes readability, while the most challenging one entails using a double nested LOOP.

In this context, I identify the "common" code shared by all three solutions (I choose to keep separate definitions for possible sizes to enhance clarity):

@sm-screen:~"(min-width: 320px)";
@md-screen:~"(min-width: 720px)";
@lg-screen:~"(min-width: 1200px)";

@xs-gutter:20px;
@sm-gutter:30px;
@md-gutter:40px;
@lg-gutter:50px;

@property:padding;
//@property:margin;

It is worth noting that @property can be either "margin" or "padding" by simply switching the comment.

To these variable definitions, I propose the following three scenarios:


SOLUTION 1

This represents the simplest approach. However, it leads to redundant code as each rule generates its own media query:

.side(top, right, bottom, left);

.side(@possible-values...) 
{
  .generate-property-loop(1, @possible-values);
}

.generate-property-loop(@var; @possible-values) when (@var <= length(@possible-values)) 
{
  //Extract values at position @var from list @possible-values
  @direction: extract(@possible-values, @var);

  .@{property}.@{direction} 
  {
        @{property}-@{direction}: @xs-gutter;

        @media @sm-screen 
        {
           @{property}-@{direction}: @sm-gutter;
        }

        @media @md-screen 
        {
           @{property}-@{direction}: @md-gutter;
        }

        @media @lg-screen 
        {
           @{property}-@{direction}: @lg-gutter;
        }

  }

  .generate-property-loop((@var + 1), @possible-values);
}

SOLUTION 2

An alternative approach involves moving the media queries outside the LOOP but still necessitates explicit media query definition:

.side(top, right, bottom, left);

.side(@possible-values...) 
{
  .generate-property-loop(1, @possible-values, @xs-gutter);

  @media @sm-screen 
  {
    .generate-property-loop(1, @possible-values, @sm-gutter);
  }

  @media @md-screen 
  {
    .generate-property-loop(1, @possible-values, @md-gutter);
  }

  @media @lg-screen 
  {
    .generate-property-loop(1, @possible-values, @lg-gutter);
  }

}

.generate-property-loop(@var, @possible-values, @gutter) when (@var <= length(@possible-values)) 
{
  //Extract values at position @var from list @possible-values
  @direction: extract(@possible-values, @var);

  .@{property}
  {
     &.@{direction} 
     {
        @{property}-@{direction}: @gutter;
     } 
  }

  .generate-property-loop((@var + 1), @possible-values, @gutter);
}

SOLUTION 3

By implementing a double nested loop, it is possible to achieve ultimate flexibility by defining both directions and screen sizes as parameters. However, this may slightly compromise readability:

.side(top, right, bottom, left);

.side(@possible-values...) 
{
  .generate-property-loop(1, @possible-values, @xs-gutter);

  .mediaquery-loop(sm,md,lg);
}

.mediaquery-loop(@possible-screens...)
{
  .generate-mediaquery-loop(1, @possible-screens);
}

.generate-property-loop(@var, @possible-values, @gutter) when (@var <= length(@possible-values)) 
{
  @direction: extract(@possible-values, @var);

  .@{property}
  {
     &.@{direction} 
     {
        @{property}-@{direction}: @gutter;
     } 
  }

  .generate-property-loop((@var + 1), @possible-values, @gutter);
}

.generate-mediaquery-loop(@var, @possible-sizes) when (@var <= length(@possible-screens)) 
{
  @sizes: extract(@possible-sizes, @var);

  @screen-size: ~"@{sizes}-screen";
  @gutter-size: ~"@{sizes}-gutter";

  @media @@screen-size
  {
    .generate-property-loop(1, @possible-values, @@gutter-size);
  }

  .generate-mediaquery-loop((@var + 1), @possible-screens);
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the process for changing an image on a webpage based on a dropdown list selection?

When I came across the site , I was impressed by their feature that allows users to select their currency. By changing the currency, the symbol beside the amount entered also changes accordingly. I am interested in incorporating a similar feature on my we ...

When there are more than two divs on the page, they tend to overflow beyond the boundaries of

I am managing a platform where users have the ability to share text-based posts, and other users can engage with those posts through comments. An issue I have encountered is that once more than two comments are posted on a parent post, they start to overfl ...

Unveiling concealed content with JQuery

Here is a link to my pen: http://codepen.io/anon/pen/IszKj I am looking to achieve the functionality where clicking on either "any status" or "any date" will reveal a hidden div containing a list of options. My main query is about the best approach to ta ...

"TailwindCSS opts for the inclusion of prefexied utilities instead of relying

Trying to set the height of the div to h-2, with a class that includes height animation on medium devices. The issue is that even though h-2 should be used on mobile, tailwindcss still uses h-20 instead. Any thoughts on why this may be happening? Here i ...

The CSS variables set within the :root section of styles.scss are not recognized as valid

Trying to implement global colors using CSS variables in my Angular 11 app. The styles.scss file contains: :root{ --primary : #0b68e8; --secondary:#ABCFFF; } .test-class{ background: var(--primary); } However, when applying this class in one ...

Parallax scrolling in all directions

Is there a resource available for learning how to program a website similar to the one at ? I am familiar with parallax but can't seem to find any examples that resemble what they have done on that site. ...

The dimensions of text that wraps itself around multiple lines are distinct from those of text that remains on a single line

Hello everyone, I need some assistance with setting up my WordPress blog. I have integrated both Facebook and native WordPress comments on my website: . My aim is to make the formatting of the WordPress comments resemble that of Facebook comments as closel ...

The width of the TD element does not affect the grid

<div id="unique-example" class="k-unique-content"> <table id="unique-grid" style="float: left; position: relative"> <thead> <tr> <th width ="410px"data-field="UniqueFileName">Unique File Name ...

Problems with pagination in jQuery DataTables

Utilizing the most recent version of jQuery's DataTables. Facing an issue where the pagination at the bottom of my data table is overlapping with my bottom div container. Unable to figure out the cause and solution for this problem. Can anyone provid ...

What is the best way to align these divs horizontally?

I am facing an issue with my web page layout. I have a list of topics that I need to display, with four topics in each row. However, when I try to render them, they end up aligning vertically instead of horizontally like this: This is the CSS code snippet ...

Top method for customizing w2ui grid appearance

While utilizing the w2ui grid to showcase data in a table, I've found it to be quite effective. However, I'm not entirely happy with the appearance of the table itself. Are there any methods available for styling the table that don't involve ...

Unexpected behavior: Bootstrap 4 tooltip fails to appear on dynamically-generated elements

The tooltips appearing are not the expected Bootstrap 4 style tooltips. The tooltips currently displayed: https://i.sstatic.net/60Ubm.png compared to the tooltips that should be shown: https://i.sstatic.net/koayu.png I have enabled the tooltips in the ...

Generate a Line that is positioned absolutely and does not line up with the center of the

Whenever I apply 'text-center', the text does not align horizontally and instead lines up vertically. You can see an example in this js fiddle: I am aiming for something like this: https://i.sstatic.net/oueEr.png https://jsfiddle.net/dungvd/ey4f ...

developing both vertical and horizontal 'cross out'

I'm attempting to add a 'spacer' between buttons on my website using the word "or" with a vertical line centered above and below it. I've tried HTML <div class="footer-btn-wrap"> <div class="decor"><a href="... < ...

What is the best way to add animation to my `<div>` elements when my website is first loaded?

I am looking for a way to enhance the appearance of my <div> contents when my website loads. They should gradually appear one after the other as the website loads. Additionally, the background image should load slowly due to being filtered by the wea ...

The issue with updating the menu class in Internet Explorer 8 is not being resolved

Here is a code snippet using JavaScript: var x = jQuery(window).innerHeight(); jQuery(document).scroll(function() { if (jQuery(this).scrollTop() >= x) { jQuery('#nav').removeClass('nav').addClass('topfix_nav'); ...

A pair of vertical sliders

I am currently troubleshooting a website located at . It seems that there are two vertical scroll bars appearing on both the home page and product page, such as here: Interestingly, these extra scroll bars do not appear on the category page located here: ...

Customize Color of Specific Options in MaterializeCSS

I am currently utilizing a select dropdown feature from the MaterializeCSS library. My goal is to implement a specific class that will change the text color of certain options within the dropdown. Is there a method available for accomplishing this? Upon r ...

Is it possible to serve CSS using relative paths that go beyond the URL root?

I've encountered a file path issue with my HTML and CSS files. My HTML file is located in: src/test/html/index.html And the corresponding CSS file is in: src/test/css/index.css In the HTML file, the CSS is linked using: <link rel="stylesheet" ...

Tips on wrapping a div snugly around an image while also adjusting the image size to fit within the content area if it is larger than the content area

Currently working on developing an image viewer that has specific requirements to meet. Here they are: The content area is defined as a grid-area. If the image is larger than the content area, it must be contained without stretching. If the image is smal ...