Creating a CSS masonry layout for an Ionic application

December 4, 2017, 9:00 am Categories:

Categories

Masonry layouts have become a popular choice of UI for websites in recent years - the most notable of which is Pinterest - and in today's tutorial I'll show you how such a layout can be achieved using pure CSS.

To accomplish this we'll use the following CSS 3 standards (as well as discussing their respective pros and cons):

We'll also provide the necessary style rules to enable our layout to be responsive across iOS, Android and Web based viewports as well as exploring a fallback option for legacy browsers that don't support CSS Flexbox or Multi-column Layouts.

The application that we'll be developing will display a range of web technologies across differently coloured tiles like so:

Browser screen capture displaying 3 to 4 column layout of a sample ionic project

When the application is resized within the browser, or viewed on a handheld device, the columns will automatically re-adjust to the size of the viewport:

Browser screen capture displaying 1 to 2 column layout of a sample ionic project

Now that we know what we'll be developing let's make a start by determining how well supported CSS Flexbox and multi-column layouts are.

As good developers we need to know that our choice of technologies for any project we work on will be able to be used by our target audience.

Browser support

We'll start with Flexbox support, courtesy of statistics provided by the very handy caniuse.com:

Browser Version
Internet Explorer 11 (Partial support)
Edge 15+
Firefox 52+
Chrome 49+
Safari 10.1+
Opera Mini 47+
iOS Safari 10.2+
Chrome for Android 62
UC Browser for Android 11.4 (Partial support)

Now let's do the same for CSS Multi-column layouts:

Browser Version
Internet Explorer 11
Edge 15+
Firefox 52+ (Partial support)
Chrome 49+ (Partial support)
60 (Full support)
Safari 10.1+
Opera Mini 47+
iOS Safari 10.2+
Chrome for Android 62
UC Browser for Android 11 (Partial support)

As you can see that's almost equivalent for both which is not a bad thing but we're clearly missing browser support for older versions.

To ensure compatibility with these legacy browsers we'll also look at adding a fallback option where Flexbox or CSS Multi-column Layout support is absent.

With the support factor now known let's start creating our application.

Setting up the project

Using the Ionic CLI create the following project named ionic-masonry using a blank template:

ionic start ionic-masonry blank

Once created open the ionic-masonry/src/app/app.scss file and add the following styles:

.red {
   background-color: rgba(220, 83, 81, 1);
}

.green {
   background-color: rgba(187, 225, 123, 1);
}

.blue {
   background-color: rgba(131, 150, 225, 1);
}

.purple {
   background-color: rgba(155, 121, 170, 1);
}

.yellow {
   background-color: rgba(235, 229, 39, 1);
}

.violet {
   background-color: rgba(213, 118, 211, 1);
}

.orange {
   background-color: rgba(213, 151, 46, 1);
}

.turquoise {
   background-color: rgba(105, 203, 217, 1);
}

These styles, as you've probably guessed, will be used to supply the necessary background colours for the tiles of our application.

With these in place let's start exploring how the masonry layout could be implemented beginning with Flexbox.

Method #1 - Flexbox

Within the application's global stylesheet - ionic-masonry/src/app/app.scss - add the following style rules (Sassdoc style comments have been added to explain key rules):

// http://ionicframework.com/docs/theming/


// App Global Sass
// --------------------------------------------------
// Put style rules here that you want to apply globally. These
// styles are for the entire app and not just one component.
// Additionally, this file can be also used as an entry point
// to import other Sass files to be included in the output CSS.
//
// Shared Sass variables, which can be used to adjust Ionic's
// default Sass variables, belong in "theme/variables.scss".
//
// To declare rules for a specific mode, create a child rule
// for the .md, .ios, or .wp mode classes. The mode class is
// automatically applied to the element in the app.


