Last updated 27 February 2014. Created on 19 November 2008.
Edited by Christopher James Francis Rodgers, KrisBulman, xjm, roderik. Log in to edit this page.

Zenophile Module

Zenophile is a tiny module which allows themers to very easily create Zen subthemes without all the tedious file copying and find-and-replacing required when creating subthemes by hand. With this module, subthemes can be created in a fraction of the time just by entering information into a single-page form and clicking “Submit.”

Drush for zen-6.x-2.x

With Drush 7.x-4.4 this script needs named as to be placed in your ~/.drush/ folder.

Run the command from the themes folder that zen resides in, example:

It supports the following commands:

drush zen "My theme name" my_theme

 * @file
 * Contains functions only needed for drush integration.

 * Implementation of hook_drush_command().
function zen_drush_command() {
  $items = array();

  $items['zen'] = array(
    'description' => 'Create a theme using zen.',
    'arguments' => array(
      'name'         => 'A name for your theme.',
      'machine_name' => '[optional] A machine-readable name for your theme.',
      'description'  => 'A description of your theme.',
    'options' => array(
      'name'         => 'A name for your theme.',
      'machine-name' => '[a-z, 0-9] A machine-readable name for your theme.',
      'description'  => 'A description of your theme.',
      'without-rtl'  => 'Remove all RTL stylesheets.',
      // @TODO: Add these options:
      // 'layout'       => '[fixed,fluid,960gs] Choose the page layout method.',
    'examples' => array(
      'drush zen "My theme name"' => 'Create a sub-theme, using the default options.',
      'drush zen "My theme name" my_theme' => 'Create a sub-theme with a specific machine name.',

  return $items;

 * Create a zen sub-theme using the starter kit.
function drush_zen($name = NULL, $machine_name = NULL) {
  // Determine the theme name.
  if (!isset($name)) {
    $name = drush_get_option('name');

  // Determine the machine name.
  if (!isset($machine_name)) {
    $machine_name = drush_get_option('machine-name');
  if (!$machine_name) {
    $machine_name = $name;
  $machine_name = str_replace(' ', '_', strtolower($machine_name));
  $search = array(
    '/[^a-z0-9_]/', // Remove characters not valid in function names.
    '/^[^a-z]+/',   // Functions must begin with an alpha character.
  $machine_name = preg_replace($search, '', $machine_name);

  // Determine the path to the new subtheme by finding the path to zen.
  $zen_path = drush_locate_root() . '/' . drupal_get_path('theme', 'zen');
  $subtheme_path = explode('/', $zen_path);
  $subtheme_path = implode('/', $subtheme_path) . '/' . str_replace('_', '-', $machine_name);

  // Make a fresh copy of the original starter kit.
  drush_op('zen_copy', $zen_path . '/STARTERKIT', $subtheme_path);

  // Replace all occurrences of 'STARTERKIT' with the machine name of our sub theme.
  drush_op('zen_file_str_replace', $subtheme_path . '/', 'STARTERKIT', $machine_name);

  // Rename the .info file.
  $subtheme_info_file = $subtheme_path . '/' . $machine_name . '.info';
  drush_op('rename', $subtheme_path . '/', $subtheme_info_file);

  // Alter the contents of the .info file based on the command options.
  $alterations = array(
    '= Zen Sub-theme Starter Kit' => '= ' . $name,
  if ($description = drush_get_option('description')) {
    $alterations['Read the <a href="">online docs</a> or the included README.txt on how to create a Zen sub-theme.'] = $description;
  drush_op('zen_file_str_replace', $subtheme_info_file, array_keys($alterations), $alterations);

  // Replace all occurrences of 'STARTERKIT' with the machine name of our sub theme.
  drush_op('zen_file_str_replace', $subtheme_path . '/theme-settings.php', 'STARTERKIT', $machine_name);
  drush_op('zen_file_str_replace', $subtheme_path . '/template.php', 'STARTERKIT', $machine_name);

  // Remove all RTL stylesheets.
  if ($without_rtl = drush_get_option('without-rtl')) {
    foreach (array('forms', 'html-reset', 'layout-fixed', 'layout-liquid', 'navigation', 'pages', 'tabs') as $file) {
      // Remove the RTL stylesheet.
      drush_op('unlink', $subtheme_path . '/css/' . $file . '-rtl.css');
      drush_op('zen_file_str_replace', $subtheme_path . '/css/' . $file . '.css', ' /* LTR */', '');
      // Remove the RTL sass file.
      drush_op('unlink', $subtheme_path . '/sass/' . $file . '-rtl.scss');
      drush_op('zen_file_str_replace', $subtheme_path . '/sass/' . $file . '.scss', ' // LTR', '');

  // Notify user of the newly created theme.
  drush_print(dt('Starter kit for "!name" created in: !path', array(
    '!name' => $name,
    '!path' => $subtheme_path,

 * Copy a directory recursively.
function zen_copy($source_dir, $target_dir, $ignore = '/^(\.(\.)?|CVS|\.svn|\.git|\.DS_Store)$/') {
  if (!is_dir($source_dir)) {
    drush_die(dt('The directory "!directory" was not found.', array('!directory' => $source_dir)));
  $dir = opendir($source_dir);
  while($file = readdir($dir)) {
    if (!preg_match($ignore, $file)) {
      if (is_dir($source_dir . '/' . $file)) {
        zen_copy($source_dir . '/' . $file, $target_dir . '/' . $file, $ignore);
      else {
        copy($source_dir . '/' . $file, $target_dir . '/' . $file);

 * Replace strings in a file.
function zen_file_str_replace($file_path, $find, $replace) {
  $file_contents = file_get_contents($file_path);
  $file_contents = str_replace($find, $replace, $file_contents);
  file_put_contents($file_path, $file_contents);

UNIX Shell script

This script prompts for a human-readable name for a new subtheme name. The human readable name is placed in the theme's .info file. A machine-readable name is created by converting the human-readable name to all lowercase letters (dashes and spaces become underscores); for example, Joe Cool's "Better" Theme becomes joe_cools_better_theme. The script then copies the STARTERKIT to the new subtheme directory and renames internal references as necessary.

Script for zen-6.x-1.x

# This script creates a new subtheme for zen following the instructions on
# Place this file in your themes directory, alongside the zen folder, as a file named "subtheme"
# Usage: open a shell, navigate to the themes directory and run ./subtheme
# (Run chmod 700 on this file to make it executable)
# Based on script submitted here:

echo "Enter a human-readable subtheme name:"

NAME=`echo $INPUTNAME | tr '[:upper:]' '[:lower:]' | sed -e 's/[ -]/_/g' -e 's/[^a-z0-9_]//g'`
SEDNAME=`echo $INPUTNAME | sed -e 's/"/\\"/g'`

echo -n "Choose a page layout type [f]ixed or [l]iquid for subtheme $NAME: "
read -e LAYOUT

if [ $LAYOUT = "f" ]; then
elif [ $LAYOUT = "l" ]; then
  echo Invalid layout type.


# Change the theme name (the line 'name = ...') to the new subtheme name
sed 's/STARTERKIT/'$NAME'/g' $NAME/ | sed 's/^\(name *= *\).*/\1'"$SEDNAME"'/g' > $NAME/$
  rm $NAME/

cp zen/zen/$CSS $NAME/layout.css
cp zen/zen/print.css $NAME/print.css
cp zen/zen/html-elements.css $NAME/html-elements.css
cp zen/zen/zen.css $NAME/$NAME.css
sed 's/STARTERKIT/'$NAME'/g' zen/STARTERKIT/template.php > $NAME/template.php
sed 's/STARTERKIT/'$NAME'/g' zen/STARTERKIT/theme-settings.php > $NAME/theme-settings.php

exit 0

Script for zen-6.x-2.x

# This script creates a new subtheme for zen-6.x-2.x-dev
# Place this file in your theme directory, alongside the zen folder, as a file name "subtheme-creator"
# Usage: open a shell, navigate to the zen directory and run ./subtheme-creator
# (Run chmod 700 on this file to make it executable)
# Based on script submitted here:

#~ Chain of events:
#~ * copy STARTERKIT/ from zen/ and rename to NEWNAME (lowercase/underscores)
#~ * rename to
#~ * edit name and description field
#~ * remove unneeded layout-{!chosen layout}.css file and reference in
#~ * replace all occurances of STARTERKIT in template.php & theme-settings.php with NEWNAME

echo -n "Enter a name for your theme: "
read -e NAME

HUMAN=`echo $NAME | sed -e 's/[^a-zA-Z0-9\ ]//g'`
NAME=`echo $NAME | tr [:upper:] [:lower:] | sed -e 's/[\-\ ]/_/g' -e 's/[^a-z0-9_]//g'`

if [ -d $NAME ]; then
    echo "Theme folder already exists with that name."
    echo "Do you want to remove the existing theme?"
    select OVERWRITE in Yes No
        if [ $OVERWRITE = "Yes" ]; then
            rm -r $NAME /tmp/$NAME;
            echo "Exiting..."

echo "Choose a page layout"
select LAYOUT in Fixed Liquid
    if [ $LAYOUT = "Liquid" ]; then

# Copy the STARTERKIT folder

# Create .info file
sed -e 's/STARTERKIT/'$NAME'/g' -e 's/Zen Sub-theme Starter Kit/'"$HUMAN"'/g' -e 's/^\(description = \).*$/\1A Zen sub-theme/g' $NAME/ > $NAME/$
rm $NAME/*

# Remove layout css file and references not required
rm $NAME/css/$DELCSS*
sed -e 's/'$DELCSS'/'$CSS'/g' $NAME/$ > /tmp/
mv /tmp/ $NAME/$

# Replace all occurances of STARTERKIT with theme name in template and theme-settings files
sed 's/STARTERKIT/'$NAME'/g' $NAME/template.php > /tmp/zen-template.php
mv /tmp/zen-template.php $NAME/template.php
sed 's/STARTERKIT/'$NAME'/g' $NAME/theme-settings.php > /tmp/zen-theme-settings.php
mv /tmp/zen-theme-settings.php $NAME/theme-settings.php

echo New theme folder $NAME created in `pwd`

exit 0

Looking for support? Visit the forums, or join #drupal-support in IRC.


Nick Robillard’s picture

I added this to handle an svn committed STARTERKIT dir.

# Handle svn - export if .svn dir exists.
if [ -d "$DIRECTORY""/.svn" ]; then
  svn export $DIRECTORY $NAME
sanguis’s picture

the 6.2 versions work for 7.3 as well.
thank you.