Join our HiFi mailing list to receive the latest news & updates

How to set up Facebook Open Graph meta tags in a HiFi theme

0 Comment(s) | Posted | by Tyler Pearson | |

Social media -- and more specifically Facebook -- is a huge driver of traffic to websites nowadays, so it's increasingly important that sites are optimized for sharing on the social network. To determine what information will be shown when a site is shared, Facebook first checks for the existence of special meta tags that follow the Open Graph protocol.

These tags provide information to Facebook about the page's title, preview image, url, description, in addition to numerous other properties, which will in turn be used when Facebook users do various actions like sharing a website, posting a link on a friend's wall, or clicking the "like" button on a website.

Additionally, these tags tell Facebook who the administrators are for the website, in turn giving them the ability to make changes to the app on Facebook, moderate Facebook plugins like Comments, and access Insights for the site.

One tip before we go through setting up the tags -- a page you will find to be invaluable as you set up Facebook open graph tags is the URL Linter tool. This page gives you the ability to see a preview what a page will look like when shared, circumventing the need to irritate your friends by constantly sharing pages that you are testing.

Additionally, when a website is shared, Facebook caches the properties for the page for 24 hours. This means that any changes we make to the tags won't be reflected for a day unless we have a way to force Facebook to re-cache the page. Fortunately, every time we add a url into the linter the page properties are re-scraped and updated.

Lastly, the linter will tell you any errors that have been made when setting up the tags and offer suggestions on how to fix them.

Now that you have a brief background on what the open graphs tags are used for, let's do a quick run-though of what each tag is and how they can be set up in Hifi.


og:title example

In the photo above, we can see that the part outlined is where the og:title tag will be shown. Unless the home page is being shared, we will want the title to match what we are already using in the meta title tag, On the home page we will only want to include the site title, since we do need the title of "Home" or "Home Page" to be included.

{% if this.type == "home" %}
     <meta property="og:title" content="{{ hifi.settings.title }}">
{% elseif this.meta.title %}
     <meta property="og:title" content="{{ this.meta.title }}">
{% else %}
     <meta property="og:title" content="{{ this.title }}">
{% endif %}


Next up is og:type, which won't show up in the preview, but tells Facebook what type of website we are sharing. For example, a website for a political candidate may be designated as "politician" and a non-profit could be designated as "non_profit". The full list of options can be found here.

In Hifi, the code will first check to see if the Hifi page type is a post and if it is, will set the og:type as "article". If it isn't, we will want to use the type of website that we found by looking at the list linked above. In this example, I have the og:type set to "website".

{% if this.type == "post" %}
    <meta property="og:type" content="article">
{% else %}
    <meta property="og:type" content="website">
{% endif %}


og:url example

Now we have og:url, which tells Facebook what the url is of the page we are sharing. Even if you redirect one to the other, Facebook differentiates between www and non-www, so make sure that this is accurate so that the wrong url isn't shared and Insights are accurate. Additionally, the url will show in the preview box.

<meta property="og:url" content="http://{{ }}{{ hifi.url.path }}">


og:image example

The og:image tag is used to tell Facebook what image to use in the preview. Facebook requires this to be larger than 50px by 50px and encourages it to be larger than 200px by 200px and have a square shape. Since this is what will be important in catching people's eyes as they browse a feed, be sure to spend some extra time customizing this.

In the Hifi theme, we will first check to see if we are on the homepage. If so, I have it set to check for an image called "logo.png", but this can (and should) be customized to a specific preview image that would catch someone's eye. For example, this is an image we used on a microsite that was launched a few months ago.

If we aren't on the home, the code will check to see if an image was uploaded to the media section of the page. If so, it will crop and resize the first image to a 300px by 300px square. If there isn't any media, Facebook will give the option to select a preview image from the images on the page.

Lastly, remember that the image must use an absolute path in order to work correctly.

{% if this.type == "home" %}
     <meta property="og:image" content="{{ hifi.url.images }}logo.png">
{% else %}
    {% if %}
        {% set photo as[0] %}
        <meta property="og:image" content="{{ photo.url|imagesize({ width:300, height:300, crop:true }) }}">
{% endif %}


The og:site_name tag is used to tell Facebook what the name of the site is that the url is shared from.

<meta property="og:site_name" content="{{ hifi.settings.title }}">


