Request More Information

We are hiring!

Xtivia, Inc.
2013 North America Liferay Partner of the Year

Join our top-notch team of Enterprise Java and Liferay Portal professionals in our Austin, TX office or work remotely! Please check out our current Dice listings.

Recent Bloggers

Luke A Smith
Posts: 17
Stars: 0
Date: 10/20/14
Sonu Verghese
Posts: 7
Stars: 0
Date: 10/16/14
Chris Shaw
Posts: 8
Stars: 0
Date: 10/15/14
Sushil Shirodkar
Posts: 1
Stars: 0
Date: 8/28/14
Vamshidhar Srikantapuram
Posts: 11
Stars: 0
Date: 8/8/14
Barrie Shaw
Posts: 6
Stars: 0
Date: 8/4/14
Omar Yimaier
Posts: 4
Stars: 0
Date: 8/1/14
Bob Dietrich
Posts: 8
Stars: 0
Date: 8/1/14
Matt Wolinski
Posts: 2
Stars: 0
Date: 7/30/14
Richard Ngo
Posts: 1
Stars: 0
Date: 6/19/14
« Back

Building Responsive Layouts in Liferay with Twitter Bootstrap

Twitter Bootstrap is a popular, easy-to-use front-end framework that not only allows for rapid prototyping, but also comes with a robust set of features that let you easily implement a responsive design.

If you're building a site from scratch, all you have to do is include the Bootstrap CSS and JavaScript, and you're off to the races! When you're working with Liferay, though, it's a bit more complicated than that. Of course, it is possible to create a new theme, drop the big bundle of Bootstrap files in there, and start running with it; but there are a lot of Bootstrap UI features that conflict messily with default Liferay styles, especially the Dockbar and Control Panel. We'll handle workarounds for some of these in a future post; but for today, we'll concentrate on the most powerful and useful feature of Bootstrap, responsive layouts.

For the sake of simplicity, we're going to build a two-column layout with a main content column and a sidebar; but the possibilities are endless. Before we get stuck in, though, you'll want to familiarise yourself with how the Bootstrap grid system works; and if you haven't seen this excellent tutorial on how to build custom layout templates, it's worth a quick read. I'll draw your particular attention to the generated code at the bottom of this page, which will give you a better idea about what we're going to be building from.

The Layout Template

Creating bootstrap-2-column-sample-layouttpl gives us a blank slate. Open up bootstrap_2_columns_sample.tpl, and let's get to work.

We'll start by duplicating the usual layout template scaffolding:

<div class="bootstrap-2-column-sample-layouttpl" id="main-content" 
    role="main">