/// Check the browser supports flexbox wrap property
/// the containing style rules will only be triggered on browsers
/// that offer support for this feature
@supports(flex-wrap: wrap) {

   /// Parent container for the masonry tiles
   /// Uses flex columns with a set height to create
   /// a masonry interface effect
   .wrapper {
      width:98.5%;
      display: flex;
      flex-flow: column wrap; /* Force column elements to wrap */
      height: 100vw; /* Without the height property/value we get a single column / NO masonry! */

      // The rules for each tile within the layout
      section {  	  	 
         margin: 0 0.5em 0.5em 0;
         padding: 1em;

         img {
            display : block;
            margin: 0 auto 2em auto; 
            width: 90px;
         }
      }
   }

   
   /// Define height of parent container and width of each tile
   /// for screen sizes above 1201 pixels in width
   /// 4 column layout
   @media only screen and (min-width: 1201px) {
   	  
      .wrapper {
         height: 100vw;
         
         section {
   	    width: 25%;
   	 }
      }
   }


   /// Define height of parent container and width of each tile
   /// for screen sizes up to 1200 pixels in width
   /// 3 column layout
   @media only screen and (max-width: 1200px) {
      .wrapper { 
         height: 150vw;     	

         section {
   	    width: 33.3%;
   	 }
      }
   }


   /// Define height of parent container and width of each tile
   /// for screen sizes up to 900 pixels in width
   /// 3 column layout
   @media only screen and (max-width: 900px) {
      .wrapper {
         height: 220vw;
      }
   }


   /// Define height of parent container and width of each tile
   /// for screen sizes up to 700 pixels in width
   /// 2 column layout
   @media only screen and (max-width: 700px) {
      .wrapper {
         height: 450vw;   	  

         section {
   	    width: 50%;
   	 }
      }
   }


   /// Define height of parent container and width of each tile
   /// for screen sizes up to 600 pixels in width
   /// 1 column layout
   @media only screen and (max-width: 600px) {
      .wrapper {
         height: 950vw;

         section {
   	    width: 100%;
   	 }
      }
   }
}

This is a relatively simple set of style rules although we do need to pay attention to the following for the parent container:

display: flex;
flex-flow: column wrap;
height: 100vw;

In order for the browser to understand that we're using Flexbox we need to set the display: flex property and then define how we want elements within the parent container to be rendered using the flex-flow property.

Notice that we set this to a value of column wrap which determines how the content will reflow within the container.

Although this approach works there is one MAJOR drawback - we NEED to set a fixed height on the parent container.

Without this in place the elements within the parent container display as a single column.

This is clearly not ideal as we can't guarantee that our content will neatly render into columns across all viewports without being 'cut off' - particularly where some content is larger than others.....which brings us to our second approach.

Method #2 - CSS Multi-column Layouts

With the obvious limitations of CSS Flexbox for implementing masonry type layouts let's now turn our attention to using CSS Multi-column Layouts instead.

Replace the existing flexbox style rules within the ionic-masonry/src/app/app.scss stylesheet with the following:

// http://ionicframework.com/docs/theming/


// App Global Sass
// --------------------------------------------------
// Put style rules here that you want to apply globally. These
// styles are for the entire app and not just one component.
// Additionally, this file can be also used as an entry point
// to import other Sass files to be included in the output CSS.
//
// Shared Sass variables, which can be used to adjust Ionic's
// default Sass variables, belong in "theme/variables.scss".
//
// To declare rules for a specific mode, create a child rule
// for the .md, .ios, or .wp mode classes. The mode class is
// automatically applied to the element in the app.


/// Check the browser supports column-count property
/// the containing style rules will only be triggered on browsers
/// that offer support for this feature
@supports (column-count: 5) {
   .wrapper {
      -moz-column-count: 5;
      -webkit-column-count: 5;
      column-count: 5;
      -moz-column-gap: 0.5em;
      -webkit-column-gap: 0.5em;
      column-gap: 0.5em;


      section {
   	    width: 100%;
   	    margin: 0 0.5em 0.5em 0;
   	    padding: 1em;
   	    display: inline-block;
        color: rgba(68, 68, 68, 1);
        text-align: center;
        -moz-box-sizing: border-box;
        -webkit-box-sizing: border-box;
        box-sizing: border-box;

        img {
           display : block;
           margin: 0 auto 2em auto; 
           width: 90px;
        }
      }
   }


   /// Define 4 column count for screen sizes up to 1200 pixels in width
   @media only screen and (max-width: 1200px) {
      .wrapper {
         -moz-column-count: 4;
         -webkit-column-count: 4;
         column-count: 4;
      }
   }


   /// Define 3 column count for screen sizes up to 900 pixels in width
   @media only screen and (max-width: 900px) {
      .wrapper {
         -moz-column-count: 3;
         -webkit-column-count: 3;      
         column-count: 3;
      }
   }


   /// Define 2 column count for screen sizes up to 800 pixels in width
   @media only screen and (max-width: 800px) {
      .wrapper {
   	     -moz-column-count: 2;
         -webkit-column-count: 2;      
         column-count: 2;
      }
   }


   /// Define 1 column count for screen sizes up to 600 pixels in width
   @media only screen and (max-width: 600px) {
      .wrapper {
         -moz-column-count: 1;
         -webkit-column-count: 1;      
         column-count: 1;
         -moz-column-gap: 0;
         -webkit-column-gap: 0;
         column-gap: 0;
      }
   }   
}

Once again we use the @supports media query to determine if the browser 'recognises' the CSS Multi-column Layout standard.