og:description example

For the code, we will first check to see if a meta description has been set for the page. If there isn't, we will move on a check to see if there is an excerpt set. Lastly, since something is better than nothing, the code will truncate the content to 160 characters and add an ellipsis at the end. The amount of characters displayed varies depending on where the page preview is on Facebook, so it's best to keep it shorter and have more important information near the beginning. Facebook suggests keeping the description to one or two sentences.

{% if this.meta.description %}
     <meta property="og:description" content="{{ this.meta.description }}">
{% elseif this.excerpt %}
    <meta property="og:description" content="{{ this.excerpt|striptags }}">
{% else %}
    <meta property="og:description" content="{{ this.content|striptags|truncate(160,'...') }}">
{% endif %}

fb:admin and fb:app_id

Finally, we have fb:admin and fb:app_id. While these tags technically aren't part of the Open Graph Protocol, they are important and necessary to tell Facebook who the admins are for the site. Admin privileges allow access to the Insights for the site in addition to other on-site actions, like moderating a Facebook Comment Box plugin.

Assigning admin privileges can be done two ways:

  1. Assigned directly by adding the user's Facebook id number in the fb:admin tag (multiple ids should be comma-separated)
  2. Creating a new Facebook app and adding the user as an admin to the app.

Which approach you will want to take is up to you, but on a smaller site you will find that setting up a separate app for the site is overkill and simply adding fb:admin is adequate. Alternatively, one thing to keep in mind is that the user ids will be visible in the page source, so instead assign admin access through an app if privacy of admins is important.

<meta property="fb:admins" content="">
<meta property="fb:app_id" content="">

Optional tags

In addition to the tags above, there are a wide range of additional optional open graph tags that can be used if they fit the content of your site. To learn more about these, visit Facebook's page on Open Graph.

Full Code

<!-- facebook og tags -->
{% if this.type == "home" %}
<meta property="og:title" content="{{ hifi.settings.title }}">
{% elseif this.meta.title %}
<meta property="og:title" content="{{ this.meta.title }}">
{% else %}
<meta property="og:title" content="{{ this.title }}">
{% endif %}
{% if this.type == "post" %}
<meta property="og:type" content="article">
{% else %}
<meta property="og:type" content="website">
{% endif %}
<meta property="og:url" content="http://{{ }}{{ hifi.url.path }}">
{% if this.type == "home" %}
<meta property="og:image" content="{{ hifi.url.images }}logo.png">
{% else %}
{% if %}
{% set photo as[0] %}
<meta property="og:image" content="{{ photo.url|imagesize({ width:300, height:300, crop:true }) }}">
{% endif %}
<meta property="og:site_name" content="{{ hifi.settings.title }}">
{% if this.meta.description %}
<meta property="og:description" content="{{ this.meta.description }}">
{% elseif this.excerpt %}
<meta property="og:description" content="{{ this.excerpt|striptags }}">
{% else %}
<meta property="og:description" content="{{ this.content|striptags|truncate(160,'...') }}">
{% endif %}
<!-- uncomment these after the ids are added -- doing before can cause a scraping error --> 
<meta property="fb:admins" content="">
<meta property="fb:app_id" content="">

How to set up a member login section on HiFi

1 Comment(s) | Posted | by Ashley Bennett |

If you plan to offer material on your website exclusively to designated users, then creating a member login section is a necessity. Luckily, HiFi makes it simple enough to set up a login area in a matter of minutes.

To start creating your login page, create a blank page in your site tree and title it something like “Member Login.”

The default HiFi template comes with a pre-built login form that appears anytime a user who is not logged in or registered attempts to access content only visible to site users. You can find the login.html file in the hifi > user folder of your template, or simply create a new one.

In the login.html file you’ll see the following form:

<form id="login" action="/hifi/login" method="post">
		<label for="email">Email</label>
		<input type="text" class="text" id="email" name="email" value="{{email}}"/>
		<label for="password">Password</label>             
	 	<input type="password" class="text" id="password" name="password" value=""/>
		<input type="hidden" name="redirect" value="{{redirect}}" />
		<fieldset class="checkbox">
			<label><input type="checkbox" name="staySignedIn" value="false"/> Remember me</label>
	<fieldset class="submit">
		<input type="submit" value="Login"/>