<div class="portlet-layout">
    <div class="portlet-column portlet-column-first">
        $processor.processColumn("column-1", "portlet-column-content
            portlet-column-content-first")
    </div>
    <div class="portlet-column portlet-column-last">
        $processor.processColumn("column-2", "portlet-column-content
            portlet-column-content-last")
    </div>
</div><!--portlet-layout-->

</div>

The usual process from here is to let AUI handle the column widths by assigning CSS classes to them; say, for a 70/30 two-column split, assigning .aui-w70 to the first column and .aui-w30 to the second column. This is where we're going to let Bootstrap do the heavy lifting.

We're going to use Bootstrap's fluid grid system to ensure that we have the best layout displayed regardless of how wide the viewport is. To make this happen, we're going to use .container-fluid on our container [1] and .row-fluid on our row.

<div class="bootstrap-2-column-sample-layouttpl container-fluid" 
    id="main-content" role="main">

<div class="portlet-layout row-fluid">
    <div class="portlet-column portlet-column-first">
        $processor.processColumn("column-1", "portlet-column-content 
            portlet-column-content-first")
    </div>
    <div class="portlet-column portlet-column-last">
        $processor.processColumn("column-2", "portlet-column-content 
            portlet-column-content-last")
    </div>
</div><!--portlet-layout-->

</div>

Now to specify the widths of our columns, we're going to use Bootstrap's classes. Bootstrap layout is based on a twelve-column grid; so if we want our main content to take up two-thirds of the page and our sidebar to take up one-third of the page, our main content will be eight columns wide and our sidebar will be four columns wide, like so:

Bootstrap grid system

Translating that into Bootstrap, that gives us .span8 and .span4, making the markup look like this:

<div class="bootstrap-2-column-sample-layouttpl container-fluid" 
    id="main-content" role="main">

<div class="portlet-layout row-fluid">
    <div class="portlet-column portlet-column-first span8">
        $processor.processColumn("column-1", "portlet-column-content 
            portlet-column-content-first")
    </div>
    <div class="portlet-column portlet-column-last span4">
        $processor.processColumn("column-2", "portlet-column-content 
            portlet-column-content-last")
    </div>
</div><!--portlet-layout-->

</div>

The Theme

Of course, adding all these classes to the layout template doesn't do us any good unless we have something in the theme to support them. Remember how I said earlier that installing the entire Bootstrap bundle can cause ugly conflicts with default Liferay styles? Well, on the Bootstrap download page, there's an option to just break out individual components. I've gone ahead and isolated just the Bootstrap layout elements, which you can download (along with the code for the sample layout template) here.

Instead of cutting and pasting all that goodness into your custom.css, you can just go ahead and call it at the top before all your custom rules, like this:

@import url(bootstrap.css);

/* All your custom CSS rules go here */

Thanks to Nate Cavanaugh for pointing out this more future-proof way of doing it! He explains further in the comments.

One more important change: even though we have a fluid grid, it will be helpful to set a max-width on the container-fluid just in case. [2]

The Finished Product

With the appropriate classes added to the layout template and the supporting CSS in the theme, you're good to go! Deploy your theme and add some content. You'll notice that it's two columns at desktop width, but if you resize your browser, you'll see the layout collapse to one column at 768px wide. If you don't feel like dragging your broswer window around, here's a handy bookmarklet you can use to view how your layout changes with viewport size.

Desktop view (1200px wide):

Liferay Bootstrap desktop view

Mobile view (468px wide):

Liferay Bootstrap mobile view

This is a very basic example that still needs some minor adjustments, but it shows how we can use popular and well-tested frameworks to extend Liferay's capabilities. Using the basic .row-fluid and column classes, we can create complex layouts that work on all devices. Bootstrap offers even more features that make it trivial to target screen widths, but that's a topic for another blog post.

Further Reading

Notes

  1. In practice, you'll probably want to specify .container-fluid somewhere in portal-normal.vm, but we're putting it in the layout template here just to keep everything self-contained. Return

  2. For the purposes of this demo, I made a couple minor changes to #wrapper in the Liferay Classic theme to make it play better with the layout template; but if you're building a new theme and layout template from scratch, you'll be better served to use .container-fluid in the theme, as I mentioned in Note 1. Return

Comments
Trackback URL:

Anonymous User
Hi guys,
This is really awesome emoticon Thanks for posting this blog post, I think it's really useful for theme devs.

I just wanted to offer one small improvement:
While we allow people to edit main.css in the theme, we don't recommend it, because we may change the files that go in there (and if it's overwritten, it makes maintaining your theme for next versions much more difficult).

Instead, what we recommend is to add your import statement to the top of your custom.css.

So in custom.css you would have at the top:
@import url(bootstrap.css);
/* ... custom rules ... */

This gives you the same result, but gives you less worries when you go to upgrade.

I hope that helps, and keep up the good work emoticon
Posted on 1/10/13 11:30 AM.
Anonymous User
Ahh it might have helped if I had included my name.
This is Nate Cavanaugh over at Liferay emoticon
Posted on 1/10/13 11:33 AM in reply to Anonymous User.
Great to hear from you! You were the first person I thought of when I read your initial comment, but thanks for clarifying. emoticon

I appreciate the tip! Anything to reduce the number of worries and amount of chaos in everybody's life, the better. I've updated the post and the GitHub project accordingly. Thanks again!
Posted on 1/10/13 12:31 PM in reply to Anonymous User.
Balaji Ramakrishnan
Hi,
It's a wonderful blog post. But if i used this template and theme, in chrome browser the layout is changing whenever we reduce the size of the browser whereas in IE the behaviour is different, in the normal desktop mode itself all the portlets are coming like mobile version, one portlet down by another portlet. Is there any particular styles we need to apply IE for specific.
Posted on 5/28/13 8:05 PM in reply to Nick Piesco.
Hari L
Thanks for nice blog Nick.

" on the Bootstrap download page, there's an option to just break out individual components. I've gone ahead and isolated just the Bootstrap layout elements, which you can download"
Could you please share what components you selected to generate ? This will be really useful info to check with latest release of bootstrap and also to narrow down on conflict styles with liferay.

Thanking you,
Posted on 6/17/13 10:54 AM.
Vaibhav Mittal
I tried above code (with bootstrap 2.3) it did work for me.

When i tried with bootstrap 3. Its did not work. If anyone can check the issue. Below is my code:
<div class="bootstrap-2-column-sample-layouttpl container"
id="main-content" role="main">

<div class="portlet-layout row">
<div class="portlet-column portlet-column-first col-xs-12 col-sm-6 col-md-8 col-lg-8">
$processor.processColumn("column-1", "portlet-column-content portlet-column-content-first")
</div>
<div class="portlet-column portlet-column-last col-xs-6 col-sm-6 col-md-4 col-lg-4">
$processor.processColumn("column-2", "portlet-column-content portlet-column-content-last")
</div>
</div>
Posted on 9/30/13 3:21 AM.
Sorry it's taken me a bit to get back to you all! Apparently I managed to accidentally turn off reply notifications. I'm glad you've all found this so useful and hope you've gotten your issues solved, but just in case....

Balaji, you're not specific about which version of IE you're using; but IE8 doesn't support media queries, which is where the responsive magic happens. Newer versions of IE should work nicely for you.

Hari, I just used the grid system. Feel free to add in other stuff if you'd like, but the grid is all you need.

Vaibhav, the layout class names changed completely between Bootstrap 2 and 3, so you'll have to download the Bootstrap 3 grid to make that work.

It's also worth noting that the nice folks at Liferay rolled the Bootstrap responsive grid (and a bunch of other goodies) into AUI 2.0; so if you're using Liferay 6.2, all of this work is done for you.
Posted on 3/7/14 3:41 PM.

Request More Information