
This site was just featured on Design Shack, a gallery of the best designs around the web. Currently, we're holding down a solid 8.5 rating based on user votes -- we encourage you all to go check out the listing and rate our design.
This listing is yet another testament to the flexibility of HiFi CMS. It demonstrates that a design can be detailed and perfected enough to be listed in respected galleries, and still be managed by a powerful CMS. So many systems restrict what you can do with the templating that you could never launch something worthy of being featured on a popular showcase. We took the opposite approach, allowing free templating and pushing web standards, which makes it easy designers to publish work that can be highlighted on design, HTML, and CSS galleries around the web.
If you're visiting from Design Shack -- welcome! Take a tour around the site, learn about the system, and be sure to sign up for updates and announcments.
Thanks again for all of your support! Also, a big shout out to Lenny Terenzi who put together the HiFi design!
What are Javascript templates?
Our HiFi web publishing platform is built for maximum speed and efficiency. An important contributing factor to HiFi’s UI performance is Javascript templates. Popularized by John Resig, Javascript templating is a fast and efficient technique to render client-side templates with Javascript using a JSON data source. The template is HTML markup, peppered with tags that will either insert variables or run programming logic.
Resig's Javascript templates can be stored in a variable or encapsulated within a <script id="templateId" type="text/html"></script> element. HTML markup in a <script> element is effectively ignored by the web browser, yet still accessible by Javascript or jQuery using the <script> element’s id attribute.
Resig’s initial proof of concept has since been converted to a convenient jQuery plugin that couldn't be easier to use:
<script type="text/html" id="template">
<li id="{%= id %}">{%= content %}</li>
</script>
<ul id="destination"></ul>
The example Javascript template above may be rendered and appended to an unordered list element with the following Javascript.
var listItem = $('#template').render({
id: '1',
content: 'List item content'
});
$('#destination').append(listItem);
Why do we use Javascript templates?
Javascript templates are rendered and cached client-side without sending an HTTP request to the server — in other words, they’re lightning fast. Speed benefits aside, Javascript templates also afford us the opportunity to abstract the HiFi administrative UI into a simple Javascript API.
The Javascript API separates concerns: HTML, CSS and Javascript are logically separated in their own files. This keeps markup and CSS out of our Javascript logic, so adding functionality to the HiFi UI does not require adjusting the HTML or CSS, and vice-versa. This separation of concerns also enforces human user interface guidelines by encapsulating the UI’s markup and style from the UI’s functionality; we can quickly add new HiFi features and functionality that integrate into the existing UI style seamlessly and automatically.
If or when we do decide to modify the the HiFi UI’s markup or style, each UI element has its own Javascript template with an associated stylesheet making such changes a breeze.
How do we use Javascript templates?
As mentioned above, each UI element has it’s own Javascript template and associated stylesheet. When the UI is rendered in the browser, all UI elements’ templates are made available in a Javascript templates[] array and may be rendered on-demand.
Additionally, the HiFi platform exposes a hifi() Javascript closure, allowing simple communication to and from the HiFi API (a topic for another day, just assume it works). As an example, let’s say you are using the HiFI platform to moderate your website’s user-submitted comments. You need to reply to a comment. You enter your reply in the provided form and click the Reply button. Behind the scenes, your reply is sent to the HiFi server with this code (simplified for demonstration purposes).
var newComment = {
type: 'comment',
author: 'Josh Lockhart',
content: $('#replyTextArea').val()
};
hifi({ type: 'page', id: pageId }).append(
newComment,
function() {
var commentLineItem = $('#commentTemplate').render(commentData);
$('#comments').append(commentLineItem);
}
);
This example code submits your new comment’s data to the HiFi API, and in the callback function, renders a comment template using your reply as the data source. The rendered template is appended to the UI.
We really enjoy client-side Javascript templating
Javascript templates isolate UI interaction client-side with minimal HTTP requests to the server. This practice drastically improves the speed of the UI and the overall performance of the HiFi platform. Additionally it has made the development process much simpler and pleasant. We have all of our markup in one place and we never have to deal with AJAX code that is tangled up with HTML creation.