Feel free to change the fieldsets to those that apply to your users. For instance, maybe you’d like to replace the email input with a username field. Once your form is set, copy this code.

Next, you’ll need to create a custom template file for your “Member Login” page. Paste the form you just copied into the correct content area and change the value of the redirect input field.

For example, if you wanted to redirect users to the homepage after they have logged in, your code would look like so:

<input type="hidden" name="redirect" value="/"/>

That’s it! If you’d like to test the functionality of your login page make sure you are completely logged out of the site or open it in a new browser window.

Twitter Timeline

0 Comment(s) | Posted | |


This snippet generates a timeline of the most recent tweets from a particular Twitter user utilizing HiFi's retrieve tag. It generates a url calls a JSON Object of a user timeline from the Twitter REST API. Then it parses the data in a format that mimics the format on the Twitter website including links and twitter handles. Because Twitter limits the number of requests per hour, the snippet is setup to cache the timeline every 15 minutes.



{% import "/snippets/twitter/twitter.html" as twitter %}
{{ twitter.draw({ handle:'gethifi' }) }}

Here is sample output including a standard tweet and a retweet:

<section class="tweets">
  <article class="profile">
    <img src="[ Image URL ]" alt="[ Display Name ]" width="73" height="73" />
    <h2>[ Display Name ] w</h2>
    <h3>@[ Screen Name / Handle ]</h3>
    <p>[ Profile Description ]<br/><a href="[ Profile URL ]" target="_blank">[ Profile URL ]</a></p>
  <article class="tweet">
    <p>[ Tweet Text ]</p>
    <div class="row">
      <time datetime="[ Tweeted Created_At Time ]" pubdate>
       <a href="[ Tweet URL ]" target="_blank">[ Parsed Timestamp ]</a>
      <div class="actions">
 	<a class="retweet" href="[ Retweet Intent URL ]" target="_blank"><span><i></i> retweet</span></a>
	<a class="reply" href="[ Reply Intent URL ]" target="_blank"><span><i></i> reply</span></a>
  <article class="tweet retweet">
    <div class="retweeted">
      <time datetime="[ Tweet Timestamp ]" pubdate>
        <a href="[ Retweet URL ]" target="_blank">[ Parsed Timestamp ]</a>
    <img src="[ Original Tweeter Profile Image ]" width="48" height="48" alt="[ Original Tweeter User ]" />
    <p>[ Retweet Text ]</p>
    <div class="row">
	<a href="[ Retweet URL ]" target="_blank">[ Original tweet timestamp ]</a>
      <div class="actions">
        <a class="retweet" href="[ Retweet Widget URL ]" target="_blank"><span><i></i> retweet</span></a>
	<a class="reply" href="[ Reply Widget URL ]" target="_blank"><span><i></i> reply</span></a>
  <div class="follow">
    <a href="[ Twitter Handle]" class="twitter-follow-button" data-show-count="false">Follow @[ Twitter Handle ]</a>
  <script type="text/javascript" src="//"></script>  


Twitter handle without the '@' symbol. This is the only required option that needs to be passed when calling this macro. ex: {handle:'nmcteam'}, {handle:'gethifi'}.

The number of items from the timeline to be displayed. You can have a maximum of 200.

If true this will display the twitter profile, similar to the one you see on a individual profile page.
Default: true

If true the user's retweets will be included in the retrieved timeline.

If true, the user's @replies will be included in the the retrieved timeline. 

If true the template will include Twitter's JS platform that allows the user pop-ups and the follow button to function.

HTML element that contains the widget.
Default: section

HTML element that contains individuals tweets.
Default: article

Path to template used to display the profile.

Path to template used to display individual tweets from the timeline.

Path to template used to generate timestamps.

Steps to use high-performing @font-face on your HiFi Site

0 Comment(s) | Posted | by Pierre Lebrun | |

For a long time, designers have been very limited when it comes to fonts on the web. There is just a small list of web safe fonts that reliably render correctly across multiple browsers and operating systems. If a designer wanted to use a font that wasn't on this short list, there were very few options, all having serious drawbacks.

Historic solutions to solve the font problem include replacing text with images, cufon, a technique for using javascript to render text and sIFR, which uses flash to substitute text with flash rendering of fonts. These are workable solutions, but not without their drawbacks. They display fonts in a non-native manner, causing text selection issues, increased hardware demands and dependencies on browsers/devices supporting a third party proprietary plugin.

