Nothing beats having a professional designer and Photoshop/Illustrator wizard on your development team, but that is a luxury that is disturbingly rare. For some reason, there just aren’t many people with a deep and broad understanding of HTML, XHTML, CSS, Javascript and server side coding.
Even so, that’s no excuse for a cluttered, hard to follow page layout when there are layout automation tools like Blueprint. While there are many such tools today, I like Blueprint for its compactness, flexibility and its reliability across browsers and platforms. To sweeten the deal, it’s also stupidly simple to implement in CakePHP.
Even if you end up not using it in the final release, it makes the development cycle much easier, simply because it aligns everything along highly customizable grids.
The first step to implementing Blueprint is importing the files. Download the Blueprint Zip file and copy the blueprint folder to APP/webroot/css. There are also useful plugins for buttons, icons and typography. If you want them, copy their respective folders to APP/webroot/css/blueprint/plugins. Each of these folders will contain, at minimum, a readme file and a CSS file, although some may contain additional image files.
Now we’ll need to load the CSS files by editing APP/view/layouts/default.ctp (or default.thtml if you’re using CakePHP 1.1). I use the code:
$screen_css = array(
'blueprint/screen',
'blueprint/plugins/fancy-type/screen',
'blueprint/plugins/buttons/screen',
'site'
);
echo $html->css($screen_css, null, array('media'=>'screen,projection'));
You can now embed Blueprint layout markup anywhere. For example, you could add “span-22 push-1 last” to the class attribute of your container element. This would give the element a left margin equal to 1/24 of the page width, a width equal to 22/24 of the page width, and leave you another 1/24 of the page width in which to place any other elements of interest.
This still doesn’t help with forms though. You could manually add the markup to your views to divide lines, but if you’re discerning, you want all of your fields and labels neatly aligned and it’s unclear how to accomplish that without mucking with the internal Cake FormHelper.
Obviously, that’s undesirable, so I implemented BlueprintHelper:
<?php
App::import('Helper', 'Form');
App::import('Helper', 'Html');
/**
* Modifies the CakePHP FormHelper to support blueprintCSS
*
* @package dadem
*/
class BlueprintHelper extends FormHelper {
/**
* The value of the class attribute assigned to the wrapper div for every label
* in the format elementType => class
*
* @var array
*/
var $_labelClass = array();
/**
* The value of the class attribute assigned to the wrapper div for most elements
*
* @var string
*/
var $_inputClass = array();
/**
* Caches the blueprintCSS markup needed for labels and form elements
*
* @param string $label The value assigned to the class attribute of the div wrapper
* @param string $input
*/
function configure($element, $label = '', $input = '') {
$this->_labelClass[$element] = $label;
$this->_inputClass[$element] = $input;
}
/**
* Wrapper for FormHelper->input that adds blueprintCSS markup
*
* @param string $fieldName This should be "Modelname.fieldname", "Modelname/fieldname" is deprecated
* @param array $options
* @return string
*/
function input($fieldName, $options = array()) {
if (!isset($options['type'])) $options['type'] = 'text';
if (isset($options['div'])) {
$options['div'] = $this->addClass($options['div'], 'clear');
$options['div'] = $this->addClass($options['div'], 'newline');
}
else {
$options['div'] = 'clear newline';
}
$options = array_merge(array(
'before' => '<div class="' . $this->_labelClass[$options['type']] . '">',
'between' => '</div><div class="' . $this->_inputClass[$options['type']] . '">',
'after' => '</div>'
), $options);
return parent::input($fieldName, $options);
}
/**
* Wrapper for FormHelper->end that adds blueprintCSS markup
*
* If $options is set a form submit button will be created.
*
* @param mixed $options as a string will use $options as the value of button,
* array usage:
* array('label' => 'save'); value="save"
* array('label' => 'save', 'name' => 'Whatever'); value="save" name="Whatever"
* array('name' => 'Whatever'); value="Submit" name="Whatever"
* array('label' => 'save', 'name' => 'Whatever', 'div' => 'good') <div class="good"> value="save" name="Whatever"
* array('label' => 'save', 'name' => 'Whatever', 'div' => array('class' => 'good')); <div class="good"> value="save" name="Whatever"
*
* @return string a closing FORM tag optional submit button.
* @access public
*/
function end($options = null) {
if ($options !== null) {
if (is_string($options)) $options = array('label'=>$options);
if (isset($options['div'])) {
$options['div'] = $this->addClass($options['div'], 'clear');
}
else {
$options['div'] = array('class'=>'clear');
}
}
return parent::end($options);
}
/**
* Wrapper for HtmlHelper->div that adds blueprintCSS markup for class clear
*
* @param string $fieldName This should be "Modelname.fieldname", "Modelname/fieldname" is deprecated
* @param string $class CSS class name of the div element.
* @param string $text String content that will appear inside the div element.
* If null, only a start tag will be printed
* @param array $attributes Additional HTML attributes of the DIV tag
* @param boolean $escape If true, $text will be HTML-escaped
* @return string The formatted DIV element
*/
function clear($class = null, $text = null, $attributes = array(), $escape = false) {
if (strlen($class) > 0) {
$class .= ' clear';
}
else {
$class = 'clear';
}
return $this->Html->div($class, $text, $attributes, $escape);
}
}
?>
The code itself is comment according to CakePHP conventions, so it should be fairly self-explanatory. The only new function is configure().
I use it at the start of my view:
$blueprint->configure('text', 'span-4', 'span-8 last');
The first parameter specifies the type attribute of the input element (you can also use ‘select’ for select elements. To see the full list of permitted values, read the code for the input function in CAKE\libs\view\helpers\form.php
The second function defines the classes assigned to the label and the third defines the classes assigned to the form element proper.
I use one such command for each possible element. I generally use it in my view file, but you could implement it in your layout file without harm.
Now, when I want to apply Blueprint formatting selectively to elements, I can implement a view like so:
<div class="locks form span-12">
<?php
echo $blueprint->create('Lock');
echo $blueprint->input('serial');
?>
<div class="clear newline">
<div class="span-4">
<label for="LockC1">Combination</label>
</div>
<div class="span-8 last">
<?php
echo $form->input('c1', array('label'=>'', 'size'=>'2', 'div'=>'')) . ' - ';
echo $form->input('c2', array('label'=>'', 'size'=>'2', 'div'=>'')) . ' - ';
echo $form->input('c3', array('label'=>'', 'size'=>'2', 'div'=>''));
?>
</div>
</div>
<div class="clear">
<?php
echo $form->input('damaged', array('type' => 'checkbox'));
?>
</div>
<div class="clear">
<?php
echo $form->input('id', array('type'=>'hidden'));
?>
</div>
<?php
echo $blueprint->end('Save Record');
?>
</div>
I won’t go into detail, because Blueprint provides a good assortment of resources. Pictures speak louder than words, anyway: