Creating style plugins for Panels in your theme folder
There seems to be a paucity of information out there on how to create styles for Panels, there's a tutorial here for doing it in a module:
http://manueg.okkernoot.net/blog/200910/how-create-panels-style-plugin
But i've done it in my theme which I thought other people might find quite handy. The good thing about putting the style (and layout plugins - look for a future lesson there) in the theme folder is that you've got all your theming stuff in one place. I think there's also a little less to do too.
There's 2 things you can create a style for in Panels, the Region style and the Pane style and you can create custom styles for each.
here's the regions style - click on the cog on the top left:
panel style
here's the panel style - click on the cog on the top right of the pane:
region styles
The first thing to do when creating custom styles in your theme is adjust the theme info file. Look for the file with a .info extension in your active theme and open that. Then add this code somewhere near the end:
; Plugins
plugins[panels][styles] = plugins/styles
plugins[panels][layouts] = plugins/layoutsThis tells Panels that we've got a styles directory, and a layouts directory in our themes folder for keeping all our custom stuff (the layouts one is for another time, though there's some help here too: http://drupal.org/node/495654)
I've made 2 custom styles one for a region and one for a pane. My particular needs were preventing the separator divs from appearing. Making sure that if nothing was being outputted by a pane then no wrapper divs would be printed either, and reducing all extra markup and still keeping the title.
The first problem was the separator divs - Panels by default sticks the bit of markup below between every pane in a region:
<div class="panel-separator"></div>The second problem was that even if nothing was in a pane, I was still getting a wrapper div appearing - which meant it was getting styled by my css and mucking up my layout. Sometimes (but not always) a CCK field for instance will send some info to panels making panels print out markup even though the pane was empty, like this:
<div class="inside nmc-padding">
<div class="panel-pane">
<div class="pane-content"> </div>
</div>
</div>In my panels layout i'm wrapping each region in an if statement to see if there's content. If there's none I don't show the region - but this breaks it. I'll go into that further if anyone else wants to know in a Panels Layout tutorial. But to get rid of that style here's how i've done it.
First of all create a folder called plugins in your theme folder, then create a folder called styles within that. So you folder layout should look like this:
THEME_FOLDER/plugins/styles
Copy a Panels region style into that folder to use as a starting point. The tricky thing here initially was working out what was a region style and what was a pane style. The are all stored in:
modules/panels/plugins/styles
In that folder default.inc is a region style - you'll see that it has a different hook (_render_panel) from pane styles (_render_pane). Copy that to your styles folder in your theme (THEME_FOLDER/plugins/styles) and rename it - i called mine nosep.inc as i wanted the separators removed. To make it work you have to rename the functions so that they include the name of the file and the theme name. So the first function implementing hook_panels_style_info needs to be renamed like this:
function THEMENAME_FILENAME_panels_styles()
You must also rename the array with the filename. The name of my file is nosep and my theme is zen_ninesixty. Here's that function in full for my nosep.inc file:
<?php
/**
* Implementation of hook_panels_style_info().
*/
function zen_ninesixty_nosep_panels_styles() {
return array(
'nosep' => array(
'title' => t('Minimal'),
'description' => t('Wont display if there\'s no conent, displays each pane without a separator.'),
'render panel' => 'zen_ninesixty_nosep_style_render_panel',
),
);
}
?>You see that the 'render panel' callback is for the second function in this file, but confusingly its not exactly the name of that function. Rename the callback like this:
THEMENAME_FILENAME_style_render_panel
The second function which is the render callback must be renamed like this (replacing only the parts in capitals), its confusing because i'm used to replacing the word theme in the template with the name of the theme name, but not here:
function theme_THEMENAME_FILENAME_style_render_panel($display, $panel_id, $panes, $settings)
Here's that function in full in my file:
<?php
function theme_zen_ninesixty_nosep_style_render_panel($display, $panel_id, $panes, $settings) {
$print_separator = FALSE;
foreach ($panes as $pane_id => $content) {
// Add the separator if we've already displayed a pane.
if ($print_separator) {
// print separator has been disabled in this style
//$output .= '<div class="panel-separator"></div>';
}
// uncomment this out to see all the content from the pane
// print_r($content);
// checking to see if there's anything in the main content area (you can also check title and lots of other stuff)
if($content->content){
$output .= $text = panels_render_pane($content, $display->content[$pane_id], $display);
}
// If we displayed a pane, this will become true; if not, it will become
// false.
$print_separator = (bool) $text;
}
return $output;
}
?>If you compare the default.inc file in your panels module to this you can see what changes i've made. There aren't many:
- To remove the separator i've commented out the line: $output .= '';
- To prevent any content outputting at all unless there's going to be visible content in the pane i've put an if statement round this line:
$output .= $text = panels_render_pane($content, $display->content[$pane_id], $display);
I'm testing to see whether $content->content has anything in it. There's lots of possible stuff that can be in the $content object which can trigger rendering, but its not always what you want. This may not be the solution for you, but it shows what you can do. If you uncomment the print_r($content); line you'll see everything that the $content object has to offer
All you have to do now is make sure you clear your cache and you should see the new theme appear under the top left cog on any panel/region.
To create a pane style is even simpler. I wanted the 'No markup at all' style, but wanted pane titles to appear. So I made my own version of that. Copy the file naked.inc from panels/plugins/styles to your THEME_FOLDER/plugins/styles. Change the name of the file, I called mine stripped.inc. You need to change the functions in this file to suit your theme name and filename like you did for region styles. The first function, implementing hook_panels_style_info() needs to be renamed as below:
function THEMENAME_FILENAME_panels_styles()
Here's my first function in full:
<?php
/**
* Implementation of hook_panels_style_info().
*/
function zen_ninesixty_stripped_panels_styles() {
return array(
'title' => t('Less markup, with title'),
'description' => t('Display the pane with less markup, but include the title.'),
'render pane' => 'zen_ninesixty_stripped_style_render_pane',
);
}
?>The render callback you can see above needs to be renamed like this:
THEMENAME_FILENAME_style_render_pane
The second function which is the render callback you can see above also needs to be renamed like this (again only change the capitals):
function theme_THEMENAME_FILENAME_style_render_pane($content, $pane, $display)
Here's that function in full:
<?php
/**
* Render callback.
*
* @ingroup themeable
*/
function theme_zen_ninesixty_stripped_style_render_pane($content, $pane, $display) {
if (!empty($content->title) && $content->content != '') {
$output = '<div class="title">' . $content->title . '</div>';
}
if (!empty($content->content)) {
$output .= $content->content;
}
return $output;
}
?>The change i've made here is to print the title, but only if there's some content in the pane. Like the naked style there's no extra markup around the content. Generally you get enough of that with CCK anyway.
Good luck with it and let me know if you've any comments

Comments
Thanks mate, really helpful
Thanks mate, really helpful post!
I just want to add one thing:
you should be careful with file names of those plugin files. They must have .inc extention or ctools module won't recognize them as plugins.
Took me a couple of hours to figure out that ;)
Nicely explained
Great job explaining how to provide a style in your theme. And thanks for linking to my tutorial, I've updated it to link to yours so that people get both ways to do it.
Greetings!
Two thumbs!
Ta muchly for that great tutorial, helped me create panels with window manager-style decorations and accordions.