In comes @font-face. @font-face is a web standard with increasing, and now-nearly universal desktop browser support. It allows designers to include and define fonts in CSS. The basic syntax is as following:

@font-face {
    font-family: 'Foo Font';
    src: url('foofont.otf');
h1 { font-family: 'Foo Font', arial, sans-serif; }

Once you declare a new font family, you are able to use it natively, just as you would a standard web-safe font. The above code assumes you have an opentype font file 'foofont.otf' in the same directory as the css file.

If you don't want to host the font files yourself, there are third party services - both free and paid - that will do it for you. The two services we most recommend are Google Fonts (free), which has many open source fonts and TypeKit (paid) which offers access to fonts from a number of popular foundries. These are simple to use and get you up and running right away.

Cross-Browser Compatibility

While it would be nice to simply use the code above and have @font-face just work, cross-browser support for font formats makes things a little trickier. The general syntax works in nearly all modern browsers, but the actual format the font files need to be in varies.

In order for fonts to render correctly across browsers, we need to include all of the supported formats - which looks something like this:

@font-face {
    font-family: 'Foo Font';
    src: url('foofont.eot');
    src: url('foofont.eot?#iefix') format('embedded-opentype'),
         url('foofont.woff') format('woff'),
         url('foofont.ttf') format('truetype'),
         url('foofont.svg#ChantelliAntiquaRegular') format('svg');
    font-weight: normal;
    font-style: normal;

Ugh. It's quite the pain to come up with all of these different font formats. Fortunately, there's an easy way to do this:'s @font-face generator lets you generate the css and font files you need in just a few clicks. We'll cover @font-face generator usage in the next section.

Using @font-face on HiFi

HiFi serves all of its theme assets (css, scripts, images, fonts) from a separate subdomain: This is done as recommended by YSlow and Google PageSpeed. By serving files from multiple (sub)domains, you increase the browsers ability to make requests in parallel, downloading your site faster.

Unfortunately, Firefox doesn't like showing fonts that are served on a different subdomain from the page for security reasons, so it won't render a font from on

There is a workaround however! In fact, it also happens to speed up page load times: you can embed the font code directly into the CSS rather than keeping it in a separate file. This saves browsers from having to download additional file(s) AND it's compatible with Firefox. It works using Base64, an encoding scheme that lets you translate binary data into an ASCII string format. Here are the steps required to do it:

  1. Find and download the regular font files you'll need. These will be .otf or .ttf.
  2. Ensure you own the proper liscenses for the fonts you'll use.
  3. Go to the FontSquirrel @font-face generator:
  4. Click +Add Fonts and add in an otf or ttf file for each font you need.
  5. Click the 'Expert' option
  6. Under CSS Options, choose Base64 encode
  7. Check the license agreement
  8. Generate/Download the kit
  9. You now have a zip containing a CSS file and a bunch of different font formats. The font files all get used by obscure browsers and IE. Most browsers will use the base64 encoded font in the CSS.
  10. Rename the CSS file to something useful like 'fontname.css'.
  11. Open the CSS file
  12. You'll notice the giant blocks of code immediately. These are the base64 embedded fonts. You'll also notice a few references to the external fonts that get used by some browsers.
  13. These external font references account for the font files being in the same dir as the CSS file. This isn't the case with HiFi. You'll need to change url(fontname.woff) (or whatever) to url(../fonts/fontname.woff). Do this with a search/replace so you don't miss any.
  14. Upload the css file to HiFi under '/styles'
  15. Upload all the font files to HiFi under '/fonts'
  16. You now need to include the CSS file in your index.html above your other stylesheets. This is a rare case where you don't want to necessarily compile/minify the CSS together with everything else. Create a separate {% css %} declaration above your main one for your fontname.css file.
  17. Voila, you can now use the fonts like any other: font-family: 'fancy name', arial, sans-serif;

It may seem like a bit of work to do this, but once you get the hang of it you'll be able to start using new fonts very quickly. It's also good to know that by using this technique, your fonts will work reliably across desktop browsers and perform as well as they can.

Image Captions

2 Comment(s) | Posted | by Ashley Bennett | |


Easily add static or animated captions to your images.


Javascript and a jQuery library are required to use one of an image’s attributes to build the markup for a caption.



	padding:0 0 20px 0;


	copyStyle: true


Insert an image (maybe in a page or post) and fill in the image description, or alt attribute, with the text you would like to appear as the caption. Then upload the jcaption.js file to your theme and include it in your desired template. Add the styles included above to the stylesheet and the javascript directly to the template so you can add or edit the options that are provided below. 

If you are floating your images, giving them padding or adding any other style, be sure to keep copyStyle set to “true” so the caption class does not override your image styles. 


	// Element to wrap the image and caption in
	wrapperElement: 'div',

	// Class for wrapper element
	wrapperClass: 'caption',

	// Caption Element
	captionElement: 'p',

	// Attribute of image to use as caption source
	imageAttr: 'alt',

	// If true, it checks to make sure there is caption copy before running on each image
	requireText: true,

	// Should inline style be copied from img element to wrapper
	copyStyle: false,

	// Strip inline style from image
	removeStyle: true,

	// Strip align attribute from image
	removeAlign: true,

	// Assign the value of the image's align attribute as a class to the wrapper
	copyAlignmentToClass: false,

	// Assign the value of the image's float value as a class to the wrapper
	copyFloatToClass: true,

	// Assign a width to the wrapper that matches the image
	autoWidth: true,

	// Animate on hover over the image
	animate: false,

	// Show Animation
	show: {opacity: 'show'},
	showDuration: 200,

	// Hide Animation
	hide: {opacity: 'hide'},
	hideDuration: 200

Google Maps

2 Comment(s) | Posted | by Patrick Clarke | |


Show a location on a Google Map by providing just an address.


Javascript is needed to use Google's API for Geocoding the location.



<div id="map-container">
	<div id="google-map"></div>

<!-- Insert Javascript Component Here -->


	border:1px solid #c5c5c5;
 	margin:20px 20px 0;


var map;
var geocoder;
var address = '{{ this.custom.googlemaps.address }}'; // Replace the variable with your HiFi Custom Field
function initialize() {
	var latlng = new google.maps.LatLng(40.111676, -98.349584);
	var myOptions = {
		zoom: 4,
 		center: latlng,
		mapTypeId: google.maps.MapTypeId.ROADMAP
 	map = new google.maps.Map(document.getElementById("google-map"), myOptions);
	geocoder = new google.maps.Geocoder();
function geocode(){
	geocoder.geocode({'address': address,'partialmatch': true}, geocodeResult);
function geocodeResult(results, status) {
	if (status == 'OK' && results.length > 0) {
		var marker = new google.maps.Marker({
			position: map.getCenter(),
			map: map
	} else {
		alert("Geocode was not successful for the following reason: " + status);    
function loadScript() {
	var script = document.createElement("script");
	script.type = "text/javascript";
	script.src = "";
window.onload = loadScript;


Add the <div id="google-maps"> onto your template and the styles to your stylesheet. Then you'll want to create a custom text field for the address on any pages you want to display the map. Then add the javascript directly to the template so you can have access to custom field variables, and change the address variable to your custom field tag.

Image Grid Creator

1 Comment(s) | Posted | by Joel Sutherland | |


This snippet allows you to easily create a grid of images from the media tab.  You upload however many image you want, and then using custom fields, specify how wide you want the container to be and the number of images you want on each row.

All of the images are then automatically resized and placed in the grid.


No external requirements.



{% include 'common/image-grid.html' with ['images', 'width':this.custom.tych.width, 'layout':this.custom.tych.layout] %}


To install this snippet, you just need to upload the file in the zip to your theme and include it in your desired template. All of the required CSS and image resizing is done inside the template itself.

You need to make sure that you're passing in variables for images, width and layout.  This means that you need to set up custom fields for width and layout.

  1. width: This is the width of the container that holds the images. Use a text field for this.
  2. layout: This is a textarea that explains how many images go on each row.

All the magic happens in the layout field.  The following would display 5 images total, 2 on the first line, 3 on the second:


It is also possible to indicate how tall you want each row to be in pixes. You can optionally do that by using a bar ("|") and then putting in the number of pixels you want. Here is an example that displays 6 images, 1 on the first row, 2 on the second, 3 on the third. The second row is set to 100px high:


Here is a screenshot showing the custom fields. Note that I am also using an "Enabled" dropdown so that it runs optionally.

Navigation Menu

1 Comment(s) | Posted | by Joel Sutherland | |


This snippet is a core part of every HiFi theme.  It allows you to easily build navigation, menus, sitemaps or any feature that needs a nested list of your site content.

The idea behind the snippet is that you provide a piece of content as a starting point and a depth, and this will build a tree for you.  Typically, the starting point will be your homepage, and the depth will be 1 or 2 depending on whether you want drop downs in your navigation.

By default, this snippet generates nice and clean markup, but you can pass in other templates if you need to change that markup dramatically.  Most common things, like adding ids and classes can be done with the default templates.


No external requirements -- although this snippet won't do you any good if you're not on HiFi.



The default options will generate nested unordered lists starting from your homepage, two levels deep.  Here is how you make the default call:

{% import '/common/navigation/navigation.html' as navigation %}
{{ navigation.draw() }}

This will generate something like the following:

    <li><a href="/">Home</a></li>
    <li><a href="/about">About</a>
            <li><a href="/about/philosophy">Philosophy</a></li>
            <li><a href="/about/history">History</a></li>
            <li><a href="/about/team">Team</a></li>
    <li><a target="_blank" href="">Yahoo</a></li>
    <li><a href="/contact">Contact</a></li>

If you need to do something beyond the defaults, you can add in options by passing in an options object. Here is an example that changes the maxDepth to 3 and has two other fake options. It doesn't matter what order they are in.

{% import '/common/navigation/navigation.html' as navigation %}
{{ navigation.draw({
    maxDepth: 3,
    option: 'value',
    otherOption: 'value'
}) }}

Options Reference


A query to find the starting point for the nav. So {type: 'page', url: '/about'} would start from the about page. It uses the first result from the query.
Default: {type: 'home'}


The number of levels this should traverse.
Default: 2


This is the query used to restrict the items that are found. It is used at each depth level.
Default: {type: 'content', inMenu: true, orderBy: 'ordinal', count: 100}


The class to put on the active nav item based on url.
Default: 'active'


The class to put on ancestors to the active nav item based on url.
Default: 'active'


If true, the root is included in the first level nav.
Default: true


Display a level class on each li (ie. class="level_3")
Default: false


Specify the string you want to prefix the level number
Default: 'level_'


The id attribute assigned to the nav's first ul
Default: ''


The class attribute assigned to the nav's first ul
Default: ''


For link types that have newWindow set, what attribute do you want to add to the link.
Default: 'target="_blank"'


Specify whether folder links should have the href attribute
Default: false


Path to the template to use for wrappers
Default: '/common/navigation/wrapper.html'


Path to the template to use for items
Default: '/common/navigation/item.html

The HiFi Snippet Library Has Launched

0 Comment(s) | Posted | by Joel Sutherland | |

HiFi Snippets

This morning we're making it official the HiFi Snippet Library has launched.

A great thing about working with HiFi is that you can use the web technologies you know (HTML/CSS/JS) with no restrictions. Many systems say this, we actually mean it. This means that it is extremely easy to build reusable snippets that can save you all kinds of time when building your client sites.

If none of this makes sense to you, hopefully we can answer your questions here.  If you have any others, let us know in the comments.

What is a snippet?

A snippet is a combination of HTML, CSS and JS that you can drop into your sites' templates to quickly implement a feature. Since it is based in just HTML/CSS/JS, it is easy to customize or change them.

Why is this cool?

Snippets let you quickly add functionality to your sites without forcing you to use particular HTML. Design and code the sites the way you want, and then hook up the functionality with snippets.

How much do they cost?

The snippets are completely free.  Most can even be used on non-HiFi sites!

Where are they coming from?

The HiFi snippets are being written both by the HiFi team as they build sites, and by customers using HiFi. If you have a snippet you would like to see, or a snippet you want to contribute to the library, get in contact.

How often will they be released?

We have committed to release at least one snippet a week, but through community contributions, the number may be even higher! Whenever we release a snippet, we will be posting it to the HiFi blog. So be sure to subscribe to the blog to get updated on the newest snippets.

Your snippets are stupid. Why don't you have an XYZ snippet?

We like your attitude. Shoot us a message and we'll make sure it gets added!