We then assign the following rules (with additional vendor specific prefixes in place) to the parent container to define the initial number of columns for the interface and the gap between each of those columns:

column-count: 5;
column-gap: 0.5em;

We then use media queries to define breakpoints where the column count will be redefined according to a specific screen width.

The great thing about using CSS Multi-column Layouts is that the content will reflow correctly regardless of the height viewport dimensions.

We still need to add media queries though to redefine the column count at selected breakpoints otherwise the content will be 'squeezed' into the original number of columns fitting to that screen width (which is going to look odd on a smaller viewport).

Still, this approach to rendering a CSS only masonry layout is the preferred one.

Legacy support

Where app development is concerned the elephant in the room will always be support for certain technologies and features.

As browser support for CSS Flexbox and Multi-column Layouts is, relatively speaking, not widely supported we need to ensure that we have a fallback option in place for older platforms.

To implement this return to the same global stylesheet - ionic-masonry/src/app/app.scss - and add the following style rules, towards the bottom of the file:

@supports not (column-count: 5) {
   .wrapper {
      width: 100%;

      section {
        display: inline-block;
        vertical-align: top;
      }
   }


   @media only screen and (max-width: 1200px) {
      .wrapper {
         section {
   	       width: 19.25%;
         }
      }
   }


   @media only screen and (max-width: 1000px) {
      .wrapper {
         section {
   	       width: 25%;
         }
      }
   }


   @media only screen and (max-width: 800px) {
      .wrapper {
         section {
   	       width: 33%;
         }
      }
   }


   @media only screen and (max-width: 600px) {
      .wrapper {
         section {
   	       width: 50%;
         }
      }
   }


   @media only screen and (max-width: 450px) {
      .wrapper {
         section {
   	       width: 100%;
         }
      }
   }
}

