Image frames for portlets
Plone with pure CSS doesn't offer very rich looks customization possibilities. If you want to go beyond bars, blocks and solid colors you need to go to beyond CSS. This tutorial explains how Plone's portlets can gain smooth round corners and drop shadows. We create a portlet frame template macro, decorate frame using CSS and then modify Plone's existing portlets to utilize the image frame template.
Basics
What are we going to do
You want to have round corners, wicky borders and drop shadows to your portlets? This tutorial explains how to modify Plone's portlets to have free form frame made from images around the portlet.
What we are going to do
- Creating one template macro which is called by every portlet
- This macro renders the frame around the portlet content
- Portlets place their content inside frame using the metal:fill-slot mechanism of TAL page template language
- Template macro defines nine image slice around the portlet content. CSS background-image property is used to stylize these slices.
- Individual portlet macros calls portlet template macro
- Modify portlet header to fill its name to fill-slot
- Modify portlet wrap its content to portlet body fill-slot
This approach is good because
- Frame visuals and content are separated to two different templates. Either of them can be changed without affecting another.
- It's very clear approach without much hazzle
- Portlets can be resized to any size without breaking the layout
- Works well with all browsers
- Changes to individual portlet code is minimized by using shared portlet frame template
Prerequisites
- Basic Plone development knowledge
- Plone skin mechanism knowledge. This tutorial doesn't tell where files should be placed inside Plone or how one can rollout new skins.
- TAL page template language knowledge
- CSS knowledge
- Image editor with slice export functionality
The result
The result will look cool like this

Making the template
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.

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.

/*
*
* 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;
}
Utilizing the template
How to change existing portlets to use image frame template
Stock Plone 2.1.x is doesn't have fill-slot mechanism for portlets. This means a lot of manual work for us: Every portlet macro you are going to use at your site must be manually modified to take the advantage of the image frame template and fill-slots. I hope this will be changed in the future versions of Plone and personally I am glad to help in such a task. Plone's militaristic 90 degree corners give imago like Plone was the content management system of 70s functional Soviet Russian. This might affect a bit in the harsh competition of modern CMS UIs.
You cannot always simply replace <div> definitions with <metal fill-slot> calls, since there are some restrictions how TAL handles variables. In fill-slot scope variables defined outside the call might not be valid and must be redeclared.
Here I have modified portlet_prefs (preference portlet as example). Changes are bolded.
| Orignal | Modified |
|---|---|
<html xmlns:tal="http://xml.zope.org/namespaces/tal" | <html xmlns:tal="http://xml.zope.org/namespaces/tal" |
Forgive me that Kupu WYSIWYG editor doesn't seem to preverse <td style="vertical-align: top"> attribute for table cells so that the versions could be compared line-to-line. I added some padding new lines to match cell heights.
The result
What will it look like
Here is a sample screenshot what the result could look like

This method is used in Red Innovation skin, available at plone.org product area. I recommend you to download this skin and recycle its TAL and CSS code, so you don't need to reinvent the wheel. Don't expect the process be so straightforward as explained here. E.g. deprecated.css always overrides some CSS styles which forced me to create new CSS styles for portlet content, instead of just redeclaring old styles.
The method is in live usage at www.redinnovation.com.
Markus Proske's MagicSkin product does also corner rounding using images. In his approach, a common portlet template macro is not used.
