Building static sites with jekyll

GFX9.COM share Building static sites with jekyll, you can download now.

A full-blown CMS is rarely necessary. Sometimes, you only need to create a light, static website … but you have just enough pages to make the process of copying template files and making cross-site markup changes a chore. Today, I’ll demonstrate a simple solution—Jekyll—that will make creating small websites a breeze.

Step 0: Meeting Jekyll

Jekyll is a simple, blog aware, static site generator.

Jekyll is a simple, blog aware, static site generator. That’s what the site says. But, what exactly does this mean? A static site generator is a program that takes a set of files and generates your site with them. As you’ll see, we’ll be able to use a set of templates, create the content files separately, and then use Jekyll to generate our site. The “blog aware” part means that we could use this to create a blog, or any website that has a series of post-like entries (such as a portfolio). Let’s give it a try!

Step 1: Installing Jekyll

Refer here for more information on Ruby Gems.

We’ll begin by installing Jekyll; it’s a Ruby gem, so doing so should be pretty straightforward.

gem install jekyll # use `sudo` if your setup requires it

Yep: it’s that easy. There are a few more pieces we could install if we are planning on doing a more complex set-up, however, since we’re not, this will do the trick.

Step 2: Creating our First Template

Every file or folder that does not begin with an underscore will be copied to the generated site.

Next, let’s set up the folders for Jekyll. Create a folder, called example-app for this tutorial; we’ll be creating a little portfolio site for, say, a photographer. This is a great example of where Jekyll shines: it’s a small site that won’t be updated too frequently, but is large enough to the point that you don’t want to open every page when you need to make a markup change.

Inside example-app, create a folder called _layouts. Notice the underscore at the beginning of this folder: any folder or file that begin with an underscore will not be part of the site that Jekyll generates. If they have a name that Jekyll recognizes (such as _config.yml or _layouts), their contents will be used in the generation of the site, but the files themselves won’t show up in the site. Remember this: every file or folder that does not begin with an underscore will be copied to the generated site (which, by the way, defaults to the _site sub-folder).

So, let’s create a layout. We’ll start with a general site layout that includes all the “chrome” for our site. Create a new file, called default.html inside the _layouts folder (the name doesn’t matter), and add the following code to it:

   {% if page.title %} {{ page.title }} | {% endif %} John Doe, Photographer 


John Doe Photograghy

{{ content }}

@copy; John Doe Photography 2011 | All Rights Reserved.

A couple of things here to keep in mind...