Not quite as elegant as using CSS Multi-column Layouts to implement a masonry layout but it will, at the very least, display our content in a grid like format (even if, unfortunately, it isn't a masonry style interface) where those legacy browsers are concerned.

Templating

With our style rules in place all that remains is to add the necessary HTML to the ionic-masonry/src/pages/home/home.html project template (the featured logos are sourced from Google Images and saved within the project's assets/imgs directory with the paragraph texts generated and copied from lipsum.com):

<ion-header>
  <ion-navbar>
    <ion-title>
      Ionic Dev Stack
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content padding>
  

   <div class="wrapper">
      <section class="blue">
      	 <img src="assets/imgs/angular-logo.png">
      	 <h2>Angular</h2>
      	 <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
            Pellentesque nisl mi, mollis aliquet auctor eu, sodales et quam. 
            Vivamus fringilla ex at erat interdum euismod. 
      	    Vivamus posuere, leo lobortis fermentum tempor, tellus justo laoreet 
            lectus, ac feugiat nisi augue ac velit. Integer eget rhoncus leo.</p>

      	 <p>Integer a porttitor turpis. Pellentesque habitant morbi tristique senectus 
            et netus et malesuada fames ac turpis egestas. Orci varius natoque 
            penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nunc et 
            diam quam. In consectetur ante tellus, vitae auctor tortor consequat et. 
            Pellentesque laoreet lacus nec felis rhoncus molestie eget ut nulla.</p>
      </section>

      <section class="red">
      	 <img src="assets/imgs/typescript-logo.png">
      	 <h2>TypeScript</h2>
      	 <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque nisl 
            mi, mollis aliquet auctor eu, sodales et quam. Vivamus fringilla ex at erat 
            interdum euismod. 
      	    Vivamus posuere, leo lobortis fermentum tempor, tellus justo laoreet lectus, 
            ac feugiat nisi augue ac velit. Integer eget rhoncus leo.</p>
      </section>

      <section class="yellow">
      	 <img src="assets/imgs/sass-logo.png">
      	 <h2>Sass</h2>
      	 <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque nisl 
            mi, mollis aliquet auctor eu, sodales et quam.</p>
      </section>

      <section class="green">
      	 <img src="assets/imgs/html5-logo.png">
      	 <h2>HTML5</h2>
      	 <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque nisl mi, mollis aliquet auctor eu, sodales et quam. Vivamus fringilla ex at erat interdum euismod.</p>
      </section>

      <section class="orange">
      	 <img src="assets/imgs/web-components-logo.png">
      	 <h2>Web Components</h2>

      	 <p>Integer a porttitor turpis. Pellentesque habitant morbi tristique senectus 
            et netus et malesuada fames ac turpis egestas. Orci varius natoque penatibus 
            et magnis dis parturient montes, nascetur ridiculus mus. Nunc et diam quam.  
            In consectetur ante tellus, vitae auctor tortor consequat et. Pellentesque 
            laoreet lacus nec felis rhoncus molestie eget ut nulla.</p>
      </section>

      <section class="purple">      	
      	 <img src="assets/imgs/ionic-native-logo.png">
      	 <h2>Ionic Native</h2>

      	 <p>Integer a porttitor turpis. Pellentesque habitant morbi tristique senectus 
            et netus et malesuada fames ac turpis egestas. Orci varius natoque penatibus 
            et magnis dis parturient montes, nascetur ridiculus mus. Nunc et diam quam.
         </p>
      </section>

      <section class="violet">
      	 <img src="assets/imgs/apache-cordova-logo.png">
      	 <h2>Apache Cordova</h2>

      	 <p>Integer a porttitor turpis. Pellentesque habitant morbi tristique senectus 
            et netus et malesuada fames ac turpis egestas. Orci varius natoque 
            penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nunc et 
            diam quam. In consectetur ante tellus, vitae auctor tortor consequat et.  
            Pellentesque laoreet lacus nec felis rhoncus molestie eget ut nulla.</p>
      </section>

      <section class="blue">
      	 <img src="assets/imgs/mongodb-logo.png">
      	 <h2>MongoDB</h2>
      </section>

      <section class="red">
      	 <img src="assets/imgs/firebase-logo.png">
      	 <h2>Firebase</h2>

      	 <p>Pellentesque laoreet lacus nec felis rhoncus 
      	molestie eget ut nulla.</p>
      </section>

      <section class="yellow">
      	 <img src="assets/imgs/php-logo.png">
      	 <h2>PHP</h2>

      	 <p>Nunc et diam quam. In consectetur ante tellus, vitae auctor tortor 
            consequat et. Pellentesque laoreet lacus nec felis rhoncus molestie eget 
            ut nulla.</p>
      </section>

      <section class="green">
      	 <img src="assets/imgs/mysql-logo.png">
      	 <h2>MySQL</h2>
      </section>

      <section class="orange">
      	 <img src="assets/imgs/css3-logo.png">
      	 <h2>CSS3</h2>
      </section>

      <section class="purple">
      	 <img src="assets/imgs/apple-logo.png">
      	 <h2>iOS</h2>
      </section>

      <section class="violet">
      	 <img src="assets/imgs/android-logo.png">
      	 <h2>Android</h2>
      </section>

      <section class="blue">
      	 <img src="assets/imgs/windows-logo.png">
      	 <h2>Windows</h2>
      </section>

      <section class="red">
      	 <img src="assets/imgs/chrome-logo.png">
      	 <h2>Google Chrome</h2>

      	 <p>Integer a porttitor turpis. Pellentesque habitant morbi tristique senectus 
            et netus et malesuada fames ac turpis egestas. Orci varius natoque 
            penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nunc et 
            diam quam.</p>
      </section>

      <section class="yellow">
      	 <img src="assets/imgs/firefox-logo.png">
      	 <h2>Firefox</h2>
      </section>

      <section class="green">
      	 <img src="assets/imgs/safari-logo.png">
      	 <h2>Safari</h2>
      </section>

      <section class="orange">
      	 <img src="assets/imgs/opera-logo.png">
      	 <h2>Opera</h2>
      </section>

      <section class="purple">
      	 <img src="assets/imgs/ie-logo.png">
      	 <h2>Internet Explorer</h2>
      </section>
   </div>


</ion-content>

Notice that each <section> element (which form our individual 'masonry' tiles) within the parent container is assigned a specific CSS class to provide the background colour for that 'tile'.

With this markup added to the HomePage component template let's now run this project in our desktop browser using the following CLI command:

ionic serve

Depending on which CSS approach you've used - Flexbox or Multi-column Layouts - you'll see a masonry layout consisting of 4 or 5 columns such as the following:

Browser screen capture displaying 3 to 4 column layout of a sample ionic project

This can then be resized to demonstrate how the masonry layout responds and adapts to a smaller viewport.

In summary

Hopefully you've found the above tutorial useful and it's given you a taste for how a masonry layout can be applied to an Ionic project.

If you've enjoyed what you've read and/or found this helpful please feel free to share your comments, thoughts and suggestions in the comments area below.

I explore styling and theming for the Ionic framework within my e-book featured below and if you're interested in learning more about further articles and e-books please sign up to my FREE mailing list.

Tags

Categories

Post a comment

All comments are welcome and the rules are simple - be nice and do NOT engage in trolling, spamming, abusiveness or illegal behaviour. If you fail to observe these rules you will be permanently banned from being able to comment.

Top