See the Demo - Download the Zip
Retweet buttons are all over the internet now. They are a handy for giving visitors a quick way to share a post. Most of the retweet buttons out there come from just a few places:
These all work great and are easily recognizable. Sometimes we have wanted to add a button that matches a site a little better. The topsy button has some customization, but not much. It just allows you to change color schemes.
Ideally, it would be possible to put together completely custom markup and styling for a button. That is the purpose of this plugin. It allows you to build your own button and style it however you like from the ground up. It is a simple jQuery plugin that can be called this easy:
$('#retweet').customRetweet();
It works by using two really handy APIs. It goes to topsy to get the tweet count of the url and then it goes to bit.ly to get a shortened link. It then puts together a bunch of variables you can use to build a custom markup and a custom retweet message. For example:
$('#retweet').customRetweet({
template: '<a href="{{retweetURL}}">Retweet this Post!</a>',
retweetTemplate: 'RT @{{account}} - {{title}} - {{shortURL}}'
});
This code would put the completed template code into the element with "retweet" as an id. When the created link was clicked, it would provide twitter with the text from the retweetTemplate variable.
Unlike the standard buttons I link to above, this markup exists in the DOM so it is possible to style it directly with CSS. This makes essentially any look possible for these buttons. In the demo, you can see three simple styles designed by Lenny at Hey Monkey Design. (The last using a bird drawn by Pasquale D'Silva) These are free to be used on your site or adapted for your needs.
To explain how to use the plugin, I'll go into depth on how I made the second one.
Using the Plugin
The first thing to do is to set up a container for the button. This makes it easy to place and target when styling:
<div class="retweetbutton"></div>
Next, we'll need to set up the plugin so that we can have our desired markup added to the container. This is really easy to do -- just write the markup and sprinkle in the information you want using simple template tags:
$('.retweetbutton').customRetweet({
template: '<a class="tweets" href="{{allTweetsURL}}">{{count}} tweets</a><a href="{{retweetURL}}" class="retweet">retweet</a>'
});
This javascript will go and fetch the information that is in the double-curly-braced tags and insert it into the markup. This means any markup can be used and the information can just be sprinkled in.
Finally, the last step is to set up the CSS. For this button I've decided to take advantage of some modern CSS and build it entirely without images. The gradients are accomplished using the cross-browser technique described on Web Designer Wall.
.retweetbutton {
width: 50px;
text-align: center;
text-transform: uppercase;
font: 8px verdana,sans-serif;
}
.retweetbutton a {
position: relative;
display: block;
border-radius: 4px;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
text-shadow: 0px 0px 3px #667ba2;
-moz-text-shadow: 0px 0px 3px #667ba2;
-webkit-text-shadow: 0px 0px 3px #667ba2;
text-decoration: none;
color: #fff;
}
.retweetbutton .tweets {
border: 1px solid #92a6ca;
background: #bed4fb;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#c2d8fe', endColorstr='#98b0da');
background: -webkit-gradient(linear, left top, left bottom, from(#c2d8fe), to(#98b0da));
background: -moz-linear-gradient(top, #c2d8fe, #98b0da);
height: 50px;
line-height: 70px;
}
.retweetbutton .tweets strong{
position: absolute;
top: -15px;
left: 0;
width: 48px;
font-size: 20px;
}
.retweetbutton .retweet {
display: block;
margin: 5px 0 0 0;
border: 1px solid #d07300;
background: #fbb45a;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fcc37c', endColorstr='#fba63b');
background: -webkit-gradient(linear, left top, left bottom, from(#fcc37c), to(#fba63b));
background: -moz-linear-gradient(top, #fcc37c, #fba63b);
height: 15px;
line-height: 16px;
}
.retweetbutton .retweet:hover {
background: #fff;
color: #bb6700;
text-shadow: 0px 0px 2px #aaa;
-moz-text-shadow: 0px 0px 2px #aaa;
-webkit-text-shadow: 0px 0px 2px #aaa;
}
Once this code is set -- the button will begin working. You'll notice that in the JS I only specified the template option.
Plugin Options
Many other options can be used with the plugin, but not all of them are required as they have sensible defaults. Here is a complete list of options available when setting this up:
settings = $.extend({
// topsy settings
topsyAPI: 'http://otter.topsy.com/stats.js?url=',
// bit.ly settings
shorten: true,
bitlyLogin: 'customretweet',
bitlyKey: 'R_be029e1ffd35d52dd502e1495752f0c2',
bitlyAPI: 'http://api.bit.ly/v3/shorten?format=json&',
// template values
url: location.href,
title: document.title,
account: 'topsy',
// templates
retweetTemplate: 'RT @{{account}} | {{title}} | {{shortURL}}',
template: 'Number of Tweets: {{count}} | All Tweets | Retweet'
}, settings);
The most important options are the template values and the templates. The template values are options you can send to the templates. By default it will use the page url and page title and retweet from the 'topsy' twitter account. The templates are the markup that the plugin will use to build the button and default retweet message.
The less important settings are the API values. By default this uses the topsy and bitly APIs. Currently no other APIs are supported, but this information is abstracted anyway. If you would like to use a different bit.ly API account it can be done through these settings.
The Template Variables
When creating the templates for the button markup and default retweet text, there are a number of variables available. To use one of the, just use currly braces without spaces. For example: {{variableName}}. There are many more variables available than commonly used. Here is the complete list:
General
- count - The total number of tweets the url has.
- title - The title of the page. This can be overridden in the settings.
- url - The full url of the page. This can be overridden in the settings.
- account - The twitter account to use in the retweetTemplate. For example "gethifi" or "nmcteam"
- shortURL - The bit.ly shortened version of the full url.
- allTweetsURL - The link to all tweets of the url on topsy.
Topsy (documentation)
- all - This is the count. The count variable is just an alias of this.
- influential - The number of 'influential' tweets the url has.
- contains/topsy_trackback_url - Not generally useful.
Bit.ly (documentation)
- url - This is NOT used. Since url is already used, this is renamed to shortURL.
- hash - the bit.ly identifier for long_url that is unique to the API account.
- global_hash - the global bit.ly identifier used for tracking stats.
- long_url - the original url.
- new_hash - this is 1 if it is the first time the long_url has been shortened by bit.ly
This covers just about everything. For any in depth comments or cool usage ideas, leave them in the comments.
See the Demo - Download the Zip

The GetHiFi.com site has been featured in the HTML 5 Gallery. This is the leading site used to showcase sites that use html5 for markup. One of our goals when creating this site was to build something that showcased the full range of what HiFi was capable of.
This site is built using HTML5 and CSS3 extensively. We were also sure to document the entire build process.
The HiFi team is really honored to have the site featured. Hopefully many more designers and developers are able to use HiFi to build HTML5 sites in the future!

Development on HiFi has been accelerating rapidly. Nearly all of the groundwork is final and most of the work is now oriented on its interface. Some of the interesting decisions that have been made recently are:
- The Admin interface is built using Javascript templating. This has a couple benefits -- the most exciting of which is how much easier it will be to build custom admin interfaces for custom types.
- All Core Types Are in Place. This means that we'll be ready to add any current NMC sites to HiFi shortly. The core types are: Page, Blog/Listing, Form, Gallery, Link, Category and Folder. Already these types are proving to be really flexible.
- Comment Moderation is Done. Getting this right can be tough. Now we have an easy-to-use Comment Moderation panel we can install on any page.
There are of course many more exciting things going on that we'll be sure to write about. In the meantime we are sure to update both twitter and our new Flickr page with news about our progress.
So for up-to-the-minute updates, be sure to follow @gethifi on twitter and check out our Flickr account.
View the Demo | Download the Zip

The screenshot above is jPhotoGrid in action. This plugin takes a simple list of images and captions and turns it into a grid of photos that can be explored and zoomed. You can check out a demo here.
Overview
Nearly all of the styling for this plugin is done in css. The trick is to layout the grid by floating the list items. The first thing the plugin will then do, is convert these all to absolutely positioned. This is what allows the plugin to zoom in on an individual image and then return it to its place. Like my interactive map plugin, this depends on the browsers ability to scale images. In my stylesheet, the images are set to width:100% and I simply animate the size of the container.
The Markup
The html for this plugin is simply an unordered list of images and captions. Here is the format:
<ul>
<li>
<img src="/hifi/site/edit/blog/source.jpg" alt="" />
<p>Caption Here</p>
</li>
...
</ul>
The CSS
The CSS is also fairly straight-forward. The key things to notice are the .active and .selected classes. When you hover over a list item, it is given the class 'active'. Once you have clicked the list item, it is given the 'selected' class.
#pg { position: relative; height: 585px; background: #000; }
#pg li { position: relative; list-style: none; width: 175px; height: 117px; overflow: hidden; float: left; z-index: 2; opacity: .3; }
#pg li.active { opacity: 1; }
#pg li.selected { opacity: 1; z-index: 99; -moz-box-shadow: 0px 0px 10px #fff; -webkit-box-shadow: 0px 0px 10px #fff; }
#pg li img { display: block; width: 100%; }
#pg li p { color: white; margin: 10px 0; font-size: 12px; }
The Javascript
The javascript is easy enough to set up. It needs to know the sizing of the thumbnails as well as the sizing and positioning of the zoomed image. This is how the plugin is able to zoom and restore each of the images. Note that you can also change the active and selected classes by setting them using the 'activeClass' and 'selectedClass' options.
$('#pg').jphotogrid({
baseCSS: {
width: '175px',
height: '117px',
padding: '0px'
},
selectedCSS: {
top: '50px',
left: '100px',
width: '500px',
height: '360px',
padding: '10px'
}
});
Conclusion
While not nearly as extensible or flexible as most of the plugins I like to put together, I enjoyed this one. It is a fun way to explore a gallery of images and ends up working great in conjunction with the jFlickerFeed Flickr Plugin I put together last week. If you have any questions or comments, leave them below.
View the Demo | Dowload the Zip
For our HiFi content management system we wanted to automatically combine and compress CSS and Javascript files. On complex sites, this minification can have a significant impact on page load times. You can read more details about the benefits and our template-side implementation in a previous post. Here I am going to discuss the technical details of how we set things up on our nginx server.
Getting Started
There is a very useful and stable Minify project on Google Code. Both CSS and Javascript minification libraries are included and they can easily be switched out if you prefer another library. The normal way to use Minify is to install it in your root directory and call its main file with a query string to specify the specific files that should be included: /min/?f=js/jquery.js,js/jquery.cycle.js,js/setup.js. However with this setup, even after caching the minified files server-side, the PHP interpreter is involved in delivering the file, which is relatively inefficient. Ideally, after a file is cached the first time nginx would serve the compressed file directly.
There is some sample code included in the Minify download that uses Apache url rewriting to take a more "normal"-looking url and point it to the Minified version. This was almost what we were looking for, except for two things: it requires a very specific url structure—with all JS and CSS files stored inside the Minify folder—and we are not using Apache. For HiFi, we decided on Nginx, as the configuration for running many sites off of the same code-base is much simpler. Nginx has a HttpRewriteModule, which is similar to Apache's mod_rewrite, so I figured there must be a way to replicate the Minify sample code in the new environment.
Aside: Template Tags
A quick preview of how HiFi handles stylesheets and scripts to give you a bit of context: We added two new tags to our templating language: {% css %} and {% js %}. These are designed to be used every time a script or stylesheet is included in a template, and they do everything needed to hook into the minification system. Each tag takes a comma-separated list of file names that will be included. For example, a standard js tag might be {% js 'jquery,jquery.cycle,setup' %}.
By default, these tags will output straight
or tags for each file in the list. However, adding min as the last parameter of either tag will cause it to instead output a single tag containing a special url that will get the Minified version. This way, it is just a matter of adding and removing that single parameter to switch between development and production modes. You could even set up a global variable to change it for all the tags in a layout at once.
The url format for serving the minified file is scripts/jquery,jquery.cycle,setup_123456.mjs. There are three things to note here: The first is that we use the .mjs extension (or .mcss). That change is what tells Nginx to serve the Minified version. The second thing is the timestamp we are appending to the filename. Since we will be setting far-future expires headers (so visitors' browsers know to cache the files locally), we need a way to manually expire the files when we have changed them. Our template tag automatically resets the timestamp whenever the source files change, but you could also increment it manually. Finally, this script assumes that JS files will be in a folder called /scripts and CSS files will be in /styles. Eventually, it would be nice to be able to place files in any arbitrary location, but that makes things considerably more complicated.
How It’s Going to Work
For maximum speed, we are going to enable two different types of cacheing: of the minified files on the server and in the visitor's web browser. Whenever a particular combination of files is requested, nginx will check to see if there is already a cached copy. If there is—and there will be except for the first time after a source file is modified—the server will return that file directly, without involving the PHP interpreter at all. This operation is very fast, as it required almost no processing.
If nginx cannot find a cached copy of the file, it will pass the request off to our minification script. This code uses the Minify library to combine and compress the source files, returns the end result to the visitor's browser, and caches it for future use. It will actually cache two versions: one as plain-text and one that has been further compressed with gzip. Nginx uses content negotiation to determine which of the two should be returned to the browser.
What happens when you change a source file? Since we will be telling the visitor's browser to cache our asset files indefinitely, you will need to change the timestamp/version number on the file when you make updates. By doing that, you will invalidate both the server-side and the client-side caches and cause the files to be re-minified.
The Code
Nginx Configuration
Open up the nginx.conf file on your server. In the http block, add the following code:
# Logic for serving minified CSS & JS
location ~* \.(mjs|mcss)$ {
set $domain www.domain.com; # Change this to your site's domain name
set $root_fcgi /var/sites/site_name; # Change this to the public root folder of your site
set $root_cache $root_fcgi/cache; # Change this to a folder in which to cache the minified files
set $min_dir /var/sites/min; # Change this folder to wherever you put the Minify files
include fastcgi_params;
fastcgi_param SITE_ROOT $root_fcgi;
fastcgi_param SCRIPT_FILENAME $min_dir/nginx-mininification.php;
fastcgi_param PATH_INFO nginx-minification.php;
fastcgi_param SERVER_NAME $domain;
fastcgi_param CACHE_DIR $root_cache;
root $root_cache;
expires max;
gzip_static on; # You will need to have installed Nginx using the --with-http_gzip_static_module flag for this to work
gzip_http_version 1.1;
gzip_proxied expired no-cache no-store private auth;
gzip_disable "MSIE [1-6]\.";
gzip_vary on;
# If there is not already a cached copy, create one
if (!-f $request_filename) {
root $root_fcgi;
fastcgi_pass 127.0.0.1:9000;
}
}
Make sure to set the four variables at the beginning to reflect the way your server is actually configured.
Minify and Cache
First, copy the Minify files to anywhere you like on your server. They do not need to be in a publicly-accessible location, since Nginx will be proxying requests to Minify behind the scenes. The Minify config.php file contains sensible defaults, but check it to be sure.
Create a new file inside the Minify directory, called nginx-minification.php. Paste the following code into it:
$sources,
'quiet' => true,
'encodeMethod' => '',
'lastModifiedTime' => 0
));
if (! $output['success']) {
send404();
}
// Clear old cached files
if ($handle = @opendir($cachedir)) {
while (false !== ($file = @readdir($handle))) {
if ($file != '.' && $file != '..' && stristr($file, $filename)) {
@unlink($cachedir . '/' . $file);
}
}
@closedir($handle);
}
// Cache the output
error_reporting(0);
if (false === file_put_contents($cache, $output['content']) ||
false === file_put_contents($cache.'.gz', gzencode($output['content'],9))) {
echo "/* File writing failed. Your cache directory, {$cachedir}, must be writable by PHP. */\n";
exit();
}
// And return it to the client
unset($output['headers']['Last-Modified'], $output['headers']['ETag']);
foreach ($output['headers'] as $name => $value) {
header("{$name}: {$value}");
}
echo $output['content'];
Testing It Out
You should be ready to go. Try converting your script and style tags to use the new url format ("/scripts/jquery,setup_123.mjs" or "/styles/layout,typography,homepage_123.mcss"), making sure that the source files are in either the scripts or the styles folders. If it doesn't work, make sure all the paths in the Nginx configuration file are set correctly and that the cache folder exists. If you have suggestions or problems, please post them in the comments! I'm no expert on the ins and outs of nginx, so I am sure there are better ways to do certain things here.
We thought it would be interesting to look at the various things that happen as a web page loads, both technically and from the visitor's perspective. Although these processes happen in parallel and are connected, events on the two timelines won't necessarily match up exactly as I've show here. The actual loading speeds depend on many factors including page size, connection speed, web browser, and number of asset files. However, the order in which the steps happen will be as shown in most cases. If you want more detailed timing information for your particular site, I recommend starting with either the "Resources" tab in Webkit's Web Inspector or the "DOM" tab in Firebug for Firefox.
Future articles here on the HiFi blog will go into more detail about ways to tweak your website in order to optimize both the technical and user experience timelines.

References:
- Powers of 10: Time Scales in User Experience and Response Times: The Three Important Limits —Jakob Nielsen
- Reaction Time —Wikipedia
- Bing and Google Agree: Slow Pages Lose Users —Brady Forrest
- 5-Second Tests: Measuring Your Site's Content Pages —Christine Perfetti
- First Impressions - What Your Web Site Says About You (or your company) —Thanny Young
View the Demo | Download the Zip
We often work with clients that maintain accounts with Twitter, Flickr, Youtube and other services in addition to their website. Often they will want to pull in data from one of their accounts to their website. With Flickr, this is pretty easy because they make a simple API available. Having worked with it a few times, we decided to make it even easier to pull photos from a public feed.

There are a few examples of this in use on the demo page. All of the photos on this page have been used with the generous permission of John Roberts (@jroberts13).
Plugin Overview
This plugin works by pulling a JSON feed from Flickr and applying the data it gets back to a template. For example, we can generate this list of pictures:
<ul>
<li><img alt="Photo Title" src="http://farm4.static.flickr.com..." /></li>
<li><img alt="Second Photo Title" src="http://farm4.static.flickr.com..." /></li>
</ul>
From the following jQuery:
$('ul').jflickrfeed({
limit: 2,
qstrings: {
id: '44802888@N04'
},
itemTemplate: '<li><img alt="{{title}}" src="{{image_s}}" /></li>'
});
The plugin gets the feed from Flickr using AJAX and applies each image it gets back to the provided template.
The Plugin Options
There are a number of options available on the plugin, these are the defaults:
flickrbase: 'http://api.flickr.com/services/feeds/',
feedapi: 'photos_public.gne',
limit: 20,
qstrings: {
lang: 'en-us',
format: 'json',
jsoncallback: '?'
},
cleanDescription: true,
useTemplate: true,
itemTemplate: '',
itemCallback: function(){}
Here is a description of each one:
- flickrbase: This is unlikely to be needed. It is used to set up the url the jQuery AJAX call will need to be made to.
- feedapi: There are a number of feeds that flickr makes available. The default is the public feed. Here is the list of all that are available: http://www.flickr.com/services/feeds/. This has only been tested on feeds that return photos. So, for example, don't expect good results using this plugin on the Forum discussion feeds.
- limit: Set how many items you want to loop through. Flickr seems to limit its feeds to 20, so that is the default.
- qstrings: This is the most important setting. This is used to request the correct feed. In my examples I use this to set the user id. These are automatically added to the request url. Depending on which feed you use (http://www.flickr.com/services/feeds/) there are different sets of query parameters available: http://www.flickr.com/services/feeds/.
- cleanDescription: Flickr puts all kinds of junk in the description it returns. By default this plugin is set to remove everything but the plain photo description.
- useTemplate: Set this to false if you don't want to use the plugins templating system.
- itemTemplate: The template rules are described below.
- itemCallback: You can add a callback on each item. The scope is set to the container and the item object is made available.
Typically, the only things that will need to be set are limit, qstrings.id and itemTemplate.
Using the Templates
In order to make it really easy to use any kind of markup needed with this plugin, a simple templating system has been build in. Here is an example of a template:
<li>
<a href="{{image_b}}"><img src="{{image_s}}" alt="{{title}}" /></a>
</li>
You can see that this is just basic html with a few special tags mixed in. All of the tags are surrouned by double curly braces. The plugin works by putting in the correct information for each tag and each item into the template.
The tags that are available depend on what flickr returns for each item. For the Public Photos API these are: title, link, date_taken, description, published, author, author_id, tags, and image. For the image property, the plugin uses the Flickr URL scheme to make several sizes available. You can read about this at http://www.flickr.com/services/api/misc.urls.html. The image tags available are: image_s, image_t, image_m, image, image_b.
The Callback Parameter and Integration
The plugin's second parameter is a callback. This has the scope of the containing element and has the entire data response available. This is a great feature if you want to integrate this plugin with others. Let's say you want to integrate it with colorbox. If you call $('selector').colorbox() and then this plugin it won't work. This is becuase the plugin is adding elements to the page after the colorbox call. This is even true if you call colorbox after this plugin. Since this uses ajax, your images won't be added to the page until well after most of your javascript has run. (In computer time).
Instead, the trick is to use the callback function. This allows you to pass in code that you want to run after the images have loaded. So for example, you can do this:
$('#cbox').jflickrfeed({
limit: 14,
qstrings: {
id: '37304598@N02'
},
itemTemplate: '...example...'
}, function(data) {
$('#cbox a').colorbox();
});
Now the colorbox call won't be made on the photos until they are loaded.
Demo and Download
You can see a demo of this plugin on the demo page. Additionally you can download a zip of this plugin and the example.
As always, if you have any questions or comments, leave them below. Enjoy!
As we’ve built out the HiFi Content Management System, we’re always working to make sure we’re including the most important features that allow users to fully leverage their websites. Furthermore, our web development company has built hundreds of sites on various versions of our previous Content Management System and got a boat load of feedback on which features were the most important, helpful, and powerful.
Below is a list I compiled of eight very important elements – I hesitate to call the most important, because that is different to different users. However, these are definitely eight that you should definitely not build a site without. Also, I've eschewed listing some of the no-brainers such as WYSIWYG editor and password protected admin area.
- RSS Feeds – The CMS should automatically generate RSS feeds from all of the sections of the site. Most people tend to just make sure the Blog can generate a feed, but it’s important to ensure that feeds can be generated from anywhere. This feature is critical when trying to push the latest news to the homepage, automatically pull new media onto your Facebook page, and more.
- SEO Control – One of the biggest benefits of a CMS is that it allows your organization to do the hardest part of SEO on your own: create content. However, it’s also important that the site give you control over other important SEO data, such as meta descriptions and title tags. To fully optimize your content and draw in leads, you need to have access to all of your SEO elements.
- Clean URLs – This element goes hand in hand with SEO control, but is important enough to also list on its own. Check out the URLS the CMS creates and look at a few things: 1) they’re not too long, 2) they’re English and could be understood by a person, 3) they can be customized if need be. Too many CMS’ generate sloppy URLs, which ruin any effort at optimization.
- Design Independence – The system needs to be accommodating to any type of design, as design remains an important aspect of visitors’ decision-making process on whether to buy what you’re selling, contact your company, or browse through your content. Don’t underestimate the importance of design; many systems restrict designers/developers and make them design for the system rather than for the brand.
- Different Tiers of User Access – A “super admin” needs to be able to delegate different roles to other administrators. You don’t want admins (or interns) poking around in areas of the site that aren’t relative to them or contain more sensitive information. A system should let you control what content administrators can see and update.
- Simple Form Builder – While there are many really great form building solutions out there, such as Wufoo, it’s important that your CMS have the ability to construct at least simple forms. It gets tedious to always have to login (and pay) to outside vendors to build a form and gather data, and there’s no reason you shouldn’t be able to do it from the CMS.
- Speed and Performance – This one seems to get underestimated by a lot of people, but is a huge deal. I’ve been bogged down in some systems where it takes several minutes to just get to the page I want to edit. This sloth should be a deal breaker. If a CMS can’t perform its main duty speedily and reliably, then that’s typically just an indicator of things to come with that system and company – not to mention it’s really annoying and inefficient
- Incorporate Plugins – The system should have demonstrated its ability to scale through plugins. No base CMS will solve every possible website problem, and you should be wary of anything that claims it does. However, the CMS needs to be open enough that your developers or the CMS company can incorporate plugins that can help improve and scale your site as needed over time.
Does your current CMS really excel without any of these features? Did I leave off some critical features that should be incorporated in this list? Later, I’ll be doing a post on the features that aren’t really as important as you might think.