Firstly, Jekyll uses the Liquid template system (by default). This means, anything you can do with Liquid, you can do in a template in Jekyll. For example, in the </code> tag, we’re using both types of Liquid markup: <em>output markup</em> and <em>tag markup</em>. Output markup may output text (if the variable referenced exists), while tag markup doesn’t. Output markup is delimited by double curly-braces, while tag markup is delimited by a the curly brace / percent sign duo.</p> <p>The next thing to notice above is what is inside the Liquid tags: things like <code>page.title</code> and <code>content</code>. These are variables provided by Jekyll; you can see the list of available template data in the docs. We can also create custom template data, as we’ll review shortly.</p> <p>Lastly, notice the CSS we’re linking to: create a <code>css</code> folder in the root of your project and throw this bit of styling into a <code>style.css</code> file:</p> <pre class="brush: css">body { font: 16px/1.5 verdana, helvetica-neue, helvetica, arial, san-serif; background: black; color: #ececec; padding: 0; margin: 0; } ul { margin: 0; padding: 0; } a { color: #ccc; text-decoration: none; } a:hover { color: #ececec; text-decoration: underline; } #main { width: 960px; margin: 0 auto; background: rgba(255, 255, 255, 0.4); } header { padding: 0 10px; overflow: hidden; } h1 { margin: 0; } nav ul, ul.entries { list-style-type: none; } nav li a { float: left; margin: 5px; } .content { padding: 10px; } ul.entries li { position: relative; margin: 20px auto; padding: 20px; background: #ececec; width: 600px; } ul.entries img { width: 600px; } ul.entries li h3 { position: absolute; bottom: -18px; left: 17px; font-size: 2em; } ul.entries a { color: #ececec; } ul.entries a:hover { color: #fff; } footer { font-size: 0.65em; text-align: center; }</pre> <p>Also, create an <code>img</code> folder and add an image, named <code>banner.jpg</code>; we’ll be using it shortly. Any image will do; just crop it to <code>960px</code> by <code>300px;</code>.</p> <p>You might be wondering why we're using the <code>if</code> statement above if the <code>page.title</code> variable just won’t display if it exists? Well, if it does exists, I want to include the vertical bar after it; another way to write that would be like this:</p> <pre class="brush: html">{{ page.title }}{% if page.title %} | {% endif %}</pre> <p>So, how we do use this template? Well, we need to create a page that will use this template. In the root directory of our project, create an <code>index.html</code> file. Here’s the content:</p> <pre class="brush: html">--- layout: default --- <section role="banner"> <img src="/img/banner.jpg" /> </section> <section class="content"> <p> Welcome to John Doe Photography! Please, check out my <a href="/portfolio/">Portfolio</a> to see my work. </p> </section></pre> <p>Here’s the content of our <code>index.html</code> file. Notice what’s at the top of the file: Jekyll calls this YAML front matter. Any file (that doesn’t start with an underscore) that has YAML front matter will be generated by Jekyll before being put in the <code>_site</code> folder (if it has no underscore or YFM, then it will just be copied <code>_site</code>). In this case, the YAML front matter just tells Jekyll what template we want it to use.</p> <p>Now, open a terminal, <code>cd</code> into your project directory, and run <code>jekyll</code>. You should see something like this:</p> <pre class="brush: html">WARNING: Could not read configuration. Using defaults (and options). No such file or directory - /Users/andrew/Desktop/example-app/_config.yml Building site: /Users/andrew/Desktop/example-app -> /Users/andrew/Desktop/example-app/_site Successfully generated site: /Users/andrew/Desktop/example-app -> /Users/andrew/Desktop/example-app/_site</pre> <p>Ignore the warning; we’ll come to that shortly. For now, you can see that the site has been built in a freshly-created <code>_site</code> directory. If you open the <code>_site/index.html</code> file in your browser of choice, you should see … a failure. The problem is that our paths (urls and stylesheet) begin with a forward slash. This means we can’t just view them as files, we need to view them on a server. Sure, you could go start up W/MAMP, but why take the trouble? Jekyll has a built in server. So, run <code>jekyll --server</code>, and go to localhost:4000 to see something like image below:</p> <div class="tutorial_image"><img alt='Tutorial Image' src="" /></div> <p>If the image above isn’t enough, look at the code of <code>_site/index.html</code>. You’ll see that the template we specified was blended with the content we provided and—voila!—we have our page.</p> <p>I want to remind you that it’s the YAML front matter that makes this magic happen; if a file doesn’t start with three dashes, one or more lines of properties, and another line of three dashes, the file will just be copied to the <code>_site</code> folder, no generation taking place.</p> <hr /> <h2><span>Step 3:</span> Creating a Portfolio Template</h2> <p>Now that we’re comfortable with the basics, let’s create a portfolio for our fictional photographer. Remember how I noted that Jekyll is “blog aware”? Well, we’re going to use this blog-awareness feature to our advantage: instead of posts, we’ll have portfolio entries.</p> <p>Posts belong in a folder, called <code>_posts</code>, so create that now. The file name pattern for posts must be specific as well: <code>year-month-day-title.ext</code>. Posts — well, any file in your Jekyll site, really — can be either Markdown or HTML.</p> <p>So let’s make a few posts: remember, these will actually be entries in our portfolio:</p> <h3><code>_posts/</code></h3> <pre class="brush: html">--- layout: portfolio_entry image: /img/bikes.jpg title: Bikes, Black and White --- Bikes are used by almost everyone in downtown Amsterdam. These ones are chained to a bike rack.</pre> <h3><code>_posts/</code></h3> <pre class="brush: html">--- layout: portfolio_entry title: Wing and a Prayer image: /img/wing.jpg --- The wing of the AirBus I rode to England.</pre> <h3><code>_posts/</code></h3> <pre class="brush: html">--- layout: portfolio_entry title: Stone Bridge image: /img/bridge.jpg --- An old stone bridge in London.</pre> <h3><code>_posts/</code></h3> <pre class="brush: html">--- layout: portfolio_entry title: Road and Curb image: /img/road.jpg --- Bike lanes here are terribly thin.</pre> <p>Pretty simple, eh? Notice how we’re creating a custom YAML front matter field: <code>image</code>. This is the URL to the image for that entry. Sure, we could build the whole entry HTML here in this file, but what if we want to change that? We’d have to return and change it in every entry. This way, we can instead use our <code>portfolio_entry</code> template to render them. What’s that template look like? It’s pretty simple too:</p> <h3><code>_layouts/portfolio_entry.html</code></h3> <pre class="brush: html">--- layout: default --- <h2 class="content">{{page.title}}</h2> <img src="{{ page.image }}" /> {{ content }}</pre> <p>If you looked at the template data page, you’ll know that any custom front matter we add will be available under <code>page</code>; so, here, we can access <code>page.image</code>. We’re also using <code>page.title</code> and <code>content</code> (everything after the last three-dash line).</p> <p>I should mention here that, while the post <code>title</code> is supposed to be available on the <code>post</code> object, I’ve only been able to get it to work on the <code>page</code> object. Whatever works!</p> <p>Also, notice that we have this template using our <code>default</code> layout. You can nest templates like that, and make your job even easier.</p> <p>This gives us our entry (post) pages, but what about the main portfolio page? When writing our navigation in our default layout, I noted that we want it as <code>/portfolio/</code>. So, create a folder, called <code>portfolio</code> in the root directory, and open an <code>index.html</code> file within it.</p> <pre class="brush: html">--- layout: default title: Portfolio --- <section class="content"> <h2>Portfolio</h2> <p>Check out my images below!</p> </section> <ul class="entries"> {% for post in site.posts %} <li> <a href="{{ post.url }}"> <img src="{{ post.image }}" /> <h3>{{ post.title }}</h3> </a> </li> {% endfor %} </ul></pre> <p>This is our most complicated piece yet. Remember, this isn’t a template: it’s a “normal” file, but it can still include Liquid tags. We start by setting <code>layout</code> to <code>default</code>, and <code>title</code> to “Portfolio.”</p> <p>Notice that, in the HTML, we have a Liquid <code>for-in</code> loop. We retrieve all the posts with <code>sites.posts</code>; then, we loop over those posts with <code>for post in site.posts</code> / <code>endfor</code>. If you’ve worked with WordPress, or any other blogging system, you should be familiar with the concept of a <code>loop</code>. That’s all this is! Inside, as you can see, we can get the standard properties, as well as any front matter we defined (like <code>image</code>).</p> <p>Now if we run <code>jekyll --server</code> to re-generate the site and start the server, localhost:4000/portfolio/ should show this:</p> <div class="tutorial_image"><img alt='Tutorial Image' src="" /></div> <p>And here’s an entry page:</p> <div class="tutorial_image"><img alt='Tutorial Image' src="" /></div> <p>Great! You’ve created a portfolio. I’m sure you see, as well, how this works for a blog. Let’s now move on to look at some configuration options for Jekyll.</p> <hr /> <h2><span>Step 4:</span> Writing a Config File</h2> <p>There’s a plethora of options for Jekyll. It’s great that all of them have really sensible defaults, but if you want to change them, it’s not hard at all.</p> <p>There are two ways to set options.</p> <ul> <li> First, when you run the program on the command line, you can pass parameters. We’ve already seen the <code>--server</code> parameter, which starts a server after generating the site.</li> <li> A different way, and the way we’ll use here, is in a config file, called <code>_config.yml</code>; this is a YAML file, so each line is <code>a key: value</code> pair, just like in the YAML front matter. Jekyll will look for this file before generating site.</li> </ul> <p>So, make an <code>_config.yml</code> file, and let’s check out some of the most common options. </p> <blockquote><p>For a complete list of options, review the configuration documentation.</p> </blockquote> <ul> <li><strong><code>auto</code></strong>: Adding <code>auto: true</code> to your config file will keep Jekyll running, watching your project folder for changes and regenerating the site on the fly.</li> <li><strong><code>source</code></strong>: If your source files are in a different directory than the one you’re running Jekyll from, you’ll want to set that directory with the <code>source</code> property.</li> <li><strong><code>destination</code></strong>: By default, the destination for your generated site is <code>./_site</code>. If you’d like something different, set it here.</li> <li><strong><code>permalink</code></strong>: The permalink is the path to your posts. By default, that’s <code>/year/month/day/title.html</code>. However, you can customize that if you want. Among others, you can use the variables <code>:year</code>, <code>:month</code>, <code>:day</code>, <code>:title</code>, and <code>:categories</code>. <code>:categories</code> comes from the front matter; all the others come from the post file name. Then, you can set <code>permalink</code> to things like <code>/:year/:month/:title/</code> or <code>/:categories/:title.html</code>. Bonus tip: if you have a <code>permalink</code> property in the post front matter, it will override the site-wide default.</li> <li><strong><code>exclude</code></strong>: Like I said above, Jekyll won’t generate files in directories starting with an underscore. But, if you have folders that you want it to ignore, but that don’t start with an underscore, you can do it with <code>exclude</code> in your config file.</li> </ul> <hr /> <h2><span>Step 5:</span> Deploying the Site</h2> <p>So, let’s say you’ve created the site, and want to set it free, out on the world wide web. How do you do that?</p> <p>There are several ways to accomplish this. Of course, if it’s a small site that you won’t be updating too often, then simply FTP it up to your server; this might be your only option, if you’re using shared hosting.</p> <p>If you’ve got a VPS or dedicated hosting setup, you can run more automatically. Check out the deployment documentation for a list of good ideas. If you aren’t sure what to do, try following the directions for using the git post-receive hook; I’ve tried that, and it’s pretty cool. </p> <hr /> <h2><span>Step 6:</span> Taking it Further</h2> <p>This is just the tip of Jekyll. </p> <ul> <li>There’s a plugin architecture that let’s you modify how your content is generated.</li> <li> There’s more you can do with Liquid and some Liquid extensions that Jekyll adds. </li> <li>There’s a lot of template data that we didn’t talk about. Check it out and see what you can do!</li> </ul> <hr /> <h2>Conclusion</h2> <p>Well, that’s your introduction to Jekyll - the simple, blog aware, static site generator. The next time you’re building a brochure-style, business-card-y, micro-portfolio site, think you’ll give Jekyll a try? Let me know in the comments and thank you so much for reading!</p> <br/> <script async src="//"></script> <!-- icon 970 --> <ins class="adsbygoogle" style="display:inline-block;width:970px;height:90px" data-ad-client="ca-pub-8818553224393555" data-ad-slot="2479370846"></ins> <script> (adsbygoogle = window.adsbygoogle || []).push({}); </script> <br/><br/> <a href="//" onclick="window.location = '//' + encodeURIComponent(window.location); return false"> <img src="//" alt="submit to reddit" border="0" /> </a> <!-- Place this tag where you want the +1 button to render. --> <div class="g-plusone"></div> <!-- Place this tag after the last +1 button tag. --> <script type="text/javascript"> (function() { var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true; po.src = ''; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s); })(); </script><br/> <br><script async src="//"></script> <!-- icon 970 --> <ins class="adsbygoogle" style="display:inline-block;width:970px;height:90px" data-ad-client="ca-pub-8818553224393555" data-ad-slot="2479370846"></ins> <script> (adsbygoogle = window.adsbygoogle || []).push({}); </script> <!-- <div id="SC_TBlock_97683" class="SC_TBlock">loading...</div> --> </div> <h2>Similar content</h2> <div><div class="post_list"><div class="thumb"><a href=""> <img class="img_listcontent" src="" alt="Link toWinners announced: $41,000 web developer competition" /></a></div><div class="meta_content"> <a href="">Winners announced: $41,000 web developer competiti...</a></div></div><div class="post_list"><div class="thumb"><a href=""> <img class="img_listcontent" src="" alt="Link toIntroducing nettuts+ fetch" /></a></div><div class="meta_content"> <a href="">Introducing nettuts+ fetch</a></div></div><div class="post_list"><div class="thumb"><a href=""> <img class="img_listcontent" src="" alt="Link toWhat are you using?" /></a></div><div class="meta_content"> <a href="">What are you using?</a></div></div><div class="post_list"><div class="thumb"><a href=""> <img class="img_listcontent" src="" alt="Link toRecently in web development (august edition)" /></a></div><div class="meta_content"> <a href="">Recently in web development (august edition)</a></div></div><div class="post_list"><div class="thumb"><a href=""> <img class="img_listcontent" src="" alt="Link toHow to create a mobile site with mofuse" /></a></div><div class="meta_content"> <a href="">How to create a mobile site with mofuse</a></div></div><div class="post_list"><div class="thumb"><a href=""> <img class="img_listcontent" src="" alt="Link toWeb development from scratch: helpful dev web apps" /></a></div><div class="meta_content"> <a href="">Web development from scratch: helpful dev web apps</a></div></div><div class="post_list"><div class="thumb"><a href=""> <img class="img_listcontent" src="" alt="Link toHtml5 microdata: welcome to the machine" /></a></div><div class="meta_content"> <a href="">Html5 microdata: welcome to the machine</a></div></div><div class="post_list"><div class="thumb"><a href=""> <img class="img_listcontent" src="" alt="Link toNew wp video series, and free rockstar book!" /></a></div><div class="meta_content"> <a href="">New wp video series, and free rockstar book!</a></div></div><div class="post_list"><div class="thumb"><a href=""> <img class="img_listcontent" src="" alt="Link to20 excellent coda tips" /></a></div><div class="meta_content"> <a href="">20 excellent coda tips</a></div></div><div class="post_list"><div class="thumb"><a href=""> <img class="img_listcontent" src="" alt="Link to50 practical, problem solving items" /></a></div><div class="meta_content"> <a href="">50 practical, problem solving items</a></div></div><br /></div><br /><div style="clear:both;"></div> </div><!-- #content --> </div><!-- #primary --> </div><!-- #main --> <footer id="colophon" class="site-footer" role="contentinfo"> <div id="left_main"> <br> <script async src="//"></script> <!-- Link ads 2 --> <ins class="adsbygoogle" style="display:inline-block;width:120px;height:90px" data-ad-client="ca-pub-8818553224393555" data-ad-slot="9127123640"></ins> <script> (adsbygoogle = window.adsbygoogle || []).push({}); </script> <br/> <h2 id="title"><a href="">GFX9.COM</a></h2> <div class="menu-main-menu-container"><ul id="menu-main-menu" class="nav-menu"><li id="menu-item-97038" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-home menu-item-97038"><a href="">Home</a></li> <li id="menu-item-97044" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-97044"><a href="">Vector</a></li> <li id="menu-item-170627" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-170627"><a href="">Tutorials</a></li> <li id="menu-item-97042" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-97042"><a href="">PSD</a></li> <li id="menu-item-97045" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-97045"><a href="">3D Models</a></li> <li id="menu-item-155832" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-155832"><a href="">Illustration</a></li> <li id="menu-item-155835" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-155835"><a href="">Tools & tips</a></li> <li id="menu-item-155836" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-155836"><a href="">Photo effects</a></li> <li id="menu-item-155837" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-155837"><a href="">Designing</a></li> <li id="menu-item-97039" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-97039"><a href="">HD Pictures</a></li> <li id="menu-item-97040" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-97040"><a href="">Icons</a></li> <li id="menu-item-97041" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-97041"><a href="">PPT Template</a></li> <li id="menu-item-97043" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-97043"><a href="">Templates</a></li> <li id="menu-item-122326" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-122326"><a href="">Font</a></li> <li id="menu-item-122327" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-122327"><a href="">Photoshop</a></li> <li id="menu-item-145855" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-145855"><a href="">Design Appreciation</a></li> <li id="menu-item-145856" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-145856"><a href="">Flash</a></li> <li id="menu-item-145857" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-145857"><a href="">Web Code</a></li> <li id="menu-item-157965" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-157965"><a href="">Android SDK</a></li> <li id="menu-item-157966" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-157966"><a href="">PHP</a></li> <li id="menu-item-157970" class="menu-item menu-item-type-taxonomy menu-item-object-category current-post-ancestor current-menu-parent current-post-parent menu-item-157970"><a href="">Web Development</a></li> <li id="menu-item-157971" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-157971"><a href="">WordPress</a></li> <li id="menu-item-157972" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-157972"><a href="">iOS SDK</a></li> <li id="menu-item-157973" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-157973"><a href="">JavaScript & AJAX</a></li> <li id="menu-item-157974" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-157974"><a href="">Mobile Development</a></li> <li id="menu-item-195013" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-195013"><a href="">search-result</a></li> </ul></div> <div style="margin-left: 10px;"> <br> <script async src="//"></script> <!-- Link ads 2 --> <ins class="adsbygoogle" style="display:inline-block;width:120px;height:90px" data-ad-client="ca-pub-8818553224393555" data-ad-slot="9127123640"></ins> <script> (adsbygoogle = window.adsbygoogle || []).push({}); </script> <br/> <a href="" target="_blank" title="Vector free download">Vector free download</a><br> <a href="" target="_blank" title="All graphic free download">All graphic free download</a><br> <a href="" title="free 3d models">free 3d models</a><br> </div> </div> </footer><!-- #colophon --> </div><!-- #page --> <script type='text/javascript' src=''></script> <script type='text/javascript' src=''></script> <script> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//','ga'); ga('create', 'UA-39448051-1', ''); ga('send', 'pageview'); </script> </body> </html>