Attention

This document was written for an unsupported version of Plone, Plone 2.1.x, and was last updated 1239 days ago.

For more information, see the version support policy.

To learn how to upgrade to the current version of Plone, read the upgrade manual.

Making the template

by Mikko Ohtamaa last modified Dec 30, 2008 03:05 PM
We draw a base image which is sliced to pieces. Then we create a TAL template macro which decorates the frame around the portlet content using CSS.

Base image

I drew the frame base in Paint Shop Pro and used its "Export Slices" function to cut image to 9 pieces. Each piece has its own image file. Filenames are formed with pattern red_portlet_[y coordinate]x[x coordinate].jpg.

Images are located under "red_portlet" folder in my skin folder. It's better to make a dedicated folder for a sliced image, since if there exists even few sliced images your skin folder, the folder listing will be polluted hindering navigation when working with skins.

The color of empty page area is gray. The portlet has dark red header and pure white content area. Drop shadow was added to make the portlet look like it's floating above the page area.

Slicing the portlet base image

TAL template

The following macro defines the template which will be filled with portlet content. In my skin, the portlet template filename is portlet_slot.pt.

TAL page template language has a template mechanism for defining places where calling macros can fill content. metal:define-slot sets the place folder for incoming content. The caller of a template can fill these places with metal:fill-slot call

The portlet header is placed to cell 1x2 (top center). 1x2 image must be so tall that even if portlet header contains more than one row it doesn't break image layout. Use metal:fill-slot="portlet-header" to place header.

The portlet content is placed into cell 2x2. The background is filled with solid white color, though texture pattern could be used. Use metal:fill-slot="portlet-body" to place portlet content.

Note that screen readers, utilities for visually challenged people,  treat <table> tags as content elements. Instead of a <table>, nested <div> layout should be used to render visuals so that text-to-speech software handle the content propeply. I used <table> as an example because table cell concept is easier to grasp than nested divs with CSS tricks. For example about how to use nested divs and background images, see this article.

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"
      i18n:domain="plone">
     
<body>

<div metal:define-macro="portlet">
   
    <table class="portlet-frame">
        <tr>   
            <td class="portlet-top-left"/>
            <td class="portlet-top">                   
                <div class="portletHeader" metal:define-slot="portlet-header">
                    Portlet name
                </div>
            </td>
            <td class="portlet-top-right"/>
        </tr>       
        <tr>
            <td class="portlet-left"/>
            <td class="portlet-center">
                <div class="portletBody" metal:define-slot="portlet-body">
                    Portlet body
                </div>
            </td>
            <td class="portlet-right"/>
        </tr>
        <tr class="portlet-frame-bottom-row">
            <td class="portlet-bottom-left"/>
            <td class="portlet-bottom"/>
            <td class="portlet-bottom-right"/>
        </tr>           
    </table>

</div>

</body>
</html>

CSS code

We define the CSS code for portlet frame in separated file. In my skin, it's called portlet_slot.css.dtml.

This is not pure CSS, but it has mixed in DTML mark-up which allows using variables in CSS. Plone defines variables like emptySpaceColor and background color in file called base_properties.prop, which has a syntax similiar to various configuration file formats.

Each of nine parts of frame image has its own CSS declaration. One need to be careful with image sizes since left, right and bottom border width/height definitions must match actual images or there will be gaps. Use backgrounds colors cleverly so that browsers with images disabled and still loading pages have proper looks for frames.

Portlet slices

/*
 *
 * Image frame component definitions
 *
 */

.portlet-top-left {
    padding: 0;
    margin: 0;
    border: 0;   
    background: &dtml-emptySpaceColor; url(&dtml-portal_url;/red_portlet/portlet_1x1.jpg) top left no-repeat;
}

.portlet-top {
    padding: 0;
    margin: 0;
    border: 0;   
    padding-top: 20px;
    background: &dtml-backgroundColor; url(&dtml-portal_url;/red_portlet/portlet_1x2.jpg) top center repeat-x;
}

.portlet-top-right {
    background: &dtml-emptySpaceColor; url(&dtml-portal_url;/red_portlet/portlet_1x3.jpg) top right no-repeat;
}

.portlet-left {
    padding: 0;
    margin: 0;
    border: 0;   
    width: 18px;
    background: &dtml-emptySpaceColor; url(&dtml-portal_url;/red_portlet/portlet_2x1.jpg) center left repeat-y;
}

.portlet-center {
    padding: 0;
    margin: 0;
    border: 0;   
    text-align: left;
    background: &dtml-globalBackgroundColor;;
}

.portlet-right {
    padding: 0;
    margin: 0;
    border: 0;   
    width: 17px;   
    background: &dtml-emptySpaceColor; url(&dtml-portal_url;/red_portlet/portlet_2x3.jpg) center right repeat-y;
}

.portlet-bottom-left {
    background: &dtml-emptySpaceColor; url(&dtml-portal_url;/red_portlet/portlet_3x1.jpg) bottom left no-repeat;
    padding: 0;
    margin: 0;
    border: 0;
}

.portlet-bottom {
    padding: 0;
    margin: 0;
    border: 0;   
    background: &dtml-emptySpaceColor; url(&dtml-portal_url;/red_portlet/portlet_3x2.jpg) bottom center repeat-x;
    height: 16px;
}

.portlet-bottom-right {
    padding: 0;
    margin: 0;
    border: 0;   
    background: &dtml-emptySpaceColor; url(&dtml-portal_url;/red_portlet/portlet_3x3.jpg) bottom right no-repeat;
}

Contribute

Something wrong or out of date? Anybody can edit or create a new article in the knowledge base. Simply create an account on this site, log in, and click the Edit button to